页面解析工具

页面解析工具

Yiuhang Chan

BS4

BeautifulSoup 是一个可以从HTML或XML文件中提取数据的Python库,它的使用方式相对于正则来说更加的简单方便,常常能够节省我们大量的时间。

官方中文文档

https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

安装

pip install beautifulsoup4

解析器

解析器 使用方法 优势 劣势
Python标准库 BeautifulSoup(markup, “html.parser”) Python的内置标准库执行速度适中文档容错能力强 Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, “lxml”) 速度快文档容错能力强 需要安装C语言库
lxml XML 解析器 BeautifulSoup(markup, [“lxml”, “xml”])``BeautifulSoup(markup, “xml”) 速度快唯一支持XML的解析器 需要安装C语言库
html5lib BeautifulSoup(markup, “html5lib”) 最好的容错性以浏览器的方式解析文档生成HTML5格式的文档 速度慢不依赖外部扩展

由于这个解析的过程在大规模的爬取中是会影响到整个爬虫系统的速度的,所以推荐使用的是lxml,速度会快很多,而lxml需要单独安装:

1
2
pip install lxml
soup = BeautifulSoup(html_doc, 'lxml')

如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,所以要指定某一个解析器。

节点对象

Tag

tag就是标签的意思,tag还有许多的方法和属性。

BeautifulSoup

Comment

Tag和遍历文档树

tag对象可以说是BeautifulSoup中最为重要的对象,通过BeautifulSoup来提取数据基本都围绕着这个对象来进行操作。
首先,一个节点中是可以包含多个子节点和多个字符串的。例如html节点中包含着head和body节点。所以BeautifulSoup就可以将一个HTML的网页用这样一层层嵌套的节点来进行表示。

基本语法

1
2
3
4
5
from bs4  import BeautifulSoup#导包
soup=BeautifulSoup(html_str,'lxml')
type(soup)

bs4.BeautifulSoup

Tag 标签取出简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from bs4 import BeautifulSoup

html_data = """
<html>
<body>
<div>
我们都是害虫
</div>
<div id="info">
<span><span class="pl">导演</span>: <span class="attrs"><a href="/celebrity/1362276/" rel="v:directedBy">邢文雄</a></span></span><br>
<span><span class="pl">编剧</span>: <span class="attrs"><a href="/celebrity/1362276/">邢文雄</a></span></span><br>
<span class="actor"><span class="pl">主演</span>: <span class="attrs"><span><a href="/celebrity/1319032/" rel="v:starring">马丽</a> / </span><span><a href="/celebrity/1355058/" rel="v:starring">魏翔</a> / </span><span><a href="/celebrity/1362567/" rel="v:starring">陈明昊</a> / </span><span><a href="/celebrity/1319540/" rel="v:starring">周大勇</a> / </span><span><a href="/celebrity/1363857/" rel="v:starring">黄才伦</a> / </span><span style="display: none;"><a href="/celebrity/1350408/" rel="v:starring">艾伦</a> / </span><span style="display: none;"><a href="/celebrity/1394939/" rel="v:starring">高海宝</a> / </span><span style="display: none;"><a href="/celebrity/1386801/" rel="v:starring">韩笑</a> / </span><span style="display: none;"><a href="/celebrity/1444360/" rel="v:starring">孙贵权</a> / </span><span style="display: none;"><a href="/celebrity/1426220/" rel="v:starring">许猛</a> / </span><span style="display: none;"><a href="/celebrity/1467304/" rel="v:starring">全容杓</a> / </span><span style="display: none;"><a href="/celebrity/1467305/" rel="v:starring">卜俊男</a> / </span><span style="display: none;"><a href="/celebrity/1316008/" rel="v:starring">张志忠</a> / </span><span style="display: none;"><a href="/celebrity/1367242/" rel="v:starring">张建新</a> / </span><span style="display: none;"><a href="/celebrity/1398260/" rel="v:starring">马驰</a> / </span><span style="display: none;"><a href="/celebrity/1353283/" rel="v:starring">陶亮</a> / </span><span style="display: none;"><a href="/celebrity/1403276/" rel="v:starring">詹卢卡·佐帕</a></span><a href="javascript:;" class="more-actor" title="更多主演">更多...</a></span></span><br>
<span class="pl">类型:</span> <span property="v:genre">喜剧</span><br>

<span class="pl">制片国家/地区:</span> 中国大陆<br>
<span class="pl">语言:</span> 汉语普通话<br>
<span class="pl">上映日期:</span> <span property="v:initialReleaseDate" content="2022-02-01(中国大陆)">2022-02-01(中国大陆)</span><br>
<span class="pl">片长:</span> <span property="v:runtime" content="109">109分钟</span><br>
<span class="pl">又名:</span> Too Cool To Kill<br>
<span class="pl">IMDb:</span> tt16254308<br>

</div>
</body>
</html>
"""

