XPath 可以在XML文档中查找信息,支持HTML,可以用来提取信息。可以把标签文本看作一个树状图,最顶层是html,第二层是head和body,body的下面是许多div,每个div可以用/[@属性=属性名]来进一步细分,也可以通过/@属性来获取对应的信息,提取双标签中的文字则可以用/text()。
安装库
pip3 install lxml
基本使用
from lxml import etree  
wb_data = """  <div>  <ul>  <li class="item-0"><a href="link1.html">first item</a></li>  <li class="item-1"><a href="link2.html">second item</a></li>  <li class="item-inactive"><a href="link3.html">third item</a></li>  <li class="item-1"><a href="link4.html">fourth item</a></li>  <li class="item-0"><a href="link5.html">fifth item</a>  </ul>  </div>  """  
html = etree.HTML(wb_data)  
print(html)  
result = etree.tostring(html)  
print(result.decode("utf-8"))
从下面的结果来看,这里的html其实就是一个python对象,etree.tostring(html)则补全了html缺胳膊少腿的标签。
输出结果:
<Element html at 0x39e58f0>  
<html><body><div>  <ul>  <li class="item-0"><a href="link1.html">first item</a></li>  <li class="item-1"><a href="link2.html">second item</a></li>  <li class="item-inactive"><a href="link3.html">third item</a></li>  <li class="item-1"><a href="link4.html">fourth item</a></li>  <li class="item-0"><a href="link5.html">fifth item</a>  </li></ul>  </div>  </body></html>
语法
选取节点
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下面列出了最有用的路径表达式:
| 表达式 | 描述 | 
|---|---|
| nodename | 选取此节点的所有子节点。 | 
| / | 表示子级范围 | 
| // | 表示子孙后代范围 | 
| . | 选取当前节点。 | 
| … | 选取当前节点的父节点。 | 
| @ | 根据属性具体判断某一个标签,可以通过 标签名[属性值判断式] 来定位, | 
示例
在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:
| 路径表达式 | 结果 | 
|---|---|
| bookstore | 选取 bookstore 元素的所有子节点。 | 
| /bookstore | 选取元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! | 
| bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 | 
| //book | 选取所有 book 子元素,而不管它们在文档中的位置。 | 
| bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 | 
| //@lang | 选取名为 lang 的所有属性。 | 
谓语(Predicates)
谓语被嵌在方括号中,用来查找某个特定的节点或者包含某个指定的值的节点。
顺序定位
- 方式一:[1]
 通配标签返回的是一个列表,里面为空或者一些元素。
 xpath支持取具体值, 例如去列表的第一个元素,[1]就可以拿到;第二个值就是[2]。
 注意列表的下角标从1开始,不像python那样从0开始
- 方式二:position()
 position函数会返回当前的位置值,拿到值之后就可以对值进行判断选取。
 举例: 选取当前位置下的所有div标签,然后去掉第一个,取出从2开始,一直到最后 一个div标签。
 .//div[position()>1]
- 方式三:last()
 这个是最简单的,就是取列表的最后一个元素
 举例:
 .//div[last()]
示例
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
| 路径表达式 | 结果 | 
|---|---|
| /bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 | 
| /bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 | 
| /bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 | 
| /bookstore/book[position() < 3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 | 
| //title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 | 
| //title[@lang=‘eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 | 
| /bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 | 
| /bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 | 
选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
| 通配符 | 描述 | 
|---|---|
| * | 匹配任何元素节点。 | 
| @* | 匹配任何属性节点。 | 
| node() | 匹配任何类型的节点。 | 
示例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
| 路径表达式 | 结果 | 
|---|---|
| /bookstore/* | 选取 bookstore 元素的所有子元素。 | 
| //* | 选取文档中的所有元素。 | 
| //title[@*] | 选取所有带有属性的 title 元素。 | 
选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
示例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
| 路径表达式 | 结果 | 
|---|---|
| //book/title | //book/price | 选取 book 元素的所有 title 和 price 元素。 | 
| //title | //price | 选取文档中的所有 title 和 price 元素。 | 
| /bookstore/book/title | //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 | 
与或非
通过与或非进行更严格的筛选
| 逻辑 | 与 | 或 | 非 | 
|---|---|---|---|
| 关键字 | and | or | not() | 
| 示例 | .//div[@id and @class] | .//div[@id or @class] | .//div[not(@id)] | 
| 示例解释 | 同时拥有id和class属性的div | 有id或者class属性的div | 没有id属性的 div | 
常用函数
contains(属性名,值)
匹配包含对应值的标签或数据
response.xpath('//a[contains(text(),"Next >")]')
response.xpath('.//div[contains(@class,"content")]')
获取某个标签的内容(基本使用),注意,获取a标签的所有内容,a后面就不用再加正斜杠,否则报错。
text()
获取文本
 写法一
html = etree.HTML(wb_data)
html_data = html.xpath('/html/body/div/ul/li/a')
print(html)
for i in html_data:print(i.text)
输出:
<Element html at 0x12fe4b8>
first item
second item
third item
fourth item
fifth item
写法二
 直接在需要查找内容的标签后面加一个/text()就行
html = etree.HTML(wb_data)
html_data = html.xpath('/html/body/div/ul/li/a/text()')
print(html)
for i in html_data:print(i)
输出:
<Element html at 0x138e4b8>
first item
second item
third item
fourth item
fifth item
parse(html文件路径)
打开读取html文件
#使用parse打开html的文件
html = etree.parse('test.html')
html_data = html.xpath('//*')#打印是一个列表,需要遍历
print(html_data)
for i in html_data:print(i.text)
将文本转码
html = etree.parse('test.html')
html_data = etree.tostring(html,pretty_print=True)
res = html_data.decode('utf-8')
print(res)
示例
打印指定路径下a标签的属性
可以通过遍历拿到某个属性的值,查找标签的内容
html = etree.HTML(wb_data)
html_data = html.xpath('/html/body/div/ul/li/a/@href')
for i in html_data:print(i)
相对路径匹配
下面我们查找相对路径,例如,查找所有li标签下的a标签内容。
html = etree.HTML(wb_data)
html_data = html.xpath('//li/a/text()')
print(html_data)
for i in html_data:print(i)
打印:
['first item', 'second item', 'third item', 'fourth item', 'fifth item']
first item
second item
third item
fourth item
fifth item
使用相对路径,查找一下相对路径下li标签下的a标签下的href属性的值,注意,a标签后面需要双//。
html = etree.HTML(wb_data)
html_data = html.xpath('//li/a//@href')
print(html_data)
for i in html_data:print(i)
打印:
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
link1.html
link2.html
link3.html
link4.html
link5.html
相对路径下跟绝对路径下查特定属性的方法类似,也可以说相同。
html = etree.HTML(wb_data)
html_data = html.xpath('//li/a[@href="link2.html"]')
print(html_data)
for i in html_data:print(i.text)
打印:
[<Element a at 0x216e468>]
second item
查找最后一个li标签里的a标签的href属性
html = etree.HTML(wb_data)
html_data = html.xpath('//li[last()]/a/text()')
print(html_data)
for i in html_data:print(i)
打印:
['fifth item']
fifth item
查找倒数第二个li标签里的a标签的href属性
html = etree.HTML(wb_data)
html_data = html.xpath('//li[last()-1]/a/text()')
print(html_data)
for i in html_data:print(i)
打印:
['fourth item']
fourth item