安装: pip install pyquery
字符串初始化
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'))
<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>
这里首先引入 PyQuery 这个对象, 取别名为 pq 。 然后声明一个长 HTML 字符串,并将其当作参数传给 PyQuery 类, 这样就成功的完成了初始化。 接着将初始化的对象传给 CSS 选择器。 在这个 例子中 我们传入了 li 这样就选择了所有 li 节点。
URL 初始化
初始化的参数除了能以字符串形式传递, 还能以网页的URL , 此时只需要指定 PyQuery 对象的参数为 url 即可
from pyquery import PyQuery as pq
doc = pq(url='https://cuiqingcai.com')
print(doc('title'))
<title>静觅丨崔庆才的个人站点 - Python爬虫教程</title>
这样的话, PyQuery 对象会首先请求这个URL , 然后用的到的HTML 内容完成初始化, 其实就相当于把网页的源代码以字符串形式传递给PyQuery 类, 来完成初始化操作
类似于这段代码
from pyquery import PyQuery as pq
import requests
doc = pq(requests.get('https://cuiqingcai.com').text)
print(doc('li'))
文件初始化
除了上面两种还可以传递本地文件名, 此时将参数指定为 filename 即可
from pyquery import PyQuery as pq
doc = pq(filename='demo.htm'))
print(doc('li'))
当然, 这里你需要有一个 本地的HTML 文件, 其内容时待解析 HTML 字符串。
基本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')))
<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> <class 'pyquery.pyquery.PyQuery'>
这里我们初始化 PyQuery 对象之后, 传入了一个CSS 选择器 #container .list li, 它的意思时先选取 id 为 container 的节点, 在选取其内部 class 为 list 的节点内部的所有 li 系欸但, 然后打印输出。从运行结果可以看到, 我们成功获取了符合条件的节点
最后我们将符合条件的节点的类型打印输出, 可以看到依然时 PyQuery 类型
下面我们遍历获取的节点, 然后调用 tex 方法, 就可以直接获取节点的文本内容
for item in doc('#container .list li').items():
print(item.text())
for item in doc('#container .list li').items():
print(item.text())
first item second item third item fourth item fifth item
查找节点
子节点
查找子节点时, 需要用到 find 方法, 其参数是CSS 选择器, 我们这里还是以上面的HTML为例
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)
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)
<class 'pyquery.pyquery.PyQuery'> <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> <class '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>
这里我们通过 .list 来选取 class 为 list 的节点, 然后调用 find 方法, 并给其传入 CSS 选择器, 选取内部的 li 节点, 最后打印输出。 可以发现, find 将所有符合条件的节点选择出来, 结果为 PyQuery 类型
其实 find 方法是查找范围内所有符合条件的子孙节点, 如果只查找子节点可以用 children 方法:
lis = items.children()
print(lis)
<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>
如果要筛选符合条件的子节点,可以传入相应的参数即可
lis = items.children('.active')
print(lis)
<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>
父节点
可以用 parent 方法获取某个节点的父节点
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 pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
container = items.parent()
print(type(container))
print(container)
<class 'pyquery.pyquery.PyQuery'> <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>
这里我们首先用 .list 选取 class 为 list 的节点, 然后调用parent 方法得到其父节点, 其类型依然是 PyQuery
这里的父节点是直接父节点,也就是说 parent 方法不会继续查找父节点的父节点, 即祖先节点。如果想要获取某个祖先节点 可以用 parents 方法
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
parents = items.parents()
print(type(parents))
print(parents)
<class 'pyquery.pyquery.PyQuery'> <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><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>
可以看到输出结果由两个: 一个是 class 为 wrap 的节点, 一个是 id 为 container 的你节点,也就是说, parents 方法会返回所有祖先节点。
如果想要某个祖先节点, 可以向parents 方法传入CSS 选择器, 这样就会返回祖先节点中某个符合调节的节点
parents = items.parents('.wrap')
print(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> </ul> </div> </div>
可以看到结果只保留了 一个 class 为 wrap 的节点
兄弟节点
siblings 方法, 以上面HTML文本为例
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
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>
这里首先选择 class 为 list 的节点内部的 class 为 item-0 和 active 的节点, 也就是第三个 li 节点,那么很明显,其兄弟节点有 4 个, 分别是 第 一, 二, 四, 五个节点
如果要筛选某个节点,依然可以传入CSS选择器
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li.siblings('.active'))
<li class="item-1 active"><a href="link4.html">fourth item</a></li>
遍历节点
可以看到 pyquery 库选择结果可能是单个节点,也可能是多个节点。
如果是单个节点,直接输出即可
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
print(str(li))
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
如果是多个节点,就需要遍历
from pyquery import PyQuery as pq
doc = pq(html)
lis = doc('li').items()
print(type(lis))
for li in lis:
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'>
可以发现, 调用 items 方法后, 会得到一个生成器, 对其进行遍历, 就可以逐个得到 li 节点对象了, 它的类型也是 PyQuery
获取属性
获取到某个节点 PyQuery 类型的节点后, 可以调用 attr 方法获取其属性
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 pyquery import PyQuery as pq
doc = pq(html)
a = doc('.item-0.active a')
print(a, type(a))
print(a.attr('href'))
<a href="link3.html"><span class="bold">third item</span></a> <class 'pyquery.pyquery.PyQuery'> link3.html
这里首先选中 class 为 item-0 和 active 的 li 节点内的 a 节点, 其类型是 PyQuery
然后调用 attr 方法, 在这个方法中传入属性名称, 就可得到相应的属性值了
此外也可以调用 attr 属性来获取属性值
print(a.attr.href)
link3.html
如果得到的是多个节点,然后调用 attr 方法
a = doc('a')
print(a, type(a))
print(a.attr('href'))
print(a.attr.href)
<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> <class 'pyquery.pyquery.PyQuery'> link2.html link2.html
可以看出,我们得到了 4 个 a 节点, 调用 attr 方法之后,只得到了第一个节点的属性
如果想要得到所有 a 节点的属性,就需要遍历
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('a')
for item in a.items():
print(item.attr('href'))
link2.html link3.html link4.html link5.html
可以看到,这里获取到了 4 个 a 节点里面的 href 属性值
获取文本
获取文本方法: text()
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 pyquery import PyQuery as pq
doc = pq(html)
a = doc('.item-0.active')
print(a)
print(a.text())
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> third item
这里首先选中 a 节点, 然后调用 text 方法, 就可获取其内部的文本信息。 此时 text 方法会忽略节点内部包含的所有 HTML , 只返回纯文字内容
想要获取节点内部的 HTML 文本, 需要调用 html 方法
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
print(li.html())
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <a href="link3.html"><span class="bold">third item</span></a>
如果选中的是多个节点, text 和 html 方法返回的内容
html = '''
<div class="wrap">
<div id="container">
<ul class="list">
<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 pyquery import PyQuery as pq
doc = pq(html)
li = doc('li')
print(li.html())
print(li.text())
print(type(li.text()))
<a href="link2.html">second item</a> second item third item fourth item fifth item <class 'str'>
结果有点不一样, html 方法返回的是第一个 li 节点内部的 HTML 文本, 而 text 返回了所有的 li 节点内部的纯文本, 各节点内容中间用一个空格分割开,即返回的结果是一个字符串
所有这个地方值得注意, 如果得到的结果是多个节点,并且向获取所有节点的内部 HTML 文本,就需要遍历这些节点
节点操作
pyquery 库提供了一系列方法对节点进行动态修改, 例如为某个节点添加一个 class , 移除某个节点等, 节点的操作方法有很多,这里举例几个
addClass 和 removeClass
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 pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
li.removeClass('active')
print(li)
li.addClass('active')
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 active"><a href="link3.html"><span class="bold">third item</span></a></li>
这里输出了三次: 第一次是原来的 li 节点, 带有 active 这个class , 第二次我们移除了 active
第三次 我们添加了 active
attr text 和 html
html = '''
<ul class="list">
<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
</ul>
'''
from pyquery import PyQuery as pq
doc =pq(html)
li = doc('.item-0.active')
print(li)
li.attr('name', 'link')
print(li)
li.text('changed item')
print(li)
li.html('<span>changed</span>')
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="link"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-0 active" name="link">changed item</li> <li class="item-0 active" name="link"><span>changed</span></li>
这里我们首先选中 li 节点, 然后调用 attr 方法修改其属性, 该方法的第一个参数我属性名, 第二个参数为属性值, 接着,调用 text 和 html 方法改变 li 节点内部的内容。
可以发现调用 attr 方法后, li 节点多了一个 name 属性, 其值为 link 。接着调用 text 方法,传入文本后,li 内部的文本全部被改为传入的字符串文本了, 最后调用 html 方法, li 内部又变成了传入的 HTML 文本
注意点 : 这里的 text 和 html 方法 添加文本,都是替换原来的内容,不是追加
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())
Hello, World This is a paragraph. Hello, World
这里我们想要获取到 wrap 中的 Hello World 文本, 而通过 text() 方法则获取所有 wrap 的内容,包括 p 节点中的, 这里可以从获取到的字符串中进行截取, 当然也可以使用 remove 方法,直接选中 p 节点,然后移除,这样我们获取到的文本就只有 Hello World 了
伪类选择器
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 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)
<li class="item-0">first item</li> <li class="item-0"><a href="link5.html">fifth item</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 class="item-1"><a href="link2.html">second item</a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-1"><a href="link2.html">second item</a></li>
这里我们使用了伪类选择器,依次选择了文本中的第一个 li 节点, 最后一个 li 节点, 第二个 li 节点, 第三个 li 节点之后的 li 节点, 偶数位置的 li 节点, 包含 second 文本的 li 节点