Python爬⾍之解析库的使⽤(XPath、BeautifulSoup)
XPath
XPath即为XML路径语⾔(XML Path Language),它是⼀种⽤来确定XML⽂档中某部分位置的语⾔。它的选择功能⼗分强⼤,所以在做爬⾍时我们完全可以使⽤XPath来做相应的信息提取。
准备⼯作
我们后⾯使⽤的是Python的lxml库,利⽤XPath进⾏HTML的解析。
Windows下可以打开命令⾏窗⼝输⼊pip3 install lxml进⾏安装lxml库,安装完之后打开python输⼊import lxml如果没有报错即安装成功。如果安装过程出错可以采⽤wheel⽅式安装,参考如下:
同时我们准备了run.html⽂件保存⾄相应python⽂件路径下以备使⽤,⽂件内容如下:
<!DOCTYPE html>
<html>
<body>
<div>
<ul>
<ul>
<li class="name-0" color ="red"><a href="link1.html"><span>first</span></a></li>
<li class="name-1" color ="red" old="21"><a href="link2.html">secode</a></li>
<li class="name-0"color="blue"><a href="link3.html">third</a></li>
<li class="name-3 boy"><a href="link4.html">fourth</a></li>
<li class="name-5 boy"><a href="link5.html"time="2020-7-29">fifth</a></li>
</ul>
</ul>
</div>
</body>
</html>
XPath常⽤规则
表达式描述
/从当前节点选取其直接⼦节点
//从当前节点选取⼦孙节点
.选取当前节点
..选取当前节点的⽗节点
@属性选取
选取所有节点
我们⼀般会⽤//开头的XPath规则来选取所有符合要求的节点。
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
print(type(html))
res = html.xpath('//*')
print(type(res))
print(res)
输出结果如下:
<_ElementTree'>
<class'list'>
[<Element html at 0x1ed5a81e248>,<Element body at 0x1ed5a8cfa48>,<Element div at 0x1ed5a8cf988>,<Element ul at 0x1ed5a8cf9c8>,<Element ul at 0x1ed5a8cfa88>,<Element li at 0x1ed5a8cfb08>,<Element a at 0x1ed5a8cfb48>,<Element span at 0x1ed5a8cfb88>,<Element li at 0x1ed5a8cfbc8>,<El ement a at 0x1ed5a8cfac8>,<Element li at 0x1ed5a8cfc08>,<Element a at 0x1ed5a8cfc48>,<Element li at 0x1ed5a8cfc88>,<Element a at 0x1ed5a8cfcc 8>,<Element li at 0x1ed5a8cfd08>,<Element a at 0x1ed5a8cfd48>]
我们通过使⽤//作为开头来选择符合要求的节点,后⾯加了*代表匹配所有节点。因此输出结果即是所有节点。返回的形式是⼀个列表,列表⾥的每个元素是⼀个Element类型,后⾯跟着节点的名称,如html、body、div、li等。
我们也可以选择指定名称的所有结点,如下:
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li')
print(res)
print(res[3])
输出如下:
[<Element li at 0x1c46fa23c48>,<Element li at 0x1c46fa23c08>,<Element li at 0x1c46fa23cc8>,<Element li at 0x1c46fa23d08>,<Element li at 0x1c46fa23 d48>]
<Element li at 0x1c46fa23d08>
我们成功输出了所有名称为li的节点,同时我们选择输出了索引为3的li节点。
选取⼦节点
我们可以通过/或//查元素的⼦节点或⼦孙节点。
加⼊我们想要选择li节点的所有直接a⼦节点,可以这样实现。
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li/a')
print(res)
输出如下:
[<Element a at 0x286380a3c48>,<Element a at 0x286380a3c08>,<Element a at 0x286380a3cc8>,<Element a at 0x286380a3d08>,<Element a at 0x286 380a3d48>]
其中//li代表选择所有li节点,/a选择刚才的所有li结点的直接a⼦节点
选取⽗节点
如果我们想要获取href属性为link4.html的a节点的⽗节点class属性,我们需要怎么办呢?
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//a[@href="link4.html"]/../@class')
print(res)
输出结果如下:
['name-3 boy']
我们成功输出了其class属性值,在这⾥我们使⽤了..来选择当前节点的⽗节点
属性匹配
有时候我们需要选择属性值为特定值的节点,则我们可以利⽤@符合进⾏属性过滤,⽐如我们要选择所有class属性为"name-0"的节点。
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li[@class="name-0"]')
print(res)
输出结果如下:
[<Element li at 0x20ddb8d3c08>,<Element li at 0x20ddb8d3cc8>]
见结果可知成功输出了两个li节点。
⽂本获取
我们可以⽤XPath中的text()⽅法获取节点中的⽂本信息,我们获取所有class属性为"name-0"的节点的⽂本信息。
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li[@class="name-0"]//text()')
print(res)
输出结果如下:
['first','third']
我们看到成功输出了相应的⽂本信息。
如果我们将res = html.xpath('//li[@class="name-0"]//text()')改为res = html.xpath('//li[@class="name-0"]/text()')会发⽣什么呢?
[]
输出将会是空的,因为相应的⽂本信息是在更深层的节点<span>和<a>之中的,可见text()只是得到当前节点层的⽂本信息,但是如果⽤//就会搜索所有节点的⽂本信息。
属性获取
我们可以通过[@xxx=xxx]来限定属性,同样的,我们也可以通过@来获取属性。
我们想要获取所有a节点的href属性:
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li/a/@href')
print(res)
输出如下:
['link1.html','link2.html','link3.html','link4.html','link5.html']
属性多值匹配
我们可以看到<li class="name-3 boy">节点的class属性有两个
如果我们想获取class属性包含"name-3"的节点的所有⽂本信息时,我们如果还是⽤上⽂的//li[@class=“name-3”]//text()是⾏不通的。
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li[@class="name-3//text()')
print(res)
python怎么读取py文件输出为空:
[]
因此这个时候就需要contains了,正如字⾯意思,contains是只要包含该元素,就会被选择.
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li[contains(@class,"name-3")]//text()')
print(res)
输出如下:
['fourth']
多属性匹配
此外我们还会遇到⼀种情况,就是根据多个属性确定⼀个节点,这是就需要同时匹配多个属性。
⽐如我们想要得到class属性为"name-0"且color属性为"blue"的li节点的⽂本信息。
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li[@class="name-0" and @color="blue"]//text()')
print(res)
['third']
这⾥的and其实是XPath⾥的运算符。下⾯列出了其他常⽤的运算符.
运算符描述
or或
and与
mod计算除法的余数
|计算两个节点集
+加
-减
*乘
div除法
=等于
!=不等于
<⼩于
<=⼩于等于
>⼤于
>=⼤于等于
按序选择
有时候哦我们在选择的时候某些属性可能匹配了多个节点,但是我们只想要其中的某个节点我们可以通过中括号传⼊索引的⽅法获取特定次序的节点。
import requests
import random
from lxml import etree
if __name__=='__main__':
html =etree.parse('run.html',etree.HTMLParser())
res = html.xpath('//li[1]/a/pan/text()')
print(res)
res = html.xpath('//li[last()]/a/text()')
print(res)
res = html.xpath('//li[position()<4]/a/text()')
print(res)
输出如下:
[]
['fifth']
['secode','third']
节点轴选择
XPath还提供了许多节点轴旋转⽅法,包括获取⼦元素、兄弟元素、⽗元素等
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论