pyquery和parsel
pyquery 库和 parsel 的使用笔记
说明:示例中的
html变量沿用前文定义;单独运行某段代码时,请先定义对应的html字符串。原笔记中的本地截图路径已省略,发布时可替换为图床图片。
一、pyquery
图示:pyquery 安装/使用示意(原笔记截图,略)
1. 准备工作
在终端中运行:
pip install pyquery
2. 初始化
初始化简单来说就是:创建一个对象,并给它赋予初始状态的过程。
2.1 字符串初始化
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
doc = pq(html)
print(doc('li'))
2.2 URL 初始化
from pyquery import PyQuery as pq
doc = pq(url='https://cuiqingcai.com')
print(doc('title'))
2.3 文件初始化
from pyquery import PyQuery as pq
doc = pq(filename='demo.html')
print(doc('li'))
3. CSS 选择器
html = '''
<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>
'''
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('#container .list li'))
print(type(doc('#container .list li')))
for item in doc('#container .list li').items():
print(item.text())
4. 子节点与父节点
html = '''
<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>
'''
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
print(type(items))
print(items)
lis = items.find('li')
print(type(lis))
print(lis)
说明:
find():查找所有子孙节点。children():只查找直接子节点。parent():查找父节点。parents():查找祖先节点。
5. 兄弟节点
html = '''
<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>
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li.siblings())
说明:siblings() 用于获取兄弟节点,默认不包含当前节点自身。
6. 节点操作
html = '''
<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>
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
li.remove_class("active")
print(li)
li.add_class("active")
print(li)
这里主要使用 remove_class() 和 add_class() 来增删 class 属性。
7. attr、text、html
html = '''
<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>
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
li.attr('name', 'link')
print(li)
li.text('change World')
print(li)
li.html('<span>change World</span>')
print(li)
说明:
attr():修改属性;传一个参数时通常用于获取属性,传两个参数时用于设置属性。text():修改文本内容。html():修改内部 HTML 标签及内容。
8. remove
html = '''
<div class="wrap">
Hello, World
<p>This is a paragraph.</p>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
wrap = doc('.wrap')
print(wrap.text())
wrap.find('p').remove()
print(wrap.text())
上面代码删除了 p 节点,只保留 .wrap 中的其他文本内容。
9. 伪类选择器
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('li:first-child')
print(li)
li = doc('li:last-child')
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 中的概念,用于根据元素的“位置”或“状态”提取元素。上面的示例分别选取:
- 第一个
li; - 最后一个
li; - 索引大于 2 的
li(即第三个之后的节点,注意索引从 0 开始); - 偶数位置的
li; - 文本中包含
second的li。
其中 :gt() 属于 jQuery/pyquery 风格的扩展伪类,不是标准 CSS 选择器。
10. 常见报错:忘记加 .items()
图示:忘记加
.items()时的报错截图(原笔记截图,略)
如果忘记加 .items(),可能会产生类似“字符串不能被调用”的类型错误。正确写法如下:
for item in doc('li').items():
print(item.text())
.items() 的核心作用是:当提取到多个元素后,在 for 循环遍历它们时,把每一个单独的节点重新包装成 PyQuery 对象,从而能够继续使用 PyQuery 的全部方法。
二、parsel
图示:parsel 使用示意(原笔记截图,略)
1. 准备工作
pip install parsel
2. Selector
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>
'''
from parsel import Selector
selector = Selector(text=html)
items = selector.css('.item-0')
print(len(items), type(items), items)
items2 = selector.xpath('//li[contains(@class, "item-0")]')
print(len(items2), type(items2), items2)
说明:contains(@class, "item-0") 表示只要 class 属性中包含 "item-0" 这个字符串,就算匹配成功。需要注意,class 是空格分隔的多值属性,contains() 是子串匹配;如果需要精确匹配某个 class,可使用 CSS 选择器或更严格的 XPath 写法。
3. 提取文本
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>
'''
from parsel import Selector
selector = Selector(text=html)
items = selector.css('.item-0')
for item in items:
text = item.xpath('.//text()').get()
print(text)
说明:.get() 用于获取第一个匹配结果;如果需要获取全部结果,应使用 .getall()。
提取所有文本
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>
'''
from parsel import Selector
selector = Selector(text=html)
result = selector.xpath('//li[contains(@class, "item-0")]//text()').getall()
print(result)
同样,也可以使用 CSS 选择器:
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>
'''
from parsel import Selector
selector = Selector(text=html)
result = selector.css('.item-0 *::text').getall()
print(result)
说明:这里的 * 用于匹配后代元素,::text 用于提取文本节点。如果希望确保提取元素自身的直接文本和所有后代文本,优先使用 XPath 的 .//text() 写法。
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>
'''
from parsel import Selector
doc = Selector(text=html)
result = doc.css('.item-0.active a::attr(href)').get()
print(result)
result = doc.xpath('//li[contains(@class, "item-0") and contains(@class, "active")]/a/@href').get()
print(result)
说明:
- CSS 选择器选取属性需要加
::attr(),并传入对应属性名称。 - XPath 中直接用
/@属性名即可选取属性。 - 最后统一使用
get()提取单个结果,或使用getall()提取全部结果。
5. 正则提取
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>
'''
from parsel import Selector
doc = Selector(text=html)
# 提取所有包含 link 的结果
result = doc.css('.item-0').re('link.*')
print(result)
# 提取所有匹配 .*item 的文本结果
result = doc.css('.item-0 *::text').re('.*item')
print(result)
# re_first 提取第一个匹配结果;小括号表示捕获分组,输出捕获分组中的内容
result = doc.css('.item-0').re_first('<span class="bold">(.*?)</span>')
print(result)
说明:如果在调用 CSS 方法时已经提取了进一步的结果,例如提取了节点文本值,那么 re() 方法只会针对这些结果进行正则提取。re() 返回列表,re_first() 返回第一个匹配结果;若正则中包含捕获分组,返回内容会对应捕获分组。
三、额外补充:正则表达式速查
1. 限定符
| 写法 | 含义 |
|---|---|
a* |
a 出现 0 次或多次 |
a+ |
a 出现 1 次或多次 |
a{6} |
a 出现 6 次 |
a{2,6} |
a 出现 2 到 6 次 |
a{2,} |
a 出现 2 次以上 |
2. 或运算符
| 写法 | 含义 |
|---|---|
| `(a | b)` |
| `(ab) | (cd)` |
3. 字符类
| 写法 | 含义 |
|---|---|
[abc] |
匹配 a 或 b 或 c |
[a-c] |
同上,匹配 a 到 c |
[a-fA-F0-9] |
匹配小写字母、大写字母和数字 |
[^0-9] |
匹配非数字字符 |
4. 元字符
| 写法 | 含义 |
|---|---|
\d |
匹配数字字符 |
\D |
匹配非数字字符 |
\w |
匹配单词字符(英文、数字、下划线) |
\W |
匹配非单词字符 |
\s |
匹配空白符(Tab 键、换行符等) |
\S |
匹配非空白字符 |
. |
匹配任意字符(换行符除外) |
\bword\b |
标定字符边界 |
^ |
匹配行首 |
$ |
匹配行尾 |
5. 贪婪匹配与懒惰匹配
| 写法 | 含义 |
|---|---|
<.+> |
默认贪婪匹配“任意字符” |
<.+?> |
懒惰匹配“任意字符” |
四、简单总结
| 库 | 特点 | 常用方法 |
|---|---|---|
| pyquery | 类似 jQuery 的链式操作,节点查找和修改较方便 | pq()、find()、items()、attr()、text()、html()、add_class()、remove_class() |
| parsel | 常用于 Scrapy,支持 CSS 与 XPath,结果通常为 SelectorList | Selector(text=...)、css()、xpath()、get()、getall()、re()、re_first() |
浙公网安备 33010602011771号