Python爬取新浪英超曼联文章内页--bs4,json,txt和csv以及编码
这个页面是新浪英超曼联新闻的首页,直接用lxml爬取,得到它是动态的爬不到,所以要考虑它的信息处理机制,使用Chrome审查元素,在Network--Priview中可以找到它的json源。通常可以把&callbacak=直到最后删除,之前的信息似乎没什么用处,有用的时候再说。
之后使用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 = "https://interface.sina.cn/pc_zt_roll_news_index.d.html?&subjectID=64558&channel=sports"
# json_url = 'https://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 = 'https://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']
今天的结果:
穆帅示爱曼联球迷+球队 他终成“快乐的一个”?
7战6球!伊布当选英超月最佳 力压切尔西王牌
曼联大将自晒缝针照片 穆帅手下他已成红魔枢纽
打脸!谁说穆里尼奥会毁了曼联两大妖王?
曝穆帅欲签一昔日爱将 在皇马切尔西都用过他
......
虽然爬取网页信息,爬的不多,但是通常有两大方法:一个是从网页本身入手用lxml定位相应的h5元素,另一个是直接找信息源,通常是json。
又运行了一下程序,得到的结果是:
曼联猎物正式提交转会申请 同胞丰特或驰援鸟叔
曼联宣布12月最佳球员 伊布力压两核心首当选......
证明这个爬虫确实是爬取动态网站的。
没做完的部分:将标题,内容都整理好,只爬取前20条信息就足够了(或者加上后面10页的内容),进入内页后,将文章的文字部分也提取出来。
一、问题:
显然,只爬取标题是不够的,最主要的还是url链接,内页的正文:
foreach injsData: print each['title']+" "+each['url']
显然,所有有用的信息都已经在jsData这个列表里面,而现在为止,有两个问题:
1. 下一页的数据如何爬取;
2. 新闻内页的文字如何爬取。
二、分析
首先,下一页的数据。点击底下的页码的时候,数据变化,而浏览器地址栏是不变的,所以还是要从json源的地址入手。而通过观察json源地址:https://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
所以可以构造一个函数专门来读取标题+日期+正文。
以页面:https://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标签的内容
像这种:https://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 = 'https://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 = 'https://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内页获取相关内容(中间涉及编码问题,在*找到答案),最后是写入txt(自带函数),写入csv(csv和codecs模块)。
上一篇: PHP 生成图像验证码,个性化参数
下一篇: Python pycharm的快捷键汇总