摘要
- 有哪些解析页面的场景
- 针对这些场景的解析页面实战
解析页面的几种场景
我们在上一步发送请求后拿到网页源码,下一步就是提取源码中我们想要的内容了,根据我的多年(不到一年)的爬虫经验,提取源码大致分为这么几种情况:
有效内容在网页源码中
对于通过鼠标右键查看网页源码就能看到我们想要的内容的情况,有效内容往往会存在以下两个地方:
1、有效内容在html的标签里,比如某个div标签,这种情况最常见。
2、有效内容在某个js代码中不规则分布,比如某头条的新闻正文页面,附上链接:
有效内容不在网页源码中
对于这种情况,我们一般通过F12的network找到有效请求后,查看其response往往是一个大json,比如腾讯体育滚动新闻:
其有效数据的url为
针对以上的情景,我们可以把解析页面的方法分为如下几类:
提取html标签的有效内容
提取js中的有效内容
提取json的有效内容
针对这些场景的解析页面实战
根据上面的三种情况,我们分别举三个例子来介绍如何解析页面,拿到我们想要的信息。
首先,我先介绍三个常用的解析页面提取信息的工具:
1、beautifulsoup:简直是一个神器!解析内容在html标签中的页面时超级给力!
2、xpath:顾名思义,是指根据html的路径来找到我们想要的信息,一直觉得beautifulsoup是xpath的升级版,更智能更易使用,所以xpath我用的也不多。
3、re:正则表达式,据我经验,很多json数据都不是特别规则的json,或者冗余信息太多,对于这种情况,可以通过正则匹配,在我看来就是找规律,来匹配获得想要的信息。
4、解析json:当然,对于规则的json,可以通过python自带的json解析包来完成信息提取。
5、find和rfind:python自带的字符串查找方法,根据寻找某个关键词的位置来截取有效信息,适合内容在js中的情况。
提取html标签中的内容
网页部分源码:
<span class="a_ilike" bosszone="textFvrtop">
<a href="javascript:void(0)">收藏</a>
</span><!--[if !IE]>|xGv00|9e1af0bee8cab9a1931a638641907475<![endif]--> <!--[if !IE]>|xGv00|da7bcdfd4f43adfd202030b53d9941f4<![endif]-->
</div>
</div>
<div class="bd" accesskey="3" tabindex="-1">
<div id="Cnt-Main-Article-QQ" class="Cnt-Main-Article-QQ" bossZone="content"><p class="text" style="TEXT-INDENT:2em">
<p align="center"><img src="//inews.gtimg.com/newsapp_bt/0/4218647361/641" style="display:block;"/></p>
</p><p class="text" style="TEXT-INDENT:2em">“中国冰球协会与首钢体育战略合作”启动仪式7月2日在首钢体育大厦举行。根据合作计划,中国冰球协会与北京首钢体育文化有限公司将汇聚各自资源、拓展合作渠道,努力实现互利共赢。双方将在国家冰球集训队的管理保障、冰球后备人才培养等方面展开全方位深度合作。</p>
<p class="text" style="TEXT-INDENT:2em">中国冰球协会主席曹卫东表示,中国冰球协会将与首钢体育紧密合作,不断提升中国冰球国家集训队的训练保障、海外集训、商业开发、人才培养等工作质量,保障中国冰球更好地备战2022冬奥会和未来的长远发展。</p>
<p class="text" style="TEXT-INDENT:2em">有着百年历史的国家特大型企业首钢集团,正在积极探索“体育报国”的国家战略新思路。在冰球领域,去年首钢冠名赞助了北京男子冰球队,并与中国冰协、北京市体育局共建成立了北京首钢冰球国家队俱乐部。此次与中国冰球协会启动战略合作,体现了多方合力进一步深耕冰球领域的思路,展现出首钢体育为北京冬奥和中国冰球人才培养作出更大贡献的决心。</p>
<p class="text" style="TEXT-INDENT:2em">根据合作计划,此次首钢体育将全面助力中国冰球国家集训队的商业运营、队伍管理、训练和比赛保障等,这将对北京首钢冰球国家队俱乐部的高效建设具有重大推动作用。</p>
</div>
<span style="width:0;height:0;overflow:hidden;display:block;font:0/0 Arial">正文已结束,您可以按alt+4进行评论</span>
</div>
</div>
<div class="qq_articleFt">
<div class="qq_toolWrap clearfix">
<div class="qq_editor" id="QQeditor">责任编辑:felixyan</div>
我们可以发现,正文内容在一个div中的所有p标签里,div的id=”Cnt-Main-Article-QQ”。这种情况我们用beautifulsoup解决,上代码:
import requests
from bs4 import BeautifulSoup
r = requests.get("http://sports.qq.com/a/20180704/033562.htm")
#print(r.text)
soup = BeautifulSoup(r.text,"lxml")
# 根据class的名称定位到特定的div标签
content = soup.find("div",class_="Cnt-Main-Article-QQ")
print(content)
# 得到div标签中的内容
'''
<div bosszone="content" class="Cnt-Main-Article-QQ" id="Cnt-Main-Article-QQ"><p class="text" style="TEXT-INDENT:2em"></p><p align="center"><img src="//inews.gtimg.com/newsapp_bt/0/4218647361/641" style="display:block;"/></p>
<p class="text" style="TEXT-INDENT:2em">“中国冰球协会与首钢体育战略合作”启动仪式7月2日在首钢体育大厦举行。根据合作计划,中国冰球协会与北京首钢体育文化有限公司将汇聚各自资源、拓展合作渠道,努力实现互利共赢。双方将在国家冰球集训队的管理保障、冰球后备人才培养等方面展开全方位深度合作。</p>
<p class="text" style="TEXT-INDENT:2em">中国冰球协会主席曹卫东表示,中国冰球协会将与首钢体育紧密合作,不断提升中国冰球国家集训队的训练保障、海外集训、商业开发、人才培养等工作质量,保障中国冰球更好地备战2022冬奥会和未来的长远发展。</p>
<p class="text" style="TEXT-INDENT:2em">有着百年历史的国家特大型企业首钢集团,正在积极探索“体育报国”的国家战略新思路。在冰球领域,去年首钢冠名赞助了北京男子冰球队,并与中国冰协、北京市体育局共建成立了北京首钢冰球国家队俱乐部。此次与中国冰球协会启动战略合作,体现了多方合力进一步深耕冰球领域的思路,展现出首钢体育为北京冬奥和中国冰球人才培养作出更大贡献的决心。</p>
<p class="text" style="TEXT-INDENT:2em">根据合作计划,此次首钢体育将全面助力中国冰球国家集训队的商业运营、队伍管理、训练和比赛保障等,这将对北京首钢冰球国家队俱乐部的高效建设具有重大推动作用。</p>
</div>
'''
# 得到div标签中所有p标签的文本内容
contents = ""
content_list = content.find_all("p")
for i in content_list:
contents += i.get_text()
print(contents)
'''
“中国冰球协会与首钢体育战略合作”启动仪式7月2日在首钢体育大厦举行。根据合作计划,中国冰球协会与北京首钢体育文化有限公司将汇聚各自资源、拓展合作渠道,努力实现互利共赢。双方将在国家冰球集训队的管理保障、冰球后备人才培养等方面展开全方位深度合作。中国冰球协会主席曹卫东表示,中国冰球协会将与首钢体育紧密合作,不断提升中国冰球国家集训队的训练保障、海外集训、商业开发、人才培养等工作质量,保障中国冰球更好地备战2022冬奥会和未来的长远发展。有着百年历史的国家特大型企业首钢集团,正在积极探索“体育报国”的国家战略新思路。在冰球领域,去年首钢冠名赞助了北京男子冰球队,并与中国冰协、北京市体育局共建成立了北京首钢冰球国家队俱乐部。此次与中国冰球协会启动战略合作,体现了多方合力进一步深耕冰球领域的思路,展现出首钢体育为北京冬奥和中国冰球人才培养作出更大贡献的决心。根据合作计划,此次首钢体育将全面助力中国冰球国家集训队的商业运营、队伍管理、训练和比赛保障等,这将对北京首钢冰球国家队俱乐部的高效建设具有重大推动作用。
'''
提取js中的有效内容
部分网页源码:
<script>var BASE_DATA = {...
articleInfo: {
title: '史上首次!中国四大银行位居全球1000家大银行前四名',
content: '<div><p>7月2日,全球权威杂志英国《银行家》发布2018年全球<span>1000家大银行榜单</span>,中国四大银行首次位列1000大银行前四名。</p><p>按照排名顺序,前四名分别为:中国工商银行、中国建设银行、中国银行、中国农业银行。</p><p>与2017年相比,前十大银行名单整体保持不变,仍主要集中在中国(4家)、美国(4家)、英国(1家)和日本(1家)四国。</p><div><img src="http://p3.pstatp.com/large/pgc-image/15305427142154b0e083b30" img_width="771" img_height="240" alt="史上首次!中国四大银行位居全球1000家大银行前四名" inline="0"><p></p></div><div><img src="http://p3.pstatp.com/large/pgc-image/15305427144787522bb06ae" img_width="1080" img_height="1256" alt="史上首次!中国四大银行位居全球1000家大银行前四名" inline="0"><p></p></div><p>中国银行国际金融研究所原晓惠分析称,今年榜单的最大变化是前四大银行都是中资银行,而去年除摩根大通以外前四大银行有3家中资银行。中国银行、中国农业银行的排名分别上升1位和2位,与此相对应的是美资银行排名都有所下降。中国银行业在一级资本总额、资产总额、税前利润总额上连续三年超越欧元区、美国,位居各国家和地区榜首。</p><p>从榜单还可以看出,全球银行业七年来首次迎来两位数的收益和资本增长。就全球1000大银行来看,2017年合计税前利润达1.1万亿美元,同比增长15.57%;即使是长期处于负利率政策下的日本银行业利润增长率也提高至7.6%。</p><div><img src="http://p1.pstatp.com/large/pgc-image/153054283475805397de3f1" img_width="400" img_height="243" alt="史上首次!中国四大银行位居全球1000家大银行前四名" inline="0"><p>图片来源网络</p></div><p>同时,全球1000大银行2017年一级资本总额合计8.2万亿美元,同比增长11.67%,除拉丁美洲和加勒比海地区以外,全球所有地区的一级资本增速均突破两位数。此次利润增速和一级资本增速水平均突破两位数为2010年以来首次,说明全球银行业复苏态势强劲。</p><p>在银行就业方面则出现两极分化:一方面,一些中小银行仍在不断扩大员工数量,以快速扩大规模;另一方面,不少大型银行都在精简员工人数,以实现有机增长和可持续发展。中国大型银行与中小银行的差异更为明显:建设银行2017年减少1万名员工,华夏银行、兴业银行、浦发银行则位居全球新增员工数量最多的前10大银行榜单。</p><hr/><p>来源:国是直通车</p><p>作者:魏晞</p><p>编辑:杨佳欣</p></div>',
groupId: '6573633703681982984',
itemId: '6573633703681982984',
type: 1,
...</script>
可以看到正文在script标签的content:和groupId:之间,于是我们需要截取字符串,然后将正文中的乱码进行处理即可,上代码:
import requests
from bs4 import BeautifulSoup
from html.parser import HTMLParser
import re
r = requests.get("https://www.toutiao.com/a6573633703681982984/")
htmls = r.text
#截取content和groupId之间的字符串
htmls = htmls[htmls.find("content: ")+8:htmls.find("groupId")]
print(htmls)
#对于类似>的这些html字符进行转义
htmls = HTMLParser().unescape(htmls)
print(htmls)
#转义成html标签后,去掉html标签
res = re.compile(r'<[^>]+>',re.S)
content = res.sub('',htmls)
print(content)
提取json的有效内容
其有效数据的url为
返回的值为
rollback({
"response": {
"code": "0",
"msg": "Success",
"dext": ""
},
"data": {
"count": 3,
"page": 1,
"article_info": [{
"title": "“小库里”:适应比赛是首要任务 投篮终会找到节奏",
"url": "http:\/\/sports.qq.com\/a\/20180704\/035378.htm",
"time": "2018-07-04 16:58:36",
"column": "NBA",
"img": "",
"desc": ""
}, {
"title": "首钢体育助力国家冰球集训队 中国冰球联赛年底启动",
"url": "http:\/\/sports.qq.com\/a\/20180704\/034698.htm",
"time": "2018-07-04 16:34:44",
"column": "综合体育",
"img": "",
"desc": ""
}...]
}
})
对于这种json,我们当然可以通过python自带的json解析包来获取想要的内容,不过看到这种规律性极强的json,我更喜欢用正则提取,也为了以防这个json不太规则,比如少个引号,上代码:
import requests
import re
headers = {
"Referer":"http://sports.qq.com/articleList/rolls/"
}
url = "http://roll.news.qq.com/interface/cpcroll.php?callback=rollback&site=sports&mode=1&cata=&date=2018-07-04&page=1&_=1530694824593"
r = requests.get(url,headers=headers)
htmls = r.text
# 把每个部分中要获取的内容用(.*?)来标识
reg_str = r'"title":"(.*?)",.*?"url":"(.*?)","time":"(.*?)","column":"(.*?)",'
pattern = re.compile(reg_str,re.DOTALL)
items = re.findall(pattern,htmls)
# items中的每个i是按正则规则出现的每个部分,在每个部分中根据第几个(.*?)表示什么内容来确定数组下标。
for i in items:
title = i[0]
weburl = i[1]
pubtime = i[2]
column = i[3]
更多技巧
更多技巧请见我博客的其他文章,爬虫解析页面常用代码集锦,里面汇集了各种可能出现的情况以及解析方法,包括html转义、url字符转码、过滤各种标签等。