爬虫-解析库的使用-Xpath

4. 解析库的使用

对于网页节点,可以定义id、class或其他属性,且节点之间有曾是关系,可以通过Xpath 或者CSS选择器来定位一个或者多个节点,再调用相应的方法获取它的正文内容或者属性。

python中强大的解析库有:lxml、Beautiful Soup、pyquery

4.1 使用Xpath

4.1.1 Xpath 概览

Xpath的选择功能十分强大,提供了非常简洁明了的路径选择表达式。几乎我们想要定位的节点,都可以用Xpath 来选择。

4.1.2 xpath 基础教程

https://www.runoob.com/xpath/xpath-tutorial.html

(1)常用规则

选取节点
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。 下面列出了最有用的路径表达式:

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
… 选取当前节点的父节点。
@ 选取属性。
在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore
选取根元素 bookstore。

注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!

bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ck23VKVJ-1617015912878)(attachment:d622fd58-bdd8-40f6-8284-b0d1ee3da64f.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eDb53adZ-1617015912882)(attachment:6e3f6e52-ae72-48b8-86fa-7b519b016e04.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njW4PTbL-1617015912884)(attachment:ec0beeaf-750d-4f1b-bf45-107bb4fec799.png)]

(2)准备工作
pip install lxml
Collecting lxml
  Downloading lxml-4.6.3-cp37-cp37m-win_amd64.whl (3.5 MB)
