HTML解析技术之pyquery
一、安装
pip install pyquery
二、简介
pyquery是Python仿照jQuery严格实现的HTML解析库。它可以让我们直接解析DOM节点的结构,并通过DOM节点的一些属性(如:id,class等)对内容进行快速提取。
三、开始使用
1. 初始化
在使用pyquery库中的方法对HTML进行解析之前,我们需要先将网页进行初始化,使其变成一个PyQuery对象,才能使用该对象对应的方法。
(1). 字符串初始化
直接把字符串类型的HTML内容作为参数去传递来初始化成PyQuery对象。
源HTML字符串:
html = """
<div>
<ul>
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
"""
初始化:
from pyquery import PyQuery as pq
# 初始化html字符串为PyQuery对象
doc = pq(html)
操作对象:
print(type(doc))
print(doc('li'))
结果:
pyquery.pyquery.PyQuery
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
(2). URL初始化
直接把URL作为参数去传递来初始化成PyQuery对象。
源URL:
url = "https://www.baidu.com"
初始化:
from pyquery import PyQuery as pq
doc = pq(url=url)
操作对象:
print(doc('title'))
# 结果,乱码
<title>ç™¾åº¦ä¸€ä¸‹ï¼Œä½ å°±çŸ¥é“</title>
PyQuery首先请求url,用得到的HTML内容完成初始化。相当于将网页的源代码以字符串的形式传递给PyQuery类来进行初始化。
那么,可以改进一下:
import requests
# 将内容变成二进制结果传入PyQuery
doc = pq(requests.get(url=url).content)
print(doc('title'))
# 结果
<title>百度一下,你就知道</title>
(3). 文件(路径+名)初始化
直接把URL作为参数去传递来初始化成PyQuery对象。
from pyquery import PyQuery as pq
doc = pq(filename="./test.html")
2. CSS选择器操作
利用CSS选择器进行:查找节点、遍历节点、获取信息等操作
格式 | 例子 | 含义 |
---|---|---|
.class | .list | 选择class='list’的所有元素 |
#id | #container | 选择id='container’的所有元素 |
* | * | 选择所有元素 |
element | li | 选择所有的li元素 |
element,element | div,li | 选择所有div元素和所有li元素 |
element element | div li | 选择所有div标签下的所有li元素 |
[attribute] | [color] | 选择带有color属性的所有元素 |
[attribute=value] | [color=‘red’] | 选择color='red’的所有元素 |
element.class | div.list | 选择类为list的所有div标签的元素 |
后续使用的HTML
html = """
<div class="wrap">
<div id="container">
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p><li>sixth item</li></p>
</ul>
</div>
</div>
"""
(1). 查找节点
子孙节点和子节点
子孙节点的查找需要使用find()
方法,而子节点的查找使用children()
方法
先通过CSS选择器,选取一个节点:
doc = pq(html)
class_list = doc(" .list")
print(class_list)
# 打印
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p><li>sixth item</li></p>
</ul>
在该节点下选择子孙节点:
# 使用find方法
tag_li = class_list.find('li')
print(tag_li)
# 打印
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<li>test</li>
# <li>test</li>是孙节点
在该节点下选择子节点:
# 使用children方法
tag_li = class_list.children('li')
print(tag_li)
# 打印
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
祖先节点和父节点
祖先节点的查找需要使用parents()
方法,父节点的查找需要使用parent()
方法
# 父节点
tag_parent = class_list.parent()
print(tag_parent)
# 打印(父节点)
<div id="container">
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p class="active"><li>sixth item</li></p>
</ul>
</div>
# 祖先节点
tag_parents = p_list.parents()
print(tag_parents)
# 打印(父节点的父节节点+父节点)
<div class="wrap">
<div id="container">
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p class="active"><li>sixth item</li></p>
</ul>
</div>
</div>
<div id="container">
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p class="active"><li>sixth item</li></p>
</ul>
</div>
根据class选择祖先:
tag_parents = p_list.parents('.wrap')
print(tag_parents)
# 打印(类为wrap的节点)
<div class="wrap">
<div id="container">
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p class="active"><li>sixth item</li></p>
</ul>
</div>
</div>
兄弟节点
兄弟节点的查找需要使用siblings()
方法
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
# <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
print(li.siblings)
# 结果
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0">first item</li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p class="active"><li>sixth item</li></p>
(2). 遍历节点
从前面节点的选取可知,节点既可以是单个也可以是多个,并且都是PyQuery类型,而不是列表或其他Python中的容器类型。对于结果,可以转换成str类型,也可以保留PyQuery类型。
from pyquery import PyQuery as pq
doc = pq(html)
## 单个结果
result1 = doc('.item-0.active')
# 保留
type(result1)
print(result1)
# pyquery.pyquery.PyQuery
# <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
# 转换str
type(str(result2))
print(str(result2))
# str
# <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
## 多个结果遍历
result2 = doc('li').items()
print(type(result2))
for li in result2:
print(li, type(li))
# 结果
<class 'generator'>
<li class="item-0">first item</li>
<class 'pyquery.pyquery.PyQuery'>
<li class="item-1"><a href="link2.html">second item</a></li>
<class 'pyquery.pyquery.PyQuery'>
<li class="item-0 active">
<a href="link3.html">
<span class="bold">third item</span>
</a>
</li>
<class 'pyquery.pyquery.PyQuery'>
<li class="item-1 active">
<a href="link4.html">fourth item</a>
</li>
<class 'pyquery.pyquery.PyQuery'>
<li class="item-0">
<a href="link5.html">fifth item</a>
</li>
<class 'pyquery.pyquery.PyQuery'>
<li>sixth item</li>
<class 'pyquery.pyquery.PyQuery'>
调用items()方法后,会转化PyQuery类型,得到一个生成器类型,遍历生成器,得到的每一个结果,还是PyQuery类型。
(3). 获取信息
提取到节点后,进一步提取节点中的信息。信息主要分成两种:属性信息和文本信息
获取属性信息
属性信息的获取使用attr('属性')
方法,或者attr.属性
方法
单个元素的属性信息获取:
from pyquery import PyQuery as pq
doc = pq(html)
a = doc(".item-0.active a")
print(a.attr('href'))
print(a.attr.href)
# link3.html
# link3.html
多个元素的属性信息获取:
from pyquery import PyQuery as pq
doc = pq(html)
a = doc("a")
print(a.attr('href'))
print(a.attr.href)
# 结果,4个a元素,但是只有一个结果
<a href="link2.html">second item</a>
<a href="link3.html"><span class="bold">third item</span></a>
<a href="link4.html">fourth item</a>
<a href="link5.html">fifth item</a>
link2.html
link2.html
整体取属性时,只会取第一个元素的属性信息。
所以需要进行遍历:
a = doc("a")
for item in a.items():
print(item.attr('href'))
# 结果
link2.html
link3.html
link4.html
link5.html
获取文本信息
纯文本信息的获取使用text()
,带标签的文本信息获取html()
单个元素的文本信息获取:
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('.item-0.active a')
print(a)
print(a.text())
print(a.html())
# <a href="link3.html"><span class="bold">third item</span></a>
# third item
# <span class="bold">third item</span>
text()
会忽略节点内部包含的HTML标签,只返回纯文本内容。
html()
会返回内部HTML标签+文本内容
多个元素的文本信息获取:
对于多个元素的属性信息获取,需要遍历,但是文本信息获取是否需要呢?
a = doc('li')
print(a)
print(type(a.text()))
print(a.text())
print(type(a.html()))
print(a.html())
# 结果
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<li>sixth item</li>
<class 'str'>
first item second item third item fourth item fifth item sixth item
<class 'str'>
first item
获取节点内部所有标签的文本,使用text,无需遍历。而获取节点内部的标签+文本,使用html,需要进行遍历,否则还是打印第一个元素的标签+文本。
(4). 节点的操作
html = '''
<div class="wrap">
<div id="container">
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
</div>
'''
类属性的增加与删除
标签的类属性的增加addclass
方法,删除removeclass
方法
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
li.removeClass('active')
print(li)
li.addClass('action')
print(li)
# 结果
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-0"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-0 action"><a href="link3.html"><span class="bold">third item</span></a></li>
标签的属性,纯文本和HTML内容的更改
标签的属性更改使用attr()
方法,若传入一个参数,则是获取该属性;若传入两个参数,则是将第一个参数代表的属性更改为第二个参数值。
纯文本更改使用text()
方法,不传入参数,则为获取值;传入一个参数,则为更改值
HTML内容更改使用html()
方法,不传入参数,则为获取值;传入一个参数,则为更改值
li = doc('.item-0.active')
print(li)
# li节点增加了新的属性name
li.attr("name","lyh")
print(li)
# li节点内部全部被改成文本
li.text("play together!")
print(li)
# li节点内部全部被改成标签
li.html("<p>extra tag</p>")
print(li)
# 结果
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-0 active" name="lyh"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-0 active" name="lyh">play together!</li>
<li class="item-0 active" name="lyh"><p>extra tag</p></li>
节点的删除
remove()
删除节点
提取p标签下的内容:sixth item
text_all = doc('.list')
print(text_all)
print(text_all.text())
"""
<ul class="list">
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<p class="active"><li>sixth item</li></p>
</ul>
first item second item third item fourth item fifth item sixth item
"""
删除li节点:
li = li_p.children('li')
print(li)
li.remove()
"""
<li class="item-0">first item</li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
[<li.item-0>, <li.item-1>, <li.item-0.active>, <li.item-1.active>, <li.item-0>]
"""
打印p标签下的文本:
print(li_p)
print(li_p.text())
"""
<ul class="list">
<p class="active"><li>sixth item</li></p>
</ul>
sixth item
"""
还有许多其他方法,如append、empty 和 prepend ,可参考官方文档:
其他方法(官网)
3. 伪类选择器
CSS选择器很强大,它支持多种多样的伪类选择器,可指定选择节点的位置,如:第一个、最后一个、偶数个、奇数个、含有某一文本的节点等。
doc = pq(html)
li = doc('li:first-child')
print(li)
li = doc('li:last-child')
print(li)
li = doc('li:nth-child(2)')
print(li)
li = doc('li:gt(2)')
print(li)
li = doc('li:nth-child(2n)')
print(li)
li = doc('li:contains(second)')
print(li)
以上使用了CSS伪类选择器,具体可参考文档:
CSS文档