lxml + XPath 处理 HTML 实践
用 python 处理 HTML 文本时,re/pyquery 的可维护性都不高,尝试用 XPath
文档来源 XPath 教程
XPath 路径表达式
nodename
次节点的所有子节点/
从根节点选取//
从所有叶子节点选取.
选取当前节点..
选取当前节点父节点@
选取属性*
任何元素节点@*
任何属性节点node()
匹配任何类型的节点|
选取多个路径::
步轴名称::节点测试[谓语]
XPath 实例
bookstore
bookstore 元素的所有子节点/bookstore
选择根元素 bookstore//book
选择所有 book 叶子节点bookstore//book
bookstore 节点下的所有 book 叶子节点//@lang
名为 lang 的所有属性/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/bookstore/*
bookstore 的所有子元素//*
所有元素//title[@*]
带有 title 属性的所有元素//title | //price
所有 title / price 节点child::book
属于当前节点子元素的 book 节点attribute::lang
选取当前节点的 lang 属性child::*
选取当前节点所有子元素attribute::*
当前节点的所有属性child::text()
当前节点的所有文本子节点child::node()
当前节点的所有子节点descendant::book
当前节点的所有 book 后代ancestor::book
当前节点的所有 book 父节点ancestor-or-self::book
当前节点的所有 book 父节点 + 当前节点child::*/child::price
当前节点的所有子节点的 price 子节点(第二代子节点)
XPath 运算符
|
两个节点集+
-
*
div
加法 减法 乘法 除法=
!=
<
<=
>
>=
or
and
mod
mod 计算余数
函数
XPath 含有超过 100 个内建的函数。这些函数用于字符串值、数值、日期和时间比较、节点和 QName 处理、序列处理、逻辑值等等
简单 lxml + XPath 处理 HTML 代码
1 # encoding: utf-8
2 from StringIO import StringIO
3
4 from lxml import etree
5
6 broken_html = """<html>
7 <head>
8 <title>Tank</title>
9 </head>
10 <body>
11 <header>
12 <ul>
13 <li>Home</li>
14 <li>About</li>
15 </ul>
16 </header>
17 <div class="container">
18 <div class="row">
19 <div class="col-md-6">
20 <table>
21 <thead>
22 <th>ID</th>
23 <th>Name</th>
24 </thead>
25 <tbody>
26 <tr><td>1</td><td>Tank</td></tr>
27 <tr><td>2</td><td>Caption</td></tr>
28 <tr><td>3</td><td>Coco</td></tr>
29 </tbody>
30 </table>
31 </div>
32 <div class="col-md-6"></div>
33 </div>
34 </div>
35 <footer>Powered by Cooper</footer>
36 </body>
37 </html>
38 """
39
40 parser = etree.HTMLParser()
41 tree = etree.parse(StringIO(broken_html), parser)
42
43
44 def t_xpath():
45 x_exp = "//div[@class='col-md-6']"
46 print 'xpath', x_exp
47 for foot in tree.xpath(x_exp):
48 print foot.tag, foot.getparent().attrib
49
50 x_exp = '//tr/td[2]'
51 print 'xpath', x_exp
52 for td in tree.xpath(x_exp):
53 print td.tag, td.text
54
55 x_exp = '//tr/td[1][text()>1]'
56 print 'xpath', x_exp
57 for td in tree.xpath(x_exp):
58 print td.tag, td.text
59
60 x_exp = '//div[@class]'
61 print 'xpath', x_exp
62 for td in tree.xpath(x_exp):
63 print td.tag, td.attrib
64
65
66 def pretty_print():
67 result = etree.tostring(
68 tree.getroot(),
69 pretty_print=True,
70 method="html")
71 print result
72
73 html = etree.HTML(broken_html)
74 print etree.tostring(html, pretty_print=True, method='html')
75
76
77 # pretty_print()
78 t_xpath()
输出
xpath //div[@class='col-md-6']
div {'class': 'row'}
div {'class': 'row'}
xpath //tr/td[2]
td Tank
td Caption
td Coco
xpath //tr/td[1][text()>1]
td 2
td 3
xpath //div[@class]
div {'class': 'container'}
div {'class': 'row'}
div {'class': 'col-md-6'}
div {'class': 'col-md-6'}
未完待续
blog comments powered by Disqus