网页解析
一,BeautifulSoup
- 安装:
pip install beautifulsoup4
- 官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
1.1,简单的例子
In [1]: html_doc = """
...: <!doctype html>
...: <html>
...: <head>
...: <meta charset="UTF-8">
...: <title>这是一个网页标题</title>
...: </head>
...: <body>
...: <p class="p1", id="pp1">今天真是个好日子</p>
...: <p class="p2", id="pp2">明天真是个好日子</p>
...: <script></script>
...: </body>
...: </html>
...: """
In [2]: soup = BeautifulSoup(html_doc) # 创建一个beautifulesoup的对象
In [3]: soup.title # 取 title 标签
Out[3]: <title>这是一个网页标题</title>
In [4]: soup.title.name # 取 title 标签的名字
Out[4]: 'title'
In [5]: soup.title.string # 取 title 标签里面的文本的内容
Out[5]: '这是一个网页标题'
In [6]: soup.title.parent # 取 title 标签的父级标签
Out[6]:
<head>
<meta charset="utf-8"/><!--国际编码 万国码-->
<title>这是一个网页标题</title>
</head>
In [7]: soup.p # 取第一个 p 标签
Out[7]: <p class="p1" id="pp1">今天真是个好日子</p>
In [8]: soup.p['class'] # 取第一个 p 标签 里的类名
Out[8]: ['p1']
In [9]: soup.a # 取第一个 a 标签
Out[9]: 今天真是个好日子
In [10]: soup.find_all('a') # 取所有的 a 标签
Out[10]: [今天真是个好日子, 今天真是个好日子]
In [11]: soup.find(id='pp1') # 取 id 为 pp1 的标签
Out[11]: <p class="p1" id="pp1">今天真是个好日子</p>
1.2,解析器
解析器在大规模的爬取中时会影响整个爬虫系统的速度的,所以推荐使用 lxml 速度快很多
- xml需要单独安装:
python -m pip install lxml
-Tag对象
Beautifulsoup里面最重要的对象
soup = BeautifulSoup(html_doc, 'lxml') # 生成一个beautifulsoup对象,解析器用 lxml
p = soup.p
print(type(p)) # 判断该标签的类型
--> <class 'bs4.element.Tag'>
print(p.name) # 拿到该标签的标签名字
--> p
print(p['class']) # 拿到单个 p 标签下的 class 名
--> ['p1']
print(p['id']) # 拿到单个 p 标签下的 id 名
--> pp1
print(p.get_text()) # 拿到单个 p 标签下的所有文本内容
--> 今天真是个好日子
body = soup.body
print(body.get_text()) # 获取页面下所有的文本内容
--> 今天真是个好日子
今天真是个好日子
-contents和children
两个方法都可以把指定对象的直接子节点清洗出来(只要儿子),children返回迭代器节省内存
soup = BeautifulSoup(html_doc, 'lxml') # 生成一个beautifulsoup对象,解析器用 lxml
# 获取 body 节点下的所有子辈节点(回车也算子节点)
print(soup.body.contents)
--> ['\n', <p class="p1" id="pp1"><a href="http://www.baidu.com">今天真是个好日 子</a></p>, '\n', <p class="p2"
id="pp2"><a href="">明天是个好日子</a></p>, '\n', <script></script>, '\n']
# 返回一个迭代器,大量数据可以一部分一部分迭代出来处理(节省内存)
print(soup.body.children)
--> <list_iterator object at 0x0000026A9B9EEEF0>
-descendants
返回指定对象下的所有直节点(儿子孙子都要)
soup = BeautifulSoup(html_doc, 'lxml')
print(list(soup.body.descendants))
--> ['\n', <p class="p1" id="pp1"><a href="http://www.baidu.com">今天真是个好日子</a></p>, <a href="http://www.baidu.com">今天真是个好日子</a>, '今天真是个好日子', '\n', <p class="p2" id="pp2"><a href="">明天是个好日子</a></p>, <a href="">明天是个好日子</a>, '明天是个好日子', '\n', <script></script>, '\n']
-string和strings及stripped_strings
string只能返回单个标签下的文本信息,strings则能返回所有信息
print(soup.p.string)
--> 今天真是个好日子
print(soup.body.string)
--> None
print(list(soup.body.strings))
--> ['\n', '今天真是个好日子', '\n', '明天是个好日子', '\n', '\n']
print(list(soup.body.stripped_strings))
--> ['今天真是个好日子', '明天是个好日子']
-next/previous_sibling
# 获取 p 标签的下一个兄弟节点
print(soup.p.next_sibling)
# 获取 p 标签的上一个兄弟节点
print(soup.p.previous_sibling)
# 获取 p 标签的下面的所有兄弟节点
print(soup.p.next_sibling)
# 获取 p 标签的上面的所有兄弟节点
print(soup.p.previous_sibling)
-find_all()
# 获取文档中所有的 p 标签
print(soup.find_all('p'))
# 获取文档中所有的 p, a 标签
print(soup.find_all(['p', 'a']))
# 返回所有属性为 p1 的标签
print(soup.find_all(attrs={'class': 'p1'}))
# 返回文本内容是 text 的标签
print(soup.find_all('a', text='今天真是个好日子'))
# 限制查找范围,只会在 soup 对象的子代里面取找
print(soup.find_all('p', recursive=False))
# 结合正则,查找出所有已 b 字母开头的标签
import re
tags = soup.find_all(re.compile("^b"))
# 通过选择器查找
print(soup.select('p')) # 拿到所有的 p 标签
print(soup.select('p > a')) # 拿到所有的 p 标签下的 a 标签
二,XPath
在 XPath 中使用路径表达在 HTML/XML 文档中选取节点
from lxml import etree
page = etree.HTML(html_doc) # 返回 html 的节点
print(page.xpath('/html/body')) # 拿到body标签
print(page.xpath('//p')) # 拿到文档下的所有的 p 标签
print(page.xpath('.')) # 拿到当前的节点
print(page.xpath('..')) # 拿到当前节点的父节点
print(page.xpath('//p/@class')) # 获取所有p标签的class属性
谓语:用来查找某些特定节点或包含某个指定值的节点
路径表达式 | 结果 |
---|---|
page.xpath('//p/a[1]') | 选取所有 p 标签下的各自的 第一个 a 标签 |
page.xpath('//p/a[last()]') | 选取所有 p 标签下各自的 最后一个 a 标签 |
page.xpath('//p/a[last()-1]') | 选取所有 p 标签下各自的 倒数第二个 a 标签 |
page.xpath('//p/a[position()<2]') | 选取所有 p 标签下各自的位置小于 2 的 a 标签 |
page.xpath('//p[@class]' | 选取所有拥有 class 类名的 p 标签 |
page.xpath('//p[@class="p1"]') | 选取所有拥有 class 类名为 p1 的标签 |
page.xpath('//p[a>19]') | 选取 p 标签里的 a 标签文本值大于 19 的该 p 标签 |
page.xpath('//p[a>19]/a') | 选取 p 标签里的 a 标签文本值大于 19 的该 p 标签下的 a 标签 |
选取未知节点,XPath通配符(*)可以选取
通配符 | 路径表达 | 结果 |
---|---|---|
* | page.xpath('//p/*') | 选取所有 p 标签下的所有标签 |
//* | page.xpath('//*') | 选取文档中的所有的标签 |
@* | page.xpath('//p[@*]')) | 选取所有拥有属性的 p 标签 |
文本的选取
路径表达式 | 结果 |
---|---|
page.xpath('//a|//p') | 选取所有的 a 标签和 p 标签 |
page.xpath('//a/text()') | 只能获取 a 标签下的文本(只包括儿子) |
page.xpath('string(//p)') | 选取所有的 p 标签下的文本(后代也包括) |
模糊匹配
路径表达式 | 结果 |
---|---|
//div[contains(@class, "only")] | 匹配出 class 属性中带有 only 的 div |
//div[starts-with(@class, "only")] | 匹配出 class 属性中以 only 开头的 div |
response.xpath('//div[@class="c" and @id]') | 匹配出带有class='c’并且有id的div标签 |
案例一:使用beautifulsoup提取网页
import requests
from bs4 import BeautifulSoup
page_url = 'http://www.quanshuwang.com/book/106/106281/27857005.html'
response = requests.get(page_url)
response.encoding = 'gbk'
html = response.text
soup = BeautifulSoup(html, 'lxml')
div = soup.find_all('div', attrs={'class': 'mainContenr', 'id': 'content'})[0]
context = ''.join([x.strip() for x in div.stripped_strings][1:-1])
print(context)
高大的树木茂密得连阳光也无法透入,以至于,在这片大森林之中,看到的只有阴暗。在森林深处,有一片小湖,湖水清澈见底,澄净的宛如一块湛蓝色的水晶,可是,它的水位距离岸边却已经遥远,干涸似乎随时都可能到来。生命的气息在湖水中荡漾,但却并不强烈,甚至是,有些虚弱。湖边,站着一个人。他穿着一身黑色长袍,看上去四十多岁的样子,相貌英俊而刚毅,额头上,有一缕金发垂在面颊一侧。他就那么站着,眼神似乎有些呆滞,身上更是有着几分颓丧的气息。就在距离他不远处,还站着一些人,有高有矮、有胖有瘦,形态各异。但每个人的眼神,却都十分黯淡。“兽神。”一名身穿翠绿色长裙的女子,悄然来到那黑衣男子身后,恭敬的说道。被称作兽神的黑衣男子全身震了震,嘴角流露出一丝苦涩,“兽神?现在我们魂兽,恐怕就剩下眼前这些了吧。我还做谁的神?”绿裙女子沉默了一下,才低声道:“一万年了,从当初那霍雨浩创建传灵塔组织到现在,已经过去了整整一万年。传灵塔组织还在,可我们,却终于要走向灭绝了吗?”兽神苦涩的道:“人类的强大,已经令我们无法抗衡。星斗大森林,也只剩下最后这一片净土。”“是啊……”绿裙女子正想要继续说些什么。被称为兽神的男子突然猛地抬起头,眼眸中爆射出两道实质般的金光,那瞬间勃发的恐怖气息,似乎令整个世界都为之颤抖起来。“嗡……”他们脚下的地面轻微的振颤了一下,紧接着,面前的湖水居然如同沸腾一般,冒出一片片气泡。这些气泡飞速升腾,紧接着,大地震颤的频率也变得剧烈起来。“怎么回事?人类来了?”绿裙女子惊呼道。“跟他们拼了!”一个身形伟岸的壮汉怒吼一声,摇身一晃,竟然化作一头身高超过三十米的巨熊,全身散发着暗金色的光泽。“熊君,冷静。不是人类。”兽神高声喝道,在他那原本暗淡的面庞上,竟是流露出了一抹难以形容的狂喜。“结束了、结束了、结束了……”低沉的声音,毫无预兆的在森林中徘徊。这声音仿佛从四面八方传来,因为过于低沉,甚至听不出来男女。“轰——”大地龟裂,整个大森林都在剧烈的颤抖,那小湖中原本就残存不多的湖水倒灌而入,顷刻间露出了湖底。“砰——”一团银光骤然从那大地的裂缝中涌出,然后重重的拍击在岸边。那是一只巨大的爪子,通体灿银色,在那灿银色的巨爪上,密布着一块块六边形的银色鳞片。每一块鳞片上都折射着奇异的光彩,那巨大的拍击声,带着无与伦比的强大压迫力,令所有生命体都要为之跪拜。兽神眼中狂喜之色更胜,一步上前,单膝跪倒在地,恭声道:“恭迎主上。”大地瞬间炸开,强盛的气息甚至令那三十米高的巨熊也随之飞跌而出,一个身长超过百丈的巨大身影骤然腾空而起,下一瞬,重重的落在地面上。一株株巨树随之拔地而起,原本站在周围的一个个人类,竟然全都变成了一只只巨兽。但他们面对那银色的庞然大物,却只能是匍匐在地。“它死了,但我还活着。”低沉的声音仿佛在咆哮,但似乎又有着一种浓浓的悲意。“卑鄙的人类,就要灭绝我们了吗?既然我醒了,他们毁灭的日子,也要到了。”灿银色光芒夺目,令所有体型巨大的魂兽们不敢直视,它们只能卑微的匍匐在那里,颤抖着,狂喜着。兽神急切的道:“主上,现在的人类,已经太过强大,他们那最顶级的魂导机甲,就算是我,也无法对抗太多。人类,已经凭借科技的力量,让我们无法抗衡。”银色的庞大身躯缓缓低下头,低沉的声音在这片不再广袤的森林中回荡:“要毁灭他们,就要先了解他们。你们,跟我来。我们生存的世界已经即将被他们完全破坏,那么,我们就要征服他们的世界。”巨大的身影缓缓迈开脚步,朝着森林外的方向走去,在那巨大的树冠遮蔽下,在这阴暗的光线中,它那庞大的身躯,缓缓缩小着、缩小着。当它在视线尽头渐渐消失时,却已化为人形……-------------------------------------------写在开篇的话。小唐:亲爱的唐门书友们,我们斗罗大陆的世界又回来了。龙王传说,是我们斗罗大陆的第三部,也将是非常重要的一部,相信看过斗罗大陆外传神界传说的书友们都知道,神界被时空乱流卷走了,小舞麟被留在了大陆上,因为他被金龙王精华入体,唐三在他身上下了十八道封印。我们这斗罗大陆第三部的故事,就是从小舞麟身上开始的,他也将是这一部的男主角。斗罗系列,一直都是大家最喜爱,也是耗费了我最多心血的系列。龙王传说的故事,在很早以前我就想好了,而且有着完整的规划。大家将会看到一个更加瑰丽的世界。我们会有全新设定:斗铠,加入其中。同时,你们最喜爱的武魂也将回归。但一切都会变得不一样了。想知道我们的小舞麟如何来解决身上十八道封印和金龙王精华的问题吗?想知道新一代史莱克七怪又是什么样的吗?想知道谁是最终的女主角吗?那么,就请跟随小唐一起进入斗罗三的世界吧。新书刚刚开启,我先注册上,天火大道进入最后一周了。在这一周内,我们龙王传说将偶尔更新,所以大家可以偶尔来看看哦。麻烦大家先行收藏,推荐。下周一零点,也就是一月十八日,我们将正式开始上传,依旧是无缝隙衔接哦!唐门的第十二年即将结束,第十三年即将开启!我深深的爱着你们每一个人,也期待着你们跟随我一起,重回斗罗,重新体会那让我们魂牵梦绕的世界。爱你们、爱斗罗!故事,即将开始!