《浅谈解析库XPath,bs4和pyquery》

# **《浅谈解析库XPath,bs4和pyquery》**  

### 作者:*墨非墨菲非菲*   

前几天在CSDN看到一篇帖子,题目是“如何让自己像打王者一样发了疯,拼了命,石乐志的学习”。这里面讲到了阶段性反馈机制,我觉得蛮有意思的,正好前两天用python写了一个scrawler爬取了某XXXX软件上面的挑战答题并自动匹配。在解析题库网页的时候碰到了一系列的问题,把三种解析库都回顾了个遍。借着这个兴奋劲儿,决定码一篇python解析库————lxml,bs4,以及pyquery的简要概述。![某xxxx](https://img-blog.csdnimg.cn/20200401100514534.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ2NjgzNzQ0,size_16,color_FFFFFF,t_70)  :happy:  

下面仅仅是我个人的回忆和记录,仅供参考,错误之处还请多多指正。

## **写在前面**  

以上提到的三个是python语言中最最最常用的三个解析库,解析库是用来提取网页的html信息的。首先要解释一下,网页中如此多的信息,为什么能够被精准的获取到。网页可以分为三大部分————HTML,JavaScrip和CSS,这些要素构成了我们看到的丰富多彩的网页。  


解析库基于网页的两类特征,一类是节点树(HTML DOM),一类是css选择器。如果把节点树比作一个家庭,那么它就包含了它爷爷,它老汉(parent),它兄弟(sibling)以及它儿子和孙孙(children)。有了这个节点树,网页里面每一个元素就层级分明的展现了出来。![节点树](https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1585714661896&di=60199f4f824cb632393634936d57e943&imgtype=0&src=http%3A%2F%2Fimg1.imgtn.bdimg.com%2Fit%2Fu%3D137618396%2C3170534982%26fm%3D214%26gp%3D0.jpg)  具体的,构成这棵树的枝节,又包根元素(html),元素(title,body,a...),属性(class,href...)和文本等等。css,即层叠样式表,它有一套属于自己的语法规则,举个例子,选择器(.link)代表了'class=“link”',(#song)代表了'id=“song”',(a)代表了'a'的所有节点。 ![html网页](https://img-blog.csdnimg.cn/20200401100658276.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ2NjgzNzQ0,size_16,color_FFFFFF,t_70)  

有了上面的规则,每个元素的位置就被唯一确定了下来了,接下来解析库就登上了舞台,使出了元哥般的秀发。  

>下面是分割线   


---

## **【解析库一:XPath】**  

XPath,全名是XML Path Lauguage。拿到一个库,如果有精力和毅力阅读官方文档当然最好,虽然我几乎没有过。知道名字,好像意义不大,所以,直接上干货。

### 1.初始化  

先说说初始化(initition),可被解析的有字符串(string)和html文件(filename.html)。具体的使用方法:  

~~首先要安装lxml库,python CMD:pip install lxml~~  

```python  

res = '''   

        

            

                射雕英雄传之东成西就

                         / 射鵰英雄傳之東成西就

                     / 东成西就  /  大英雄 (日本)

            

                [可播放]

    

            '''

from lxml import etree

html = etree.HTML(res) #调用HTML()方法初始化字符串(string)例

html = etree.tostring() #调用tostring()方法可以补全未闭合的标签

print(html.decoding('utf8')) #以字符串的格式打印 

```  



文件采用以下方法:  

```python

from lxml import etree  

html = etree.parse('./text.html',etree.HTMLParser()) #调parse()方法来实例化文件(filename.html)  

result = html.xpath('//*') #xpath()里面传入匹配规则  

```  

到这里,初始化的方法就说完了(我所知道的,手动狗头保命/滑稽)  

### 2.匹配方法

#### (1)子节点(/)和子孙节点(//)

例如,我想获取以上res文本中的“射雕英雄传之东成西就”和'a'标签里的网址,

以下涉及到了子节点,子孙节点的选择,属性的匹配,文本和属性值的获取。  

```python

movie = html.xpath('//div[@class="info"]//a/span/text()')  

url = html.xpath('//div[@class="info"]//a/@href')  

```    

#### (2)获取文本“东成西就”,class属性值有两个,那么就引入了属性多值匹配和多属性匹配

```python

text=html.xpath('//div[@class="info"]//span[contains(@class,"other") and @name="item"]/text()')  

``` 

### 3.节点选择  

在匹配方法中主要涉及了子节点和子孙节点。接下来还有父节点(parent)和祖父节点(parents)

```python

result = html.xpath('//span[@class="titile"]/../a')  

```    

因为xpath默认解析全部符合规则的标签,所以这里就不提及sibling节点。  

### 4.类型  

如果可爱的你玩元哥不想成为神仙打架,那么时时刻刻清楚自己的本体在哪是很重要的。如果在运用解析库的时候不想天花乱坠,得时时刻刻清楚下一步输出的文件类型!!!知道类型才知道应该用什么方法。 

```python

print(type(result),result,sep='|')  

>>>|[]  

```  

是列表类型记得迭代,for搞定一切!

### 5.按序选择

因为同级的标签是有很多的,上面得出了输出文件是list的结论,那么聪明的你一定知道了,它可以切片,我仿佛看到了乐事薯片发出的香甜可口的气息~好了,pia!回到正题,按序选择就是用中括号传入索引获取特定了次序的节点。  

==注意是从“1”开始==  

直接上码  

```python

result = html.xpath('//li[1]/a/text()')

#可替换的例如:[last()], [last()-1], [positon()<3]等等

```

### 6.好了,没有6了  

当然,更多详细的关于python的lxml库的使用可以[访问网址](http://lxml.de/)。

个人的一点小总结,lxml的使用方法和BeautifulSoup是比较接近的,都是依赖于节点树和标签。功能上说得上中规中矩,该有的它都有。一定要说一点优势嘛,它的层级结构是非常清楚的,非常易于阅读。并且直接建立在lxml之上,匹配速度应该算是很快的了(空口说这个速度会不会有些有失公允,让用户感知不强?)。不过关于属性匹配的写法,难免复杂了些。遇到属性多值的匹配还傲娇,必须得用[constains(@class,"...")来进行匹配,多多少少有些复杂。当然,这都是些后话啦。

## **【解析库二:BeautifulSoup】**  

一个强大的解析工具BeautifulSoup,借助网页结构和属性来解析网页。最先接触的解析库,本着“国外的月亮更圆”的陋习,在只知道BeautifulSoup的时候,还去硬刚过re,结果被re复杂的匹配规则劝退。顺便说一下,re它长这样:

```python

>data-src = re.findall('

.*?class="star">(.*?)
))$。  

```

手动(黑人脸问好.png),废话不多说,直接上菜。  

### 1.初始化

BeautifulSoup可传入字符串(string),在解析时依赖于解析器,例如'BeautifulSoup(res,'html.parser'),这里的html.parser就是python内置的标准解析库,执行速度适中,文档容错性强。如果你非要快,'lxml'和'xml'是你的不二选择,不过提前安装好C语言库哦。  

~~首先要安装bs4库,python CMD:pip install bs4~~  

```python

import requests

from bs4 import BeautifulSoup

res = requests.get('https://www.baidu.com/s?ie=UTF-8wd=%E5%B0%8F%E8%AF%B4').text    #获取网页源代码

soup = BeautifulSoup(res,'lxml')     #格式化字符串,依靠lxml析库

```  

### 2.基本用法

**find_all()**,顾名思义,查找所有符合条件的元素。它的api如下:

find_all(name,attr,text,**kwargs) 

实操环节  

```python

items = soup.find('div', class_="c-tabs c-gap-top-small")find('ul').find_all('li)['href']

for item in items[0]:

    print(item)

```

以上实例展示了find():查找符合条件的第一个标签,属性的引用==需要说明,在python中class为关键字,所以碰到class标签要加上下划线==,获取属性值['href'] or .get('herf), 文本的获取(.text)/(.string)  

### 3.节点选择

#### (1)子节点和子孙节点  

选取节点元素之后,如果想要获取它的直接子节点,直接调用contents属性或children即可,例如:    

```python

print(items.a.contents)

```

如果要得到子孙节点,可以调用descendants属性。值得一提,descendants会递归查询所有的子孙节点。

#### (2)父节点和祖父节点

```python  

print(items.a.parent)   #父节点

print(items.a.parents)   #祖先节点

```

#### (3)兄弟节点  

```python

print(items.a.sibling)  

```

哈哈哈,到这里,你以为只是简单的复述几个单词咯?下面才是冷知识点。  

**next_sibling, previous_sibling, list(enumerate(next_siblings)), list(enumerate(previous_siblings))**  

### 4.类型  

类型永远是个重点,初次试水记得时时刻刻用type()函数查询,直接捡现的:  

```python

print(type(soup))

>>>

```

在没有被text之前,它始终是个bs4.element.Tag类型,这也意味着,能够在bs4.element.Tag上面套娃————实现嵌套功能:find('ul').find_all('li)。  

### 5.CSS选择器  

BeautifulSoup还提供了css选择器,只需要调用select()方法,传入相应的css选择即可。并且,它继承了BeautifulSoup的bs4.element.Tag类型,支持嵌套功能。举个例子:  

```python

items = soup.find('div', class_="c-tabs c-gap-top-small")find('ul').find_all('li)['href']  #find()写法

items = soup.select('div[contains(@class="c-tabs")]/ul/li@href')       #select()写法

```

### 6.好了,BeautifulSoup的基本用法就介绍完了  

最后来点小总结。聪明的你一定会发现,BeautifulSoup的篇幅足够短,因为它足够easy和brief。而且它向左兼容节点,向右能用css。给它个“好看又能打”的称号一点也不为过了。从初始化开始,它的文件类型始终是,你可以随意嵌套。碰到单个文件,直接打印;多个文件,迭代打印。唯一需要注意的是class_="...",一定不要忘记!


## **【解析库三:pyquery】**  

接下来,让我们来感受一下这个偏科生————pyquery的强大之处。

### 1.初始化  

~~首先安装pyquery库,python CMD:pip install pyquery~~  

pyquery支持三种类型的参数传入,分别是字符串(string),网址(url='www.baidu.com'),文件(filename='.*.html'),举个例子:  

```python

from pyquery import PyQuery as pq

doc = pq(url='www.baidu.com) 

``` 

细心的你在这里已经发现了问题:为啥传了一个网址进去,这怎么用。其实,pyquery自带了获取html的功能,它相当于:  

```

doc = pq(requests.get('www.baidu.com).text)  

```

### 2.基本用法

复习一下CSS选择器的规则。

```

lis = '''

    

        

            阳光灿烂的日子

             / In the Heat of the Sun

        

    

    

        导演: 姜文 Wen Jiang   主演: 夏雨 Yu Xia / 宁静 Jing Ning / 陶虹 Hong Tao

        1994 / 中国大陆 中国香港 / 剧情 爱情

    

'''  

from pyquery import PyQuery as pq

doc = pq(lis)

print(doc('#container .hd a span.title).text())

>>>阳光灿烂的日子 

``` 

获取属性:调用attr方法(a.attr('href))或(a.attr.href);获取文本:text();获取html:html()  

### 3.查找节点  

#### (1)子节点和子孙节点

查找子节点,需要用到find()方法,会将符合条件的所有节点全部提取出来。如果只查找子节点,调用children()方法就即可。  

#### (2)父节点和祖父节点

我们可以用parent()来获取某个节点的父节点,parent()获取祖先节点,可以传入参数筛选。

```python

items = lis('span.title').parents('a')

```

#### (3)兄弟节点

获取兄弟节点可以用siblings()方法  

### 4.文件类型

如期而至,让我们来看看pyquery的文件类型是什么。  

```python

print(type(doc))

```

如果获取的是单个节点,可以直接输出,也可以转化为字符串输出。

让我们来看看下面这种情况:  

```python

print(type(items))

 #generator意思是"发生器",这个时候就需要历了。使用之前需要先用items()格式化。

for item in items.items()

    print(item)

```

!!!**重点在这里,pyquery返回的多个节点需要用遍历处理。**  

### 5.节点处理

下面列举几种pyquery常用的节点处理的方法。

#### (1)addClass( ) 和removeClass( )  

```python

lis='''

        

            阳光灿烂的日子

             / In the Heat of the Sun

        

    

'''  

from pyquery import PyQuery as pq

doc = pq(lis)

print(doc('.hd').removeClass('hd').addClass('hahah'))

```

#### (2)attr( )和text( )  

```python

print(doc('a').attr('name','link'))

print9doc('a').text('changed items)

```

#### (3)remove( )

比如提取标签里面“阳光灿烂的日子”  

```python

item = lis('a')

item.find('.other').remove()

print(item)

```

### 6.好了,pyquery的用法到这里也介绍完了  

总结一下,功能强大,写法简洁。愿你解析半天,归来还是pq。


#**Title1**   

##*line2*  

###==line3==  >muname  

<'alert('hello world');'  >  

    '''

    

    '''   

![picture](C:\Users\tingy\Desktop\节点树.jpg)

## 写在最后

从2020/03/31 15:20开始,到04/01 09:59,这一篇文章已经算是圆满了。再次感谢我妈和我的芬芬儿昨天晚上帮我洗碗,我才能够比较顺利的,时效的完成这篇梳理。

##  引用

[1]崔庆才,Python3网络开发与实践,[M],2018.4,人民邮电出版社。  

[2]URL:https://docs.python.org/zh-cn/3.7/  [点击阅读python官方文档](https://docs.python.org/zh-cn/3.7/)。  

[3]python3 lxml标准库[点击阅读lxml文档](http://lxml.de/)。   

[4]沈承放,莫达隆,beautifulsoup库在网络爬虫中的使用技巧及应用,[J],2019.3,2019(28)[点击阅读paper](https://login.cnki.net/login/?platform=kdoc&ForceReLogin=1&ReturnUrl=%2f%2fkns.cnki.net%2fKXReader%2fDetail%3fdbcode%3dCJFQ%26filename%3dDNZS201928007%26uid%3d)。  

[5]风变编程——BeautifulSoup实践。

![](https://img-blog.csdnimg.cn/20200401101928419.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzQ2NjgzNzQ0,size_16,color_FFFFFF,t_60)

你可能感兴趣的:(《浅谈解析库XPath,bs4和pyquery》)