之后使用json解析。
必须仔细观察json的结构,而且还需要不断地断点调试。
import requests
import json
headersIm = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
#network headers里面
url_sport = "http://interface.sina.cn/pc_zt_roll_news_index.d.html?&subjectID=64558&channel=sports"
# json_url = 'http://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page=1&channel=sports&callback=jsonpcallback1483559621857'
json_url = 'http://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page=1&channel=sports'
jsContent = requests.get(json_url, headers = headersIm).content
print jsContent
jsDict = json.loads(jsContent)
jsResult = jsDict['result']
jsData = jsResult['data']
for each in jsData:
print each['title']
今天的结果:
又运行了一下程序,得到的结果是:
没做完的部分:将标题,内容都整理好,只爬取前20条信息就足够了(或者加上后面10页的内容),进入内页后,将文章的文字部分也提取出来。
一、问题:
显然,只爬取标题是不够的,最主要的还是url链接,内页的正文:
foreach injsData:
print each['title']+" "+each['url']
显然,所有有用的信息都已经在jsData这个列表里面,而现在为止,有两个问题:
1. 下一页的数据如何爬取;
2. 新闻内页的文字如何爬取。
二、分析
首先,下一页的数据。点击底下的页码的时候,数据变化,而浏览器地址栏是不变的,所以还是要从json源的地址入手。而通过观察json源地址:http://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page=1&channel=sports可知,page=1,这个1很可能是相应页码,同时,共114页,我们只取前20。
for i in range(1, 21):
print i
所以可以构造一个函数专门来读取标题+日期+正文。
以页面:http://sports.sina.com.cn/g/pl/2017-01-07/doc-ifxzkfuk2454395.shtml为例,可知想得到的信息分别是:
标题:<h1id="artibodyTitle">欧文:穆帅别抱怨裁判专注比赛吧瓜帅没必要改变</h1>
title_news =bsObj.find("h1",{"id":"artibodyTitle"}).string #获取h1标签的内容
日期:<spanid="pub_date">2017年01月07日07:01</span>
date_news =bsObj.find("span", {"id":"pub_date"}).string #同上
内容:<divclass="BSHARE_POP blkContainerSblkCon clearfixblkContainerSblkCon_14" id="artibody">下的所有p标签的内容。
content_news =bsObj.find("",{"id":"artibody"}).findAll("p") #先找到id为artibody的元素,再向下寻找p标签
for content in content_news:
print content.get_text() #获得每一个p标签的内容
三、构造
至此,这个函数已写完,解析新闻内页的函数:
def getNewsContent(url_news):
html_WebPage = requests.get(url_news,headers = headersIm)
html_WebPage.encoding = 'utf-8'
html_WebText = html_WebPage.text
bsObj = BeautifulSoup(html_WebText, 'lxml') #shtml的解析使用lxml方式
title_news = bsObj.find("h1",{"id":"artibodyTitle"}).string #获取h1标签的内容
date_news = bsObj.find("span", {"id": "pub_date"}).string #同上
content_news = bsObj.find("", {"id":"artibody"}).findAll("p") #先找到id为artibody的元素,再向下寻找p标签
for content in content_news:
print content.get_text() #获得每一个p标签的内容
像这种:http://k.sina.cn/article_5695956221_1538164fd02000165x.html?cre=aspect&mod=2L&loc=27&r=0&doct=0&rfunc=39&tj=none&s=0&from=sports&vt=4就不解析了,所以还要用到python的try...except。另外就是把标题,时间,内容,都存储为csv和txt,后面我分别写。
然而,获得的标题,时间,内容,经过测试,都是NavigableString类型的,需要把这种类型写入txt。只要在这种对象后面加上.encode('utf-8')即可转换:title_news = bsObj.find("h1",{"id":"artibodyTitle"}).string.encode('utf-8')
四、写入txt
最终的程序,把文章标题,日期,和内容存储在txt文档中(爬小说像不像):
# encoding=utf-8 注释可用中文
import requests
import json
from bs4 import BeautifulSoup
headersIm = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
f = open("news.txt", 'a')
def getNewsContent(url_news):
html_WebPage = requests.get(url_news,headers = headersIm)
html_WebPage.encoding = 'utf-8'
html_WebText = html_WebPage.text
bsObj = BeautifulSoup(html_WebText, 'lxml') #shtml的解析使用lxml方式
try:
title_news = bsObj.find("h1",{"id": "artibodyTitle"}).string.encode('utf-8') #获取h1标签的内容,.encode('utf-8')转换为str
date_news = bsObj.find("span", {"id": "pub_date"}).string.encode('utf-8') #同上
content_news = bsObj.find("", {"id":"artibody"}).findAll("p") #先找到id为artibody的元素,再向下寻找p标签
f.write(title_news.rstrip()+'----'+date_news.lstrip()) #将标题和日期用----连接,并删除标题右侧和日期左侧的空格回车等,规范格式 写入
for content in content_news:
news_text = content.get_text().encode('utf-8') #获得每一个p标签的内容,转换为str
f.write(news_text) #写入txt
f.write('\n') #文章末尾加换行符,区别两块新闻
except:
pass
finally:
pass
def newsListPages(urlPages):
jsContent = requests.get(urlPages, headers=headersIm).content #requsets解析文章列表
jsDict = json.loads(jsContent) #将内容用json库解析
jsResult = jsDict['result']
jsData = jsResult['data']
for each in jsData:
newsUrl = each['url'] #获得相应文章的url
getNewsContent(newsUrl) #函数解析文章内页内容
for i in range(1, 3):
url_json = 'http://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page='+str(i)+'&channel=sports'
#构造json数据源的地址
newsListPages(url_json)
f.close()
原理:分析从上至下,构建从下至上。首先分析树状结构,之后再构建解析文章—解析文章列表。但是,这样单线程似乎有点慢,如果用多线程的方式,首先需要构建文章列表,再用函数进行统一解析。
结果:
尴尬!鲁尼找对手换球衣被拒 来自曼城的恨(图)----2017年01月08日01:11
新浪体育讯 曼联4-0大胜雷丁一战,对于鲁尼本人意义非凡,此役他追平了队史射手王查尔顿爵士249球的纪录。按理说,这样一场比赛,谁都想沾沾鲁尼的喜气,但是雷丁后卫乔治-埃文斯便是个另类,他拒绝了鲁尼主动交换球衣的请求,原因是什么呢?
五、写入csv
写入csv格式类似:
# encoding=utf-8 注释可用中文
import requests
import json
from bs4 import BeautifulSoup
import csv
import codecs #保证csv能正确写入的库
headersIm = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
csvfile = file('csv_test.csv', 'wb') #创建csv文件,并准备写入
csvfile.write(codecs.BOM_UTF8) #csv写入中文必备
writer = csv.writer(csvfile) #构建写入对象
writer.writerow(['标题', '日期', '内容'])
def getNewsContent(url_news):
html_WebPage = requests.get(url_news,headers = headersIm)
html_WebPage.encoding = 'utf-8'
html_WebText = html_WebPage.text
bsObj = BeautifulSoup(html_WebText, 'lxml') #shtml的解析使用lxml方式
try:
title_news = bsObj.find("h1",{"id": "artibodyTitle"}).string.encode('utf-8') #获取h1标签的内容,.encode('utf-8')转换为str
date_news = bsObj.find("span", {"id": "pub_date"}).string.encode('utf-8') #同上
content_news = bsObj.find("", {"id":"artibody"}).findAll("p") #先找到id为artibody的元素,再向下寻找p标签
content_str = ''
for content in content_news:
news_text = content.get_text().encode('utf-8') #获得每一个p标签的内容,转换为str
content_str += news_text #将文章正文存储在相应字符串中
writer.writerow([title_news, date_news, content_str]) #构建每一行的格式,并写入
except:
pass
finally:
pass
def newsListPages(urlPages):
jsContent = requests.get(urlPages, headers=headersIm).content #requsets解析文章列表
jsDict = json.loads(jsContent) #将内容用json库解析
jsResult = jsDict['result']
jsData = jsResult['data']
for each in jsData:
newsUrl = each['url'] #获得相应文章的url
getNewsContent(newsUrl) #函数解析文章内页内容
for i in range(1, 3):
url_json = 'http://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page='+str(i)+'&channel=sports'
#构造json数据源的地址
newsListPages(url_json)
csvfile.close()
六、总结:
request负责获取html页面,json负责解析数据源,bs负责解析html内页获取相关内容(中间涉及编码问题,在stackoverflow找到答案),最后是写入txt(自带函数),写入csv(csv和codecs模块)。