爬虫 网页数据的解析提取

Xpath

常用规则

表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父亲节点
@ 选取属性

表达式//title[@lang='eng']表示选择所有名称为title,同时属性lang的值为eng的节点。

安装

Python 复制代码
pip3 install lxml

实例

Python 复制代码
from lxml import etree

text = '''
<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(text)
result = etree.tostring(html)
print(result.decode('utf-8'))

导入lxml库的etree模块并调用HTML类对一段HTML文本进行初始化,这样就构造了一个Xpath解析对象。注意HTML文本中的最后一个li节点是没有闭合的,但是etree模块自动修正了HTML文本,并且自动添加了body、html节点。调用tostring方法输出修正后的bytes类型的HTML代码。

也可以直接解析html文件:

Python 复制代码
from lxml import etreeh

html = etree.parse('./temp.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))

其中temp.html就是上个例子中的HTML文本。这次的结果多了一个DOCPYTE声明。

所有节点

一般用//开头的Xpath规则,来选取所有符合规则的节点:

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//*')
print(type(result))
print(result)

*表示匹配所有节点,运行结果就是整个HTML文本的所有节点,形式是一个element的列表。

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//li')
print(result)
print(result[0])

表达式//li就能获取全部的li节点。

子节点

通过/或者//可查找子节点或者子孙节点。

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//li/a')
print(result)

表达式//li/a用于获取所有li节点的所有直接子节点a。

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//ul//a')
print(result)

/用于获取直接子节点,要获取节点所有符合要求的子孙节点,可以使用//:

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//ul//a')
print(result)

//ul//a是获取所有ul节点的所有子孙节点a。

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//ul/a')
print(result)

因为ul节点的子节点中没有a节点,所以返回结果为空。注意/和//的区别。

父节点

..可以用来查找父节点。

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)

//a[@href="link4.html"]/../@class表示href属性为link4.html的a节点的父节点,再获取该节点的class属性。

也可以用parent::获取父节点:

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/parent::*/@class')
print(result)

属性匹配

@符合可以用于属性过滤

Python 复制代码
from lxml import etree

html = etree.parse('./temp.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]')
print(result)

@class="item-0"\]限制了节点li的class属性为item-0。 ## 文本获取 Xpath的text方法可以获取节点中的文本。 ```Python from lxml import etree html = etree.parse('./temp.html', etree.HTMLParser()) result = html.xpath('//li[@class="item-0"]/text()') print(result) ``` ![image.png](https://file.jishuzhan.net/article/1783043331605925890/5a0fb68af9143a4244525bda56a12497.webp) 没有获取任何文本,只得到了一个换行符。原因是text方法前面的是/,而/选取的是直接子节点。li节点的直接子节点都是a节点,文本实际上是a节点内部的。这里返回的是自动修正的li节点内部的换行符。 想获取li节点内部的文本有两个方法。第一是先选取li节点内部的a节点,再提取a节点内部的文本。 ```Python from lxml import etree html = etree.parse('./temp.html', etree.HTMLParser()) result = html.xpath('//li[@class="item-0"]/a/text()') print(result) ``` ![image.png](https://file.jishuzhan.net/article/1783043331605925890/0998e961624da70409c249496c983368.webp) 这种情况下我们是逐层选取的。 另一种方法是使用//选取所有子孙节点的文本。 ```Python from lxml import etree html = etree.parse('./temp.html', etree.HTMLParser()) result = html.xpath('//li[@class="item-0"]//text()') print(result) ``` ![image.png](https://file.jishuzhan.net/article/1783043331605925890/fd158c6d6efe68796ce1ca844b6b03b3.webp) 这样就获取了所有子孙节点的文本。但是这样的结果可能会掺杂一些特殊字符。 ## 属性获取 @也可以用来获取节点的属性。 ```Python from lxml import etree html = etree.parse('./temp.html', etree.HTMLParser()) result = html.xpath('//li/a/@href') print(result) ``` ![image.png](https://file.jishuzhan.net/article/1783043331605925890/6123a4625632844fece0a71b65c837ec.webp) 这样可以获取所有li节点的所有子节点a的href属性。

相关推荐
HuiSoul2003 小时前
Spring MVC
java·后端·spring mvc
Flobby5293 小时前
Go 语言中的结构体、切片与映射:构建高效数据模型的基石
开发语言·后端·golang
摇滚侠5 小时前
面试实战 问题二十四 Spring 框架中循环依赖问题的解决方法
java·后端·spring
GetcharZp6 小时前
C++日志库新纪元:为什么说spdlog是现代C++开发者必备神器?
c++·后端
三木水7 小时前
Spring-rabbit使用实战七
java·分布式·后端·spring·消息队列·java-rabbitmq·java-activemq
快乐就是哈哈哈7 小时前
一篇文章带你玩转 EasyExcel(Java Excel 报表必学)
后端
快乐就是哈哈哈7 小时前
手把手教你用 Java 写出贪吃蛇小游戏(附源码)
后端
别来无恙1497 小时前
Spring Boot文件下载功能实现详解
java·spring boot·后端·数据导出
公众号_醉鱼Java8 小时前
Elasticsearch文档数迷思:为何count和stats结果打架?深度解析背后机制
后端·掘金·金石计划
程序员爱钓鱼8 小时前
Go语言实战案例:使用Gin处理路由参数和查询参数
后端