Python爬虫第13节-解析库pyquery 的使用

目录

前言

[一、pyquery 初始化](#一、pyquery 初始化)

[1.1 字符串初始化](#1.1 字符串初始化)

[1.2 URL 初始化](#1.2 URL 初始化)

[1.3 文件初始化](#1.3 文件初始化)

[二、基本 CSS 选择器](#二、基本 CSS 选择器)

[三、pyquery 查找节点](#三、pyquery 查找节点)

[3.1 子节点](#3.1 子节点)

[3.2 父节点](#3.2 父节点)

[3.3 兄弟节点](#3.3 兄弟节点)

四、遍历

五、获取信息

[5.1 获取属性](#5.1 获取属性)

[5.2 获取文本](#5.2 获取文本)

六、节点操作

[6.1 addClass 和 removeClass](#6.1 addClass 和 removeClass)

[6.2 attr、text、html](#6.2 attr、text、html)

[6.3 remove](#6.3 remove)

七、伪类选择器


前言

上两节我们讲了Beautiful Soup这个网页解析库,它确实很厉害。不过,大家用它的一些方法时,会不会感觉不太顺手?还有它的CSS选择器,用起来是不是觉得功能没那么强呢?

要是你接触过Web开发,平时习惯用CSS选择器,或者对jQuery有一定了解,那我得给你介绍一个更称手的解析库,它就是pyquery。 下面,咱们就一起来见识下pyquery有多厉害。

一、pyquery 初始化

在正式开始前,得先确认你已经把pyquery正确安装好了。要是还没安装,就得自己动手通过pip或pip3安装一下。 和Beautiful Soup类似,初始化pyquery时,同样要传入HTML文本,以此来创建一个PyQuery对象。它有好几种初始化的办法,既可以直接传入字符串,也能传入URL,还能传入文件名等等。接下来,我们就详细讲讲这些方法。

1.1 字符串初始化

咱们先通过一个实际例子来体验体验:

python 复制代码
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)
for item in doc('li').items():
    print(item.outer_html())

运行结果如下:

咱们先引入PyQuery对象,给它取个pq的别名。接着弄了个很长的HTML字符串,把这个字符串作为参数,传给PyQuery类,这样就完成了初始化。初始化完之后,再把得到的对象放到CSS选择器里。在这个例子里,我们输入li节点,这么一来,就能选中所有的li节点了 。

1.2 URL 初始化

初始化时,参数不只能用字符串形式来传递。要是你想传入网页的URL,也完全没问题,只要把参数指定为url就行 :

python 复制代码
from pyquery import PyQuery as pq
doc = pq(url='https://linshantang.blog.csdn.net/')
print(doc('title'))

运行结果:

<title>攻城狮7号-CSDN博客</title>

这么操作后,PyQuery对象会先对这个URL发起请求。等拿到网页的HTML内容,就用这些内容完成初始化。这和直接把网页的源代码,以字符串形式传给PyQuery类来初始化,效果是一样的 。

它与下面的代码功能是相同的:

python 复制代码
from pyquery import PyQuery as pq
import requests
doc = pq(requests.get('https://linshantang.blog.csdn.net/').text)
print(doc('title'))

1.3 文件初始化

当然啦,初始化的时候,除了能传一个URL,要是你想传本地的文件名也是可以的,只要把参数指定成filename就行:

python 复制代码
from pyquery import PyQuery as pq
doc = pq(filename='demo.html')
print(doc('li'))

当然,得先有个本地的HTML文件,叫demo.html,里面的内容就是要解析的HTML字符串。这样一来,它会先读取这个本地文件里的内容,接着把文件内容当成字符串,传给PyQuery类进行初始化。

上面这3种初始化方法都能用,不过在实际使用中,最常用的初始化方式还是用字符串来传递 。

二、基本 CSS 选择器

咱们先通过一个实际例子,来体验体验pyquery里CSS选择器该怎么用:

python 复制代码
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')))

运行结果:

这里呢,我们先初始化了PyQuery对象,接着输入了一个CSS选择器#container .list li 。这个选择器的意思是,先找到id是container的节点,再从这个节点里面,找到class是list的节点,最后把这个list节点里面所有的li节点选出来。然后我们把选出来的结果打印出来,能看到,确实成功找到了符合条件的节点。 最后呢,我们把选出来结果的类型也打印出来。可以看到,它还是PyQuery类型 。

三、pyquery 查找节点

接下来给大家讲讲一些常用的查询方法,这些方法的使用方式跟jQuery里的方法一模一样 。

3.1 子节点

要是想查找子节点,就得用find方法,这个方法的参数是CSS选择器。咱们还是拿上面那个HTML做例子来说:

python 复制代码
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是list的节点,接着调用find()方法,把CSS选择器作为参数传进去,这样就选中了这个节点里面的li节点,最后把结果打印出来。可以看到,find()方法会把所有符合条件的节点都选出来,选出来的结果是PyQuery类型。 实际上,find方法会在节点的所有子孙节点里查找。要是我们只想找子节点,那就可以用children方法。

python 复制代码
lis = items.children()
print(type(lis))
print(lis)

运行结果如下:

要是想从所有子节点里挑出符合条件的节点,就拿筛选出子节点里class是active的节点来说,可以给children()方法传入CSS选择器.active。

python 复制代码
lis = items.children('.active')
print(lis)

运行结果:

从输出结果能明显看出,已经筛选过了,只剩下class是active的节点 。

3.2 父节点

我们可以用parent方法获取某个节点的父节点,下面通过一个例子来看看效果:

python 复制代码
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)

运行结果如下:

这里我们先用.list选中class是list的节点,接着调用parent方法,就能得到这个节点的父节点,这个父节点也是PyQuery类型。 这里得到的父节点是直接的父节点,不会再去查父节点的父节点,也就是不会找祖先节点。 要是想获取某个祖先节点,该咋整呢?这时候就可以用parents方法。

python 复制代码
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
parents = items.parents()
print(type(parents))
print(parents)

运行结果:

能看到,输出结果有俩:一个是class为wrap的节点,另一个是id为container的节点。这说明,parents()方法会把所有的祖先节点都返回。 要是想筛选出某个祖先节点,就可以给parents方法传入CSS选择器,这样就能得到祖先节点里符合这个CSS选择器的节点。

python 复制代码
parent = items.parents('.wrap')
print(parent)

运行结果:

从输出结果能明显看出来,少了一个节点,现在只剩下class是wrap的那个节点了。

3.3 兄弟节点

上面我们讲了子节点和父节点的用法,还有一种节点叫兄弟节点。要是想获取兄弟节点,可以用siblings()方法。咱们还是接着用上面的HTML代码来说明:

python 复制代码
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li.siblings())

这里先选中class是list的节点里面,class分别为item - 0和active的节点,也就是第三个li节点。显然,它有4个兄弟节点,分别是第一个、第二个、第四个和第五个li节点。

运行结果:

能看到,这就是我们刚才说的那4个兄弟节点。 要是想筛选出某个兄弟节点,还是可以给siblings方法传入CSS选择器,这样就能从所有兄弟节点里挑出符合条件的节点。

python 复制代码
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li.siblings('.active'))

这里我们筛选了 class 为 active 的节点,通过刚才的结果可以观察到,class 为 active 的兄弟节点只有第四个 li 节点,所以结果应该是一个。我们再看一下运行结果:

<li class="item-1 active"><a href="link4.html">fourth item</a></li>

四、遍历

刚才能看到,pyquery选择出来的结果可能是多个节点,也可能是单个节点,但类型都是PyQuery类型,不会像Beautiful Soup那样返回列表。 要是选出来的是单个节点,既可以直接打印,也能直接转成字符串。

python 复制代码
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
print(str(li))

运行结果:

要是选择结果有多个节点,就得通过遍历来获取每个节点。就像这里,要遍历每个li节点的话,就得调用items方法。

python 复制代码
from pyquery import PyQuery as pq
doc = pq(html)
lis = doc('li').items()
print(type(lis))
for li in lis:
    print(li, type(li))

运行结果如下:

能发现,调用items()方法后会得到一个生成器,对这个生成器进行遍历,就能逐个拿到li节点对象,这些对象也是PyQuery类型。每个li节点都能调用前面提到的方法来做选择操作,像接着查找子节点、找某个祖先节点之类的,用起来很灵活。

五、获取信息

把节点提取出来以后,我们的最终目标肯定是要提取出节点里包含的信息。其中比较关键的信息有两种,一种是获取节点的属性,另一种是获取节点的文本内容。下面我就分别给大家讲一讲。

5.1 获取属性

当提取到一个PyQuery类型的节点后,就能调用attr()方法来获取这个节点的属性了。

python 复制代码
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属性来获取属性,具体用法如下:

python 复制代码
print(a.attr.href)

这两种方法得到的结果是完全相同的。 要是选中了多个元素,再去调用attr方法,会得到什么样的结果呢?下面我们通过实际例子来测试看看。

python 复制代码
a = doc('a')
print(a, type(a))
print(a.attr('href'))
print(a.attr.href)

运行结果如下:

按道理,我们选中的a节点应该有4个,打印结果也该是4个。但调用attr方法时,返回的却只有第一个节点的属性。这是因为,当返回结果包含多个节点时,调用attr方法只能得到第一个节点的属性。 要是遇到这种情况,想获取所有a节点的属性,就得用前面说过的遍历方法了。

python 复制代码
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('a')
for item in a.items():
    print(item.attr('href'))

运行结果:

所以,在获取属性的时候,要先看返回的节点是一个还是多个。要是返回多个节点,就得通过遍历才能逐个获取每个节点的属性。

5.2 获取文本

提取到节点后,另一个重要操作就是获取其内部的文本内容,这时调用text方法就能达成这一目的。

python 复制代码
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)
print(a.text())

运行结果:

这里先选中一个a节点,接着调用text方法,就能获取该节点内部的文本信息。这时它会把节点内部的所有HTML内容忽略掉,只返回纯文本。 不过要是想获取这个节点内部的HTML文本,那就得使用html方法了。

python 复制代码
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
print(li.html())

这里我们选定了第三个li节点,随后调用了html()方法。该方法返回的结果会是这个li节点内包含的所有HTML文本内容。

运行结果:

这里存在一个疑问,如果我们选中的结果包含多个节点,那么调用 text() 或 html() 方法会返回什么样的内容呢?下面我们通过实际例子来探究一下。

python 复制代码
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()))

运行结果如下:

结果可能有点让人意外,html方法返回的是第一个li节点内部的HTML文本,而text方法返回的是所有li节点内部的纯文本,各文本间用一个空格分隔,也就是返回一个字符串。 所以这里要特别留意,如果得到的结果是多个节点,还想获取每个节点内部的HTML文本,那就需要对每个节点进行遍历。而text()方法不用遍历就能获取文本,它会把所有节点的文本提取出来并合并成一个字符串。

六、节点操作

pyquery提供了一系列可对节点进行动态修改的方法,像给某个节点添加一个class,或者移除某个节点等。这些操作有时能为信息提取带来极大便利。 鉴于节点操作的方法众多,下面我会列举几个典型例子来说明其用法。

6.1 addClass 和 removeClass

那咱们先通过一个具体的实例来体验一番:

python 复制代码
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节点,接着调用removeClass()方法,把li节点上的active这个class给去掉了,之后又调用了addClass()方法,再把这个class添加回来。每次执行完一次操作,就把当前li节点的内容打印输出。 运行后得到的结果如下:

从结果能看到,总共输出了3次。在第二次输出的时候,li节点的active这个class已经被移除掉了,而到第三次输出时,这个class又被重新添加回来了。 由此可见,addClass和removeClass这两个方法是能够对节点的class属性进行动态修改的。

6.2 attr、text、html

当然,除了对 class 属性进行操作外,还能使用 attr 方法操作其他属性。另外,也可借助 text 和 html 方法来改变节点内部的内容。下面是相关示例:

python 复制代码
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 item</span>')
print(li)

这里我们先选中了li节点,之后调用attr方法修改属性。attr方法的第一个参数是属性名,第二个参数是属性值。接着,我们又调用text和html方法来改变节点内部的内容。每次操作完成后,都会打印输出当前的li节点。 下面是运行结果:

可以看出,调用attr方法后,li节点新增了一个原本不存在的属性"name",其值为"link"。随后调用text方法并传入文本,li节点内部的文本就都变成了传入的字符串文本。最后,调用html方法并传入HTML文本,li节点内部又变成了传入的HTML文本。 由此可知,attr方法若只传入第一个参数即属性名,是用于获取该属性值;若传入第二个参数,则可用来修改属性值。text和html方法若不传入参数,分别是获取节点内的纯文本和HTML文本;若传入参数,则是进行赋值操作。

6.3 remove

从名字就能知道,remove 方法的作用是移除节点,在某些情况下,它能极大地便利信息提取。下面给出一段 HTML 文本,咱们接着分析它的应用。

python 复制代码
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())

现在你想提取"Hello, World"这个字符串,同时排除 p 节点内部的字符串,该怎么做呢? 这里直接先试着提取 class 为 wrap 的节点的内容,看看是否是我们想要的结果。下面是运行结果:

从这个结果能看出,它还包含了内部 p 节点的内容,也就是说 text 方法把所有纯文本都提取出来了。若想去掉 p 节点内部的文本,一种做法是先提取 p 节点内的文本,再从整个结果里移除这个子串,但这种做法显然比较繁琐。 这时就可以发挥 remove 方法的作用了,我们可以接着这样操作:

python 复制代码
wrap.find('p').remove()
print(wrap.text())

首先我们选中 p 节点,接着调用 remove() 方法把它移除掉。此时,wrap 节点内部就只剩下"Hello, World"这句话了,之后利用 text() 方法就能把它提取出来。 此外,实际上还有不少节点操作的方法,像 append()、empty() 和 prepend() 等,这些方法的用法和 jQuery 完全相同。若想了解详细用法,可以参考官方文档:[http://pyquery.readthedocs.io/en/latest/api.html\](http://pyquery.readthedocs.io/en/latest/api.html)

七、伪类选择器

CSS 选择器如此强大,一个重要原因是它支持丰富多样的伪类选择器。这些伪类选择器能实现很多特殊的选择功能,比如选择第一个节点、最后一个节点、奇偶数节点,以及包含特定文本的节点等。下面通过示例来具体说明:

python 复制代码
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: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)

在这个示例里,我们运用了 CSS3 的伪类选择器,分别选中了第一个 li 节点、最后一个 li 节点、第二个 li 节点、第三个 li 节点之后的所有 li 节点、偶数位置的 li 节点,以及包含"second"文本的 li 节点。 若你想了解 CSS 选择器更多的用法,可以参考 [http://www.w3school.com.cn/css/index.asp\](http://www.w3school.com.cn/css/index.asp)。

至此,pyquery 的常用用法就介绍完毕了。要是你还想了解更多内容,可查阅 pyquery 的官方文档:[http://pyquery.readthedocs.io](http://pyquery.readthedocs.io)。我们相信,有了 pyquery 的助力,网页解析将不再困难。

学习参考书籍:Python 3网络爬虫开发实战

相关推荐
小学生搞程序3 分钟前
学习Python的优势体现在哪些方面?
开发语言·python·学习
_玖-幽4 分钟前
Python 数据分析01 环境搭建教程
大数据·python·jupyter
databook9 分钟前
『Plotly实战指南』--面积图绘制与应用
python·数据分析·数据可视化
carpell19 分钟前
【双指针法】:这么常用的你怎么能不知道
python·链表·字符串·数组·双指针法
pound12724 分钟前
第五章.python函数
windows·python·microsoft
仙人掌_lz26 分钟前
企业年报问答RAG挑战赛冠军方案:从零到SotA,一战封神
python·gpt·ai·llm·rag·问答·年报
Code_流苏29 分钟前
《Python星球日记》第27天:Seaborn 可视化
python·数据可视化·python入门·seaborn·统计图表
过期动态1 小时前
【动手学深度学习】LeNet:卷积神经网络的开山之作
人工智能·python·深度学习·神经网络·机器学习·分类·cnn
晴天毕设工作室1 小时前
计算机毕业设计指南
java·开发语言·python·计算机网络·课程设计
明月看潮生1 小时前
青少年编程与数学 02-016 Python数据结构与算法 16课题、贪心算法
python·算法·青少年编程·贪心算法·编程与数学