【网络爬虫】python中的数据解析工具(re,bs4,xpath,pyquery)
1.基础知识
1.1 正则(re)
Regular Expression, 正则表达式, ⼀种使⽤表达式的⽅式对字符进⾏匹配的语法规则.
⽹⻚源代码本质上就是⼀个超⻓的字符串, 想从⾥⾯提取内容.⽤正则再合适不过了.
正则的优点: 速度快, 效率⾼, 准确性⾼ 正则的缺点: 新⼿上⼿难度有点⼉⾼.
不过只要掌握了正则编写的逻辑关系, 写出⼀个提取⻚⾯内容的正则其实并不复杂
正则的语法: 使⽤元字符进⾏排列组合⽤来匹配字符串
在线测试正则表达式 /
元字符: 具有固定含义的特殊符号 常⽤元字符:
. 匹配除换⾏符以外的任意字符, 未来在python的re模块中是⼀个坑.
\w 匹配字⺟或数字或下划线
\s 匹配任意的空⽩符
\d 匹配数字
\n 匹配⼀个换⾏符
\t 匹配⼀个制表符
^ 匹配字符串的开始
$ 匹配字符串的结尾
\W 匹配⾮字⺟或数字或下划线
\D 匹配⾮数字
\S 匹配⾮空⽩符
a|b 匹配字符a或字符b
() 匹配括号内的表达式,也表示⼀个组
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中字符的所有字符
量词: 控制前⾯的元字符出现的次数
* 重复零次或更多次
+ 重复⼀次或更多次
? 重复零次或⼀次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
贪婪匹配和惰性匹配
.* 贪婪匹配, 尽可能多的去匹配结果
.*? 惰性匹配, 尽可能少的去匹配结果 -> 回溯
1.2.HTML基础语法
HTML(Hyper Text Markup Language)超⽂本标记语⾔, 是我们编写⽹⻚的最基本也是最核⼼的⼀种语⾔. 其语法规则就是⽤不同的标签对⽹⻚上的内容进⾏标记, 从⽽使⽹⻚显示出不同的展示效果.
<h1>我爱你
</h1>
标签
上述代码的含义是在⻚⾯中显示"我爱你"三个字, 但是我爱你三个字被标记了. ⽩话就是被括起来了. 被H1这个标签括起来了. 这个时候. 浏览器在展示的时候就会让我爱你变粗变⼤. 俗称标题, 所以HTML的语法就是⽤类似这样的标签对⻚⾯内容进⾏标记. 不同的标签表现出来的效果也是不⼀样的.
h1: ⼀级标题
h2: ⼆级标题
p: 段落
font: 字体(被废弃了, 但能⽤)
body: 主体
这⾥只是简单科普⼀下, 其实HTML标签还有很多. 就不⼀⼀列举了.
属性
<h1>我爱你
</h1>
<h1 align='right'>我爱你妹
</h1>
有意思了. 我们发现在标签中还可以给出xxx=xxx这样的东⻄. 那么它⼜是什么呢? ⼜该如何解读呢?
⾸先, 这两个标签都是h1标签, 都是⼀级标题, 但是下⾯这个会显示在右边. 也就是说, 通过xxx=xxx这种形式对h1标签进⼀步的说明了. 那么这种语法在html中被称为标签的属性. 并且属性可以有很多个. 例如:
<body text="green" bgcolor="#eee">你看我的颜⾊. 贼健康
</body>
总结
<标签 属性="值" 属性="值">被标记的内容
</标签>
1.3 CSS基础语法
CSS全称层叠样式表(Cascading Style Sheets), 主要⽤来定义⻚⾯内容展示效果的⼀⻔语⾔.
HTML: ⻚⾯⻣架. 素颜 CSS: ⻚⾯效果美化. 美妆+滤镜
语法规则
- 通过style属性来编写样式
- 通过style标签. 然后使⽤选择器的形式来编写样式
- 在css⽂件中编写样式, 通过link引⼊该⽂件
选择器
1. id选择器 #
2. 标签选择器 标签
3. 类选择器 .
4. 选择器分组 ,
5. 后代选择器 空格
6. ⼦选择器 >
7. 相邻选择器 +
8. 属性选择器 [属性=值]
2.re模块
re模块中只需要记住这么⼏个功能
- findall 查找所有. 返回list
lst = re.findall("m", "mai le fo len, mai ni mei!")
print(lst) # ['m', 'm', 'm']
lst = re.findall(r"\d+", "5点之前. 你要给我5000万")
print(lst) # ['5', '5000']
- search 会进⾏匹配. 但是如果匹配到了第⼀个结果. 就会返回这个结果. 如果匹配不上search返回的则是None
ret = re.search(r'\d', '5点之前. 你要给我5000万').group()
print(ret) # 5
- match 只能从字符串的开头进⾏匹配
ret = re.match('a', 'abc').group()
print(ret) # a
- compile() 可以将⼀个⻓⻓的正则进⾏预加载. ⽅便后⾯的使⽤
obj = repile(r'\d{3}') # 将正则表达式编译成为⼀个 正则表达式对象, 规则要匹配的是3个数字
ret = obj.search('abc123eeee') # 正则表达式对象调⽤search, 参数为待匹配的字符串
print(ret.group()) # 结果: 123
- 正则中的内容如何单独提取?单独获取到正则中的具体内容可以给分组起名字
s = """
<div class='⻄游记'><span id='10010'>中国联通</span></div>
"""
obj = repile(r"<span id='(?P<id>\d+)'>(?P<name>\w+)</span>", re.S)
result = obj.search(s)
print(result.group()) # 结果: <span id='10010'>中国联通</span>
print(result.group("id")) # 结果: 10010 # 获取id组的内容
print(result.group("name")) # 结果: 中国联通 #获取name组的内容# 这⾥可以看到我们可以通过使⽤分组. 来对正则匹配到的内容进⼀步的进⾏筛选.
3.bs4模块安装和使用
模块安装
pip install bs4# 如果安装速度慢可以更换国内源(阿里或者清华)
pip install -i bs4
使用
BeautifulSoup对象获取html中的内容主要通过两个⽅法来完成:
● find()
● find_all()
基本上有这两个⽅法就够⽤了. 其他的可以⾃⾏进⾏英⽂翻译就知道啥意思了.
不论是find还是find_all 参数⼏乎是⼀致的.
语法:
find(标签, 属性=值)
意思是在⻚⾯中查找 xxx标签, 并且标签的xxx属性必须是xxx值
例:
find(‘div’, age=18) 含义: 在⻚⾯中查找div标签, 并且属性age必须是18的这个标签.
find_all()的⽤法和find()⼏乎⼀致. find()查找1个. find_all()查找⻚⾯中所有的.
# 第一种写法
<div class="honor">page.find("div", class="honor")
# 注意, python中class是关键字. 会报错的. 怎么办呢? 可以在class后⾯加个下划线
page.find("div", class_="honor")#我们可以使⽤第⼆种写法来避免这类问题出现page.find("div", attrs={"class": "honor"})
示例
html = """<ul><li><a href="zhangwuji">张⽆忌</a></li><li id="abc"><a href="zhouxingchi">周星驰</a></li><li><a href="zhubajie">猪⼋戒</a></li><li><a href="wuzetian">武则天</a></li></ul>
"""
from bs4 import BeautifulSoup
page = BeautifulSoup(html, "html.parser")
lis = page.find_all("li")
for li in lis:print(li.find("a").get("href"))
4. xpath
XPath是⼀⻔在 XML ⽂档中查找信息的语⾔. XPath可⽤来在 XML ⽂档中对元素和属性进⾏遍历. ⽽我们熟知的HTML恰巧属于XML的⼀个⼦集. 所以完全可以⽤xpath去查找html中的内容.
基础概念
<book><id>1</id><name>野花遍地⾹</name><price>1.23</price><author><nick>周⼤强</nick><nick>周芷若</nick></author>
</book>
在上述html中,
- book, id, name, price…都被称为节点.
- Id, name, price, author被称为book的⼦节点
- book被称为id, name, price, author的⽗节点
- id, name, price,author被称为同胞节点
安装
pip install lxml
用法
- 将要解析的html内容构造出etree对象.
- 使⽤etree对象的xpath()⽅法配合xpath表达式来完成对数据的提取
from lxml import etree
html = """
book/*/nick
<book><id>1</id><name>野花遍地⾹</name><price>1.23</price><nick>臭⾖腐</nick><author><nick id="10086">周⼤强</nick><nick id="10010">周芷若</nick><nick class="joy">周杰伦</nick><nick class="jolin">蔡依林</nick><div><nick>惹了</nick></div></author><partner><nick id="ppc">胖胖陈</nick><nick id="ppbc">胖胖不陈</nick></partner>
</book>
"""et = etree.XML(html)
# 根据节点进⾏搜索
# result = et.xpath("/book")
# result = et.xpath("/book/id") # /在开头表示⽂档最开始, /在中间表示⼉⼦
# result = et.xpath("/book//nick") # //表示后代
result = et.xpath("/book/*/nick") # *表示通配符print(result)
html = """
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><title>Title</title>
</head>
<body><ul><li><a href="">百度</a></li><li><a href="">⾕歌</a></li><li><a href="">搜狗</a></li></ul><ol><li><a href="feiji">⻜机</a></li><li><a href="dapao">⼤炮</a></li><li><a href="huoche">⽕⻋</a></li></ol><div class="job">李嘉诚</div><div class="common">胡辣汤</div>
</body>
</html>
"""# xpath解析
from lxml import etreef = open("1.html", mode="r", encoding='utf-8')
tree = etree.HTML(f.read())
result = tree.xpath("/html/body/ul/li/a/@href")
print(result)
result = tree.xpath("/html/body/ul/li")
for li in result:print(li.xpath("./a/@href")) # 局部解析
result = tree.xpath("//div[@class='job']/text()") # [@class='xxx']属性选取 text()获取⽂本
print(result)
5.pyquery
pyquery是⼀个相对新颖的数据解析⽅式. 有别于xpath和bs4. 它是使⽤css选择器作为语法规则的⼀种解析⽅式, 其⽤法⼏乎和前端的jQuery库⼀致.
安装
pip install pyquery
使用
- 基本使用
# 使⽤pyquery加载html代码. 此时⾃动⽣成PyQuery对象# 导⼊pyquery
from pyquery import PyQuery as pqhtml = '<li><a href="">百度</a></li>' # 加载HTML代码, 此时会⾃动⽣成PyQuery对象
query = pq(html)
print(query) # <li><a href="">百度</a></li>
print(type(query)) # <class'pyquery.pyquery.PyQuery'># 从PyQuery对象中提取内容# 1. 标签选择器
li = query("li")
print(li) # li标签# 2. id选择器
a = query("#god")
print(a)# 3. 类选择器
btn = query(".btn")
print(btn)# 注意, 通过pyquery选择器提取的节点, 依然是pyquery对象. 所以. 就有了这种写法
# 4. 链式提取
btn = query("li")("a") # 先提取li, 然后再提取a.
print(btn)
# 但这种写法个⼈极不推荐. 看着很不舒服. 舒服的写法⼀定是这样的# 5. 后代选择器
btn = query("li a") # 直接使⽤css选择器搞就完了.
print(btn)# 提取⼀些重要属性试试看# 6. 属性提取
btn = query("a")
print(btn.attr("href")) # 提取href属性. attr()的作⽤就提取属性
print(btn.text()) # 提取a标签的⽂本信息
print(btn.html()) # 提取a标签中的html信息
- 提取批量的数据
query = PyQuery(html)# 插播⼀条根据属性提取数据
# feiji = query("a[@href='feiji']")
# print(feiji)# 拿到所有的a标签
lis = query("li a")
a_list = lis.items() # 批量数据处理必须加上items()
for a in a_list:print(a.text())
- pyquery还可以对HTML结构进⾏修改
from pyquery import PyQuery as pq
html = """<HTML><div class="aaa">哒哒哒</div><div class="bbb">嘟嘟嘟</div></HTML>
"""
doc = pq(html)
doc(".aaa").after("""<div class="ccc">妈呀</div>""") # 插⼊HTML代码⽚段
doc(".bbb").append("<span>我爱span</span>") # 向HTML内层标签中插⼊HTML⽚段
doc(".aaa").html("<span>我是span</span>") # 修改标签内的html代码
doc('c').text("美滋滋") # 修改⽂本内容doc("c").attr("cs", "测试") # 添加属性
doc("c").remove_attr("cs") # 删除属性print(doc)
文档
.html