soup = BeautifulSoup(html_data, 'lxml')

# 取第一个a标签(tag)里面的文本信息
print(soup.a.text, type(soup.a.text)) # 邢文雄 <class 'str'>
print(soup.a.string, type(soup.a.string)) # 邢文雄 <class 'bs4.element.NavigableString'>
print("===========================================================")

# 标签的属性
print(soup.a.get("href")) # /celebrity/1362276/
print("===========================================================")

# 子代标签
body = soup.body
print(list(body.children)) # <list_iterator object at 0x000001B973C23BE0>
print("===========================================================")

# 后代标签
print(list(body.descendants)) # <generator object Tag.descendants at 0x00000159C796A510>
test = body.descendants
print("===========================================================")

# 兄弟标签 (同级的标签,有相同的父标签)
span = body.span
print(span.next_sibling.next_sibling.next_sibling) # 下一个兄弟标签
# 第一个兄弟是 <br>
# 第二个兄弟是 \n
# 第三个兄弟是第一个内容标签
print(span.previous_sibling.previous_sibling)
# 第一个兄弟是 \n
# 第二个兄弟是 我们所需的标签
print("===========================================================")

# 父类标签, 加了s就是所有父类标签,不加就是它本身
p_parents = span.parents
print(list(p_parents)) # <generator object PageElement.parents at 0x000001A4DBD37E40>
1.find_all

直接通过名字和属性来进行访问,很多时候只能适用于比较简单的一些场景,所以BeautifulSoup还提供了搜索整个文档树的方法find_all()。

  • 通过name搜索,find_all('b')可以直接查找出整个文档树中所有的b标签,并返回列表

  • 通过属性搜索,我们在搜索的时候一般只有标签名是不够的,因为可能同名的标签很多,那么这时候我们就要通过标签的属性来进行搜索。这时候我们可以通过传递给attrs一个字典参数来搜索属性。
    soup.find_all(attrs={'class': 'sister'})

  • 通过文本搜索,在find_all()方法中,还可以根据文本内容来进行搜索。soup.find_all(string="Elsie")

  • 限制查找范围为子节点
    find_all()方法会默认的去所有的子孙节点中搜索,而如果将recursive参数设置为False,则可以将搜索范围限制在直接子节点中。 soup.html.find_all("title", recursive=False)

  • 通过正则表达式来筛选查找结果在BeautifulSoup中,也是可以与re模块进行相互配合的,将re.compile编译的对象传入find_all()方法,即可通过正则来进行搜索。tags = soup.find_all(re.compile("^b"))

  • 以之前的数据为例子:

    1
    2
    3
    4
    5
    print(soup.find_all('a', string='邢文雄')) 
    # [<a href="/celebrity/1362276/" rel="v:directedBy">邢文雄</a>, <a href="/celebrity/1362276/">邢文雄</a>]

    print(soup.find_all('span', attrs={'class': 'pl'}))
    # [<span class="pl">导演</span>, <span class="pl">编剧</span>, <span class="pl">主演</span>, <span class="pl">类型:</span>, <span class="pl">制片国家/地区:</span>, <span class="pl">语言:</span>, <span class="pl">上映日期:</span>, <span class="pl">片长:</span>, <span class="pl">又名:</span>, <span class="pl">IMDb:</span>]
2.CSS选择器

在BeautifulSoup中,同样也支持使用CSS选择器来进行搜索。使用select(),在其中传入字符串参数,就可以使用CSS选择器的语法来找到tag。

1
2
print(soup.select('a'))
print(soup.select('span > a'))

JSON Path

JSON Path 是一种查询语言,用于从 JSON (JavaScript Object Notation) 数据中提取和处理信息。它类似于 SQL,但专门用于与 JSON 数据交互。使用 JSON Path,可以从复杂的 JSON 结构中轻松检索特定元素或值。这在处理大型或嵌套的 JSON 数据时特别有用。

以下是 JSON Path 的一些主要用途:

  1. 数据提取:从 JSON 数据中选择和提取数据,例如提取特定属性的值或筛选符合特定条件的元素。
  2. 查询构建:创建灵活的查询来搜索和筛选 JSON 数据中的信息。
  3. 数据处理:简化对复杂 JSON 结构的遍历和分析,无需编写复杂的代码。
  4. API 交互:常用于与 RESTful API 交互时解析和处理返回的 JSON 数据。

JSON Path 通过提供一种简洁的语法来实现这些功能,使得从复杂的 JSON 结构中检索信息变得更加简单和直接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import jsonpath  # pip install jsonpath

dic = {
"resultCode": "1",
"resultMsg": "success",
"reqId": "52f9f3e1-1d76-47b4-b2ae-226633b61476",
"systemTime": "1681991278593",
"videoInfo": { # 父亲
"playSta": "1",
"video_image": "https://image.pearvideo.com/cont/20170714/cont-1110173-10436784.png",
"srcUrl": "https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4",
"videos": { # 儿子
"hdUrl": "",
"hdflvUrl": "",
"sdUrl": "",
"sdflvUrl": "", # 孙子
"srcUrl": "https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4"
}
}
}
# jsonpath(数据,'语法')