Installing collected packages: lxml
Successfully installed lxml-4.6.3
Note: you may need to restart the kernel to use updated packages.
(3)实例引入
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a herf="link0.html">first item</a></li>
<li class="item-1"><a href="link1.html">second item</a></li>
<li class="items-2"><a herf="link2.html">third item</a></li>
<li class="item-3"><a href="link3.html">fourth item</a></li>
<li class="item-4"><a href="link4.html">fourth item</a>
</ul>
</div>
'''
html=etree.HTML(text)
result=etree.tostring(html)
print(result.decode('utf-8'))
<html><body><div>
<ul>
<li class="item-0"><a herf="link0.html">first item</a></li>
<li class="item-1"><a href="link1.html">second item</a></li>
<li class="items-2"><a herf="link2.html">third item</a></li>
<li class="item-3"><a href="link3.html">fourth item</a></li>
<li class="item-4"><a href="link4.html">fourth item</a>
</li></ul>
</div>
</body></html>

首先导入了lxml库的etree模块(用于解析DOM树),然后声明了一段HTML文本,调用HTML类进行初始化。注意这里HTML文本中最后一个li节点是没有的,使用etree.tostring()方法进行修正,但是输出的结果是Bytes型,所以利用decode()方法将其转换成str类型。

另外可以直接读取文本文件进行解析。:

from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=etree.tostring(html)
print(result.decode('utf-8'))
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><div>
<ul>
<li class="item-0"><a herf="link0.html">first item</a></li>
<li class="item-1"><a href="link1.html">second item</a></li>
<li class="items-2"><a herf="link2.html">third item</a></li>
<li class="item-3"><a href="link3.html">fourth item</a></li>
<li class="item-4"><a href="link4.html">fourth item</a></li><li>
</li></ul>
</div></body></html>
(4)所有节点
  • 一般会使用//开头的Xpath规则来选取所有符合要求的节点。
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//*')
print(result)
[<Element html at 0x22f1233c3c8>, <Element body at 0x22f1233c608>, <Element div at 0x22f1233c6c8>, <Element ul at 0x22f1233ca08>, <Element li at 0x22f1233cb08>, <Element a at 0x22f1233cd88>, <Element li at 0x22f1233ce48>, <Element a at 0x22f1233f108>, <Element li at 0x22f1233f388>, <Element a at 0x22f1233cb88>, <Element li at 0x22f1233f4c8>, <Element a at 0x22f1233f588>, <Element li at 0x22f1233f5c8>, <Element a at 0x22f1233f608>, <Element li at 0x22f1233f648>]

备注:*代表匹配所有节点

  • 获取指定节点名称
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//li')
print(result)
print(result[0])
[<Element li at 0x22f1234a708>, <Element li at 0x22f1234a7c8>, <Element li at 0x22f1234ab08>, <Element li at 0x22f1234abc8>, <Element li at 0x22f1234ad08>, <Element li at 0x22f1234ae88>]
<Element li at 0x22f1234a708>
(5)子节点
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//li/a')
print(result)
print(result[0])
[<Element a at 0x22f1234d388>, <Element a at 0x22f1234d788>, <Element a at 0x22f1234d888>, <Element a at 0x22f1234d908>, <Element a at 0x22f1234d948>]
<Element a at 0x22f1234d388>

注意:/用于选取直接子节点,如果想要获取所有子孙节点,可以使用//,如下要获取ul节点下的所有子孙节点a

from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//ul/a')
print(result)
print(result[0])
[]



---------------------------------------------------------------------------

IndexError                                Traceback (most recent call last)

<ipython-input-18-68eb1d35d42c> in <module>
      3 result=html.xpath('//ul/a')
      4 print(result)
----> 5 print(result[0])


IndexError: list index out of range
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//ul//a')
print(result)
print(result[0])
[<Element a at 0x22f12356f48>, <Element a at 0x22f119820c8>, <Element a at 0x22f11982348>, <Element a at 0x22f119823c8>, <Element a at 0x22f11982408>]
<Element a at 0x22f12356f48>
(6)父节点

如果知道了子节点,如何来找父节点的?

  • 这可以使用… 来实现。

如先选中 href 属性为link4.html 的a节点,然后再获取其父节点,再获取其class属性

from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//a[@href="link4.html"]/../@class')
print(result)
['item-4']
  • 通过parent:: 来获取父节点
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//a[@href="link4.html"]/parent::*/@class')
print(result)
['item-4']
(7)属性匹配

用@符号进行属性过滤,比如选取class为item-0 的li节点

from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]')
print(result)
[<Element li at 0x22f119af8c8>]
(8) 文本获取

用text()可以获取节点内部文本
两种方式:

  • 先取节点再取文本
  • //
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]/a/text()')
print(result)
['first item']
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]//text()')
print(result)
['first item']

如果想要获取子孙节点内部的所有文本,可以直接用//加text()的方式,这样可以保证获取最全面的文本信息,但是可能会夹杂一些换行符等特殊字符。如果想获取某些特定子孙节点下的所有文本,可以先选取到特定的子孙节点,然后再调用text()方法获取其内容,这样可以保证获取结果是整洁的

(9)属性获取

@

from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//li/a/@href')
print(result)
['link0.html', 'link1.html', 'link2.html', 'link3.html', 'link4.html']
(10)属性多值匹配

有时候,某些节点的属性有多个可能的值,如:

from lxml import etree
text='''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result=html.xpath('//li[@class="li"]/a/text')
print(result)
[]

li中 class 有两个属性值,用以前的办法无法匹配,需要用 contains() 函数

from lxml import etree
text='''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result=html.xpath('//li[contains(@class,"li")]/a/text()')
print(result)
['first item']
from lxml import etree
text='''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result=html.xpath('//li[contains(@class,"li")]//text()')
print(result)
['first item']
(11) 多属性匹配
from lxml import etree
text='''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result=html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
print(result)
['first item']

li 中多了一个name 属性

(12)按序选择
from lxml import etree
html = etree.parse("./test.html",etree.HTMLParser())
result=html.xpath('//li[1]//text()')
print(result)
result1=html.xpath('//li[last()]//text()')
print(result1)
result2=html.xpath('//li[position()<3]//text()')
print(result2)
['first item']
['\n']
['first item', 'second item']
(13)节点轴选择

Xpath 提供了很多节点轴的选择方法,包括获取子元素、兄弟元素、父元素、祖先元素等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RFTmucXJ-1617015912888)(attachment:6e6e2d45-63e3-4297-8eec-883c444d9a5b.png)]


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闪闪发亮的小星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值