Python3 爬虫学习(五) - XPath 语法和 lxml 模块

什么是 XPath

XPath(XML Path Language) 是一门在 XML 和 HTML 文档中查找信息的语言,可用来在 XML 和 HTML 文档中对元素和属性进行遍历。

XPath 开发工具

1.Chrome 插件 XPath Helper
2.Firefox 插件 XPath Checker

XPath 语法

  • 选取节点

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。

表达式 描述 示例 结果
nodename 选取此节点的所有子节点 bookstore 选取 bookstore 下所有的子节点
/ 如果是在最前面,代表从根节点选取。否则选择某节点下的某个节点 /bookstore 选取根元素下所有的 bookstore 节点
// 从全局节点中选择节点,随便在哪个位置 //book 从全局节点中找到所有的 book 节点
@ 选取某个节点的属性 //book[@price] 选择所有 book 节点的 price 属性
  • 谓词
路径表达式 描述
/bookstore/book[1] 选取 bookstore 下的第一个子元素
/bookstore/book[last()] 选取 bookstore 下的最后一个 book 元素
bookstore/book[position()<3] 选取 bookstore 下前面两个子元素
//book[@price] 选取拥有 price 属性的 book 元素
//book[@price=10] 选取所有属性 price 等于 10 的 book 元素
//book/a/text() 选取所有 book 标签下的 a 标签的文本
  • 通配符

* 星号表示通配符

通配符 描述 示例 结果
* 匹配任意节点 /bookstore/* 选取 bookstore 下的所有子元素
@* 匹配节点中的任何属性 //book[@*] 选取所有带有属性的 book 元素
  • 选取多个路径

通过在路径表达式中使用 "|" 运算符,可以选取若干个路径。
示例如下:

//bookstore/book* | //book/title
# 选取所有 book 元素以及 book 元素下所有的 title 元素

  • 运算符
运算符 描述 实例 返回值
| 计算两个节点集 //book | //cd 返回所有拥有 book 和 cd 元素的节点集
+ 加法 6 + 4 10
- 减法 6 - 4 2
* 乘法 6 * 4 24
div 除法 8 div 4 2
= 等于 price=9.80 如果 price 是 9.80,则返回 true,否则返回 false
!= 不等于 price!=9.80 如果 price 不是 9.80,则返回 true,否则返回 false
< 小于 price<9.80 如果 price 小于 9.80,则返回 true,否则返回 false
<= 小于等于 price<=9.80 ...
> 大于 price>9.80 ...
>= 大于等于 price>=9.80 ...
or price=9.80 or price=9.70 ...
and price>9.00 and price<9.90 ...
mod 计算除法的余数 5 mod 2 1
  • contains

有时候某个属性包含多个值,就可以使用 contains 函数,示例代码如下:

//div[contains(@class, 'job_detail')

lxml 库

lxml 是一个 HTML/XML 的解析库,主要功能是如何解析和提取 HTML/XML 数据。

lxml 和 正则是一样,也是用 C 语言实现的,是一款高性能的 Python HTML/XML 解析器,可以利用之前学习的 XPath 语法,来快速定位特定元素以及节点信息。

lxml python 官方文档:http://lxml.de/index.html

可通过 pip 安装:pip install lxml

  • 基本使用

我们可以利用他来解析 HTML 代码,且在解析 HTML 代码的时候,如果 HTML 代码不规范,他会自动进行补全。示例代码如下:

from lxml.html import etree

htmlText = '''
<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>
'''

# 利用 etree.HTML,将字符串解析为 HTML 文档
html = etree.HTML(htmlText)

# 按字符串序列化 HTML 文档
result = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')

print(result)
  • 在文件中读取 html 代码

除了直接使用字符串进行解析,lxml 还支持从文件中读取内容。我们新建一个 hello.html 文件:

<!-- hello.html -->
<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>

然后利用 etree.parse() 方法来读取读取文件,如下:

from lxml.html import etree

html = etree.parse('files/hello.html')
result = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')
print(result)

另外,需要注意的是,使用 etree.parse() 方法解析 html 内容时,有时候会报 lxml.etree.XMLSyntaxError 的错,这是因为 etree.parse() 默认使用的是 XML 的解析器,所以当 html 内容不规范,比如出现某个标签缺少闭合标签时,就会报这个错误。这时,可使用 etree.HTMLParser() 创建一个 HTML 的解析器,然后作为 etree.parse() 方法的参数即可,如下:

htmlParser = etree.HTMLParser(encoding='utf-8')
html = etree.parse('files/hello.html', parser=htmlParser)
result = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')
print(result)
  • 在 lxml 中使用 XPath 语法

(1) 使用 xpath 语法,应该使用 Element.xpath 语法,来执行 xpath 的选择
(2) xpath 函数返回来的永远是一个列表

from lxml.html import etree

htmlParser = etree.HTMLParser(encoding='utf-8')
html = etree.parse('files/hello.html', parser=htmlParser)
# htmlText = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')
# print(htmlText)

lis = html.xpath('//li')
for li in lis:
    print(etree.tostring(li, encoding='utf-8', pretty_print=True).decode('utf-8'), end='')

aList = html.xpath('//a/@href')
for a in aList:
    print(a)

print('-----------------------')

lis = html.xpath('//li')
for li in lis:
    # . 号表示在当前的 li 元素下去匹配
    href = li.xpath('.//a/@href')[0]   #获取 a 标签的 href 属性
    txt = li.xpath('.//a/text()')[0]   #获取 a 标签的文本
    print(href, txt)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容