# dic.get("videoInfo").get("videos").get("srcUrl")

# print(jsonpath.jsonpath(dic, '$..srcUrl'))
#
# print(jsonpath.jsonpath(dic, '$..video_image'))

print(jsonpath.jsonpath(dic, '$..videos.srcUrl'))
#['https://video.pearvideo.com/mp4/short/20170714/1681991278593-10632788-hd.mp4']

Xpath

简介

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
相比于BeautifulSoup,Xpath在提取数据时会更有效率。

安装

pip install lxml

语法

XPath 使用路径表达式在 XML/HTML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。

下面列出了最有用的路径表达式:

谓语

谓语用来查找某个或某些特定的节点或者包含某个指定值的节点
谓语被嵌在方括号中。在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果。实例:

选取未知节点

XPath通配符可用来选取未知节点

通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。

选取多个路径

通过在路径表达式中使用”|”运算符,可以选取若干个路径。在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:

获取节点下文本

用text()获取某个节点下的文本,用string()获取某个节点下所有的文本。

解析工具应用案例

1. 明确目标:

以豆瓣电影排行榜为案例,抓取豆瓣电影的排行榜: https://movie.douban.com/chart

需要提取的数据:

1. movie_name	2. movie_actor	3. movie_score

2. 进行伪装与请求:

进行伪装与请求:

  1. requests

3. 交互响应中去提取我们需要的信息:

  1. 正则 2. bs4 3. json() 4. xpath(可平替bs4)

4. 保存结果

  1. 文件

5. 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import requests
from lxml import etree

url = 'https://movie.douban.com/chart'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
}
res = requests.get(url, headers=headers)
with open('douban.html', 'w', encoding='utf8') as f: # encoding='utf8' 避免乱码
f.write(res.text)

# 读取已保存的文件 douban.html
with open('douban.html', 'r', encoding='utf8') as f:
data = f.read()

# 通过etree读取改文件,建立对象page
page = etree.HTML(data)

# 示例操作,一层一层往下探究对象
test = page.xpath('//div[@class="pl2"]/a/text()') # xpath(xpath语法字符串)
print(test)
li = []
for i in test:
movie_name = i.replace(' ', '').replace('\n', '').replace('/', '')
if not movie_name:
continue
li.append(movie_name)

print(li) # 获取处理后的列表,分析拿到片名,但是因为还有其它值,不可能多个循环,所以实际应用不能这么操作

# 实际操作
test2 = page.xpath('//tr[@class="item"]')

movie = {}
for i in yao:
# 电影的名字
movie_name = i.xpath('./td/a[@class="nbg"]/@title')[0]
my_key = md5(movie_name.encode()).hexdigest()
# print(movie_name)
# 电影的演员表
movie_actor = i.xpath('./td/div/p[@class="pl"]/text()')[0]
# print(movie_actor)
# 电影的评分
# movie_score = i.xpath('//span[@class="rating_nums"]/text()')
# movie_score = i.xpath('./td/div/div/span[@class="rating_nums"]/text()')[0]
movie_score = i.xpath('./td//span[@class="rating_nums"]/text()')[0]
# print(movie_score)
movie.update({my_key: [{'片名':movie_name,'演员': movie_actor, '评分': movie_score}]})
print(movie)

6. 租客网代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests
from lxml import etree
import json

url = 'https://cs.zu.anjuke.com/?from=HomePage_TopBar'

res = requests.get(url)

tree = etree.HTML(res.text)

div = tree.xpath('//div[@class="zu-itemmod clearfix"]')


for i in div:
# 标题
title = i.xpath('./div/h3/a/b[@class="strongbox"]/text()')[0]
# 价格
price = i.xpath('./div//strong[@class="price"]/text()')[0]
# 地址
address = i.xpath('./div//address[@class="details-item tag"]/text()')
# ['\n ', '\xa0\xa0\n 芙蓉', '五一广场 ', '韭菜园路101号 ']
"""
1.使用join 把列表转换成字符串了
2.字符串使用replace进行替换
总结: 先拼接再替换
"""
address = ''.join(address).replace('\n', '').replace(' ', '').replace(' ',' ')
print(address)

# 保存进文件中
with open('租房信息.txt','a', encoding='utf8') as f:
f.write("标题:"+ title + "\t" + "价格:" + price + "\t"+ "地址:" + address + '\n')
  • 标题: 页面解析工具
  • 作者: Yiuhang Chan
  • 创建于 : 2021-02-23 14:12:56
  • 更新于 : 2024-02-28 18:50:23
  • 链接: https://www.yiuhangblog.com/2021/02/23/20210223页面解析工具/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论