lxml与XPath
尽管正则表达式处理字符串的能力非常强,但编写功能强大的正则表达式并不容易,而且难以维护,复杂的正则表达式也并不容易理解
幸好还有其他的方式处理字符串,这就是 XPath
XPath 以非常容易理解的路径方式选择 XML 和 HTML 中的节点,容易编写和维护;目前支持 XPath 的库非常多,lxml 就是其中一个
lxml
lxml 是 Python 的一个解析库,用于解析 HTML 和 XML,支持 XPath 解析方式;并且由于 lxml 底层是 C 语言编写的,所以解析效率非常高
安装 lxml
lxml 官网:https://lxml.de/
lxml 在 GitHub 的地址:https://github.com/lxml/lxml
这里只介绍在 Windows 下的安装方法:执行pip install lxml
安装命令即可;lxml 安装完成后即可通过import lxml
导入依赖
操作 xml
lxml 可以读取 xml 文件,也可以使用字符串形式的 xml 文档;如果读取 xml 文件,需要使用 parse 函数,该函数需要传入一个 xml 文件名;如果要解析字符串形式的 xml 文档,需要使用 fromstring 函数,该函数的参数就是 xml 字符串,eg:
准备一个 xml 文件(products.xml),内容如下:
<products>
<product id="0001">
<name>phone</name>
<price>4500</price>
</product>
<product>
<name>computer</name>
<price>6000</price>
</product>
</products>
读取 products.xml 文件(文件与代码在同一目录下)
from lxml import etree
# 读取 products.xml 文件
tree = etree.parse('products.xml')
print(type(tree))
# 将 tree 转换为字符串形式的 xml 文档并输出
print(str(etree.tostring(tree, encoding='utf-8'), 'utf-8'))
# 获取根节点对象
root = tree.getroot()
print(type(root))
# 获取根节点名称并输出
print(root.tag)
# 获取根节点所有子节点
childrens = root.getchildren()
for children in childrens:
print(children.get('id'))
print(children[0].text)
print(children[1].text)
解析字符串形式的 xml 文档
from lxml import etree
root = etree.fromstring('''
<products>
<product id="0001">
<name>phone</name>
<price>4500</price>
</product>
<product id="0002">
<name>computer</name>
<price>6000</price>
</product>
</products>
''')
print(type(root))
print(root.tag)
childrens = root.getchildren()
for children in childrens:
print(children.get('id'))
print(children[0].text)
print(children[1].text)
使用 lxml 库也可以操作 HTML 文档,HTML 本质上与 XML 类似,它们所不同的是 XML 除了这些节点,不能有其他东西,而 HTML 的语法比较自由,不仅可以有节点,还可以有其他任何文本
用 lxml 库操作 HTML 的方式与操作 XML 非常类似,同样可以通过 getroot 方法获取根节点,通过 get 方法获得节点属性值,通过 text 属性获取节点内容,通过索引的方式引用子节点,eg:
准备一个 test.html 文件(与代码在同一目录下),内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body></body>
</html>
解析 html
from lxml import etree
# 获取 HTMLParser 对象
html_parser = etree.HTMLParser()
print(type(html_parser))
# 读取并解析 test.html 文件
tree = etree.parse('test.html', html_parser)
# 获取根节点
root = tree.getroot()
# 获取 html 文档内容
result = etree.tostring(root, encoding='utf-8', pretty_print=True, method='html')
# 将文档转换为可读形式并输出
print(str(result, 'utf-8'))
print(root.tag)
print(root.get('lang'))
print(root[0][0].get('charset'))
print(root[0][1].text)
XPath
XPath 的功能非常强大,它提供了非常简单的路径选择表达式;另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等;几乎所有想定位的节点,都可以用 XPath 来选择
XPath 于1999年11月16日成为 W3C 标准,它被设计为可以在 XSLT、XPointer 以及其他 XML 解析软件中使用
官方文档:https://www.w3.org/TR/xpath-31
XPath 的基本语法规则:
XPath 语法规则 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
从文件装载,通过 parse 函数指定 HTML 文件名;eg(还是解析上面的 test.html 文件):
from lxml import etree
html_parser = etree.HTMLParser()
tree = etree.parse('test.html', html_parser)
# 使用 xpath 定位 title 节点,返回一个集合
titles = tree.xpath('/html/head/title')
if len(titles) > 0:
print(titles[0].text)
从代码装载,通过 HTML 函数指定 HTML 代码,eg:
from lxml import etree
html = '''
<div>
<ul>
<li class="item1"><a href="https://geekori.com">geekori.com</a></li>
<li class="item2"><a href="https://www.jd.com">京东</a></li>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
</ul>
</div>
'''
# 分析 html 代码
tree = etree.HTML(html)
# 使用 xpath 定位 class 属性值为 item2 的<li>节点
a_tags = tree.xpath("//li[@class='item2']")
if len(a_tags) > 0:
print(a_tags[0][0].get('href'), a_tags[0][0].text)
注意:通过 XPath 定位节点返回的是节点集合,即使只有一个节点,返回的也是一个节点集合;使用 XPath 分析的 HTML 文档并不一定是标准的
选取所有节点
//
开头的 XPath 规则会选取所有符合要求的节点,如果使用//*
那么会选取整个 HTML 文档中的所有节点
选取子节点
在选取子节点时,通常会将//
和/
规则放在一起使用,eg://li/a
表示选取 li 节点下的 a 节点
选取父节点
如果知道子节点,想得到父节点,可以使用..
,eg://a[@class='class1']/..
可以得到 class 属性为 class1 的 a 节点的父节点;得到父节点还可以使用parent::*
,所以这个例子也可以使用//a[@class='class1']/parent::*
# 获取 href 属性值为 https://www.jd.com 的 a 节点的父节点的 class 属性值
result = tree.xpath('//a[@href="https://www.jd.com"]/../@class')
result = tree.xpath('//a[@href="https://www.jd.com"]/parent::*/@class')
属性匹配与获取
# 获取所有 href 属性值包含 www 的 a 节点
nodes = tree.xpath('//a[contains(@href,"www")]')
多属性匹配
# 获取所有 href 属性值为 https://www.jd.com 或 https://geekori.com 的 a 节点
node_list = tree.xpath('//a[@href="https://www.jd.com" or @href="https://geekori.com"]')
# 获取 href 属性值包含 www,并且父节点中 value 属性值等于 1234 的 a 节点
node_list = tree.xpath('//a[contains(@href,"www") and ../@value="1234"]')
XPath 中的运算符及其描述:
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集 |
+ | 加法 | 10 + 5 | 15 |
- | 减法 | 10 - 5 | 5 |
* | 乘法 | 10 * 5 | 50 |
div | 除法 | 10 div 5 | 2 |
= | 等于 | value = 218 | 如果 value 是218,返回 true,否则返回 false |
!= | 不等于 | value != 218 | 如果 value 不是218,返回 true,否则返回 false |
< | 小于 | value < 218 | 如果 value 小于218,返回 true,否则返回 false |
<= | 小于或等于 | value <= 218 | 如果 value 小于或等于218,返回 true,否则返回 false |
> | 大于 | value > 218 | 如果 value 大于218,返回 true,否则返回 false |
>= | 大于或等于 | value >= 218 | 如果 value 大于或等于218,返回 true,否则返回 false |
or | 或 | value = 20 or value = 30 | 如果 value 等于20或30,返回 true,否则返回 false |
and | 与 | value = 20 and age = 19 | 如果 value 等于20并且 age 等于30,返回 true,否则返回 false |
mod | 取余 | 10 mod 3 | 1 |
扩展
在 Chrome 中自动获取 XPath 代码
尽管 XPath 代码写起来要比正则表达式简单,但遇到复杂的节点,写起来仍然比较费劲;很多浏览器提供了自动获取 XPath 代码的能力,可以在自动获取 XPath 代码的基础上修改,很多时候甚至不需要修改就可以直接使用;eg:
通过 Chrome 浏览器中的开发者工具定位到指定元素,选择对应的 HTML 代码,然后右击,选择 Copy->Copy XPath 即可将当前选中的 HTML 代码对应的 XPath 代码复制到剪贴板
使用 Chrome 验证 XPath
在浏览器控制台输入对应代码可定位到指定元素(如果元素存在),eg:
$x('//*[@id="s_tab"]/div/b')
标签:XPath,lxml,value,html,print,节点 From: https://www.cnblogs.com/Y-wee/p/17032595.html $x 用来运行 XPath 函数,参数需要指定 XPath 代码