“干将莫邪” —— Xpath 与 lxml 库

图片来自 unsplash

前面的文章,我们已经学会正则表达式以及 BeautifulSoup库的用法。我们领教了正则表达式的便捷,感受 beautifulSoup 的高效。本文介绍也是内容提取的工具 —— Xpath,它一般和 lxml 库搭配使用。所以,我称这两者为“干将莫邪”。

1 Xpath 和 lxml

  • Xpath

XPath即为XML路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言。XPath 基于 XML 的树状结构,提供在数据结构树中找寻节点的能力。

Xpath 原本是用于选取 XML 文档节点信息。XPath 是于 1999 年 11 月 16 日 成为 W3C 标准。因其既简单方便又容易,所以它逐渐被人说熟知。

  • lxml

lxml 是功能丰富又简单易用的,专门处理 XML 和 HTML 的 Python 官网标准库。

2 Xpath 的语法

正则表达式的枯燥无味又学习成本高,Xpath 可以说是不及其万分之一。所以只要花上 10 分钟,掌握 Xpath 不在话下。Xpath 的语言以及如何从 HTML dom 树中提取信息,我将其归纳为“主干 - 树支 - 绿叶”。

2.1 “主干” —— 选取节点

抓取信息,我们需知道要从哪里开始抓取。因此,需要找个起始节点。Xpath 选择起始节点有以下可选:

表达式 描述
nodename 选取标签节点的所有子节点。
/ 从根节点选取,html DOM 树的节点就是 html。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选择当前节点,一般用于二级提取。
.. 选取当前节点的父节点,在二级提取时用到。
@ 选取属性。

我们通过以下实例来了解其用法:

路径表达式 描述
xpath('//div') 选取 div 元素的所有子节点。
xpath('/div') 选取 div 元素作为根节点。如果同级有多个 div ,那么所有 div 都会被选为根节点。
xpath('/div/span') 选取属于 div 元素<font color='red'>下</font>所有 span 元素节点。如果 span 有多个,也会被选中。
xpath('//div') 选取所有 div 元素节点,不管它们在文档的位置。
xpath('//div/span') 选取 div 元素<font color='red'>下</font>的所有 span 元素节点,不管位于 div 之下的什么位置
xpath("//@[class='content']") 选取包含属性 class 的值为 content 的节点,不管是 div 元素还是其他元素
xpath("//@[id='center']") 选取属性 id 的值为 center 的节点,不管是 div 元素还是其他元素

如果你对于提取节点没有头绪的时候,可以使用通配符来暂时替代。等查看输出内容之后再进一步确认。

路径表达式 描述
xpath('/div/*') 选取 div 元素节点<font color='red'>下</font>的所有节点
xpath('/div[@*]') 选取所有带属性的 div 元素节点

2.2 “分支” —— 关系节点与谓语

这一步的过程其实是通过起点一步步来寻找最终包含我们所需内容的节点。我们有时需要使用到相邻节点信息。因此,我们需要了解关系节点或者谓语。

  • 关系节点

一般而言,DOM 树中一个普通节点具有父节点、兄弟节点、子节点。当然也有例外的情况。这些有些节点比较特殊,可能没有父节点,如根节点;也有可能是没有子节点,如深度最大的节点。Xpath 也是有支持获取关系节点的语法。

关系 路径表达式 描述
parent(父) xpath('./parent::*') 选取当前节点的父节点
ancestor(先辈) xpath('./ancestor::*') 选取当前节点的所有先辈节点,包括父、祖父等
ancestor-or-self(先辈及本身) xpath('./ancestor-or-self::*') 选取当前节点的所有先辈节点以及节点本身
child(子) xpath('./child::*') 选取当前节点的所有子节点
descendant(后代) xpath('./descendant::*') 选取当前节点的所有后代节点,包括子节点、孙节点等
following xpath('./following ::*') 选取当前节点结束标签后的所有节点
following-sibing xpath('./following-sibing::*') 选取当前节点之后的兄弟节点
preceding xpath('./preceding::*') 选取当前节点开始标签前的所有节点
preceding-sibling xpath('./parent::*') 选取当前节点之前的兄弟节点
self(本身) xpath('./self::*') 选取当前节点本身
  • 谓语

谓语用来查找某个特定的节点或者包含某个指定的值的节点。同时,它是被嵌在方括号中的。

路径表达式 描述
xpath('./body/div[1]') 选取 body 元素节点<font color='red'>下</font>的第一个 div 子节。
xpath('./body/div[last()]') 选取 body 元素节点<font color='red'>下</font>的最后一个 div 子节。
xpath('./body/div[last()-1]') 选取 body 元素节点<font color='red'>下</font>的倒数第二个 div 子节。
xpath('./body/div[position()-3]') 选取 body 元素节点<font color='red'>下</font>的前二个 div 子节。
xpath('./body/div[@class]') 选取 body 元素节点<font color='red'>下</font>的所有带有 class 属性的 div 子节。
xpath("./body/div[@class='content']") 选取 body 元素节点<font color='red'>下</font>的 class 属性值为 centent 的 div 子节。

2.3"绿叶" —— 节点内容以及属性

到了这一步,我们已经找到所需内容的节点了。接下来就是获取该节点中的内容了。Xpath 语法提供了提供节点的文本内容以及属性内容的功能。

路径表达式 描述
text() 获取节点的本文内容
@属性 获取节点的属性内容

具体用法见以下实例:

路径表达式 描述
xpath('./a/text()') 获取当前节点<font color='purple'>中</font> a 元素节点<font color='red'>中</font>的本文内容
xpath('./a/@href') 获取当前节点<font color='purple'>中</font> a 元素节点<font color='red'>中</font>的 href 属性的内容

3 lxml 的用法

3.1 安装 lxml

pip 是安装库文件的最简便的方法,具体命令如下:

pip install lxml
# 如果出现因下载失败导致安装不上的情况,可以先启动 ss 再执行安装命令
# 或者在终端中使用代理
pip --proxy http://代理ip:端口 install lxml

3.2 使用 lxml

lxml 使用起来是比较简单的。我们首先要使用 lxml 的 etree 将 html 页面进行初始化,然后丢给 Xpath 匹配即可。具体用法如下:

from lxml import etree

html = requests.get(url)           # 使用 requests 请求网页
selector = etree.HTML(html.text)
content = selector.xpath('//a/text()')

没错,就这短短几行代码即可完成信息提取。
值得注意的是:xpath 查找匹配返回的类型有可能是一个值,也有可能是一个存放多个值的列表。这个取决于你的路径表达式是如何编写的。


上篇文章:详解 Requests 库的用法
推荐阅读:Python 多进程与多线程


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容