Python爬虫之数据解析(XPath)

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历,而将 HTML文档转换成 XML文档后,就可以用 XPath 查找 HTML 节点或元素。

XML 文档的特点:
XML 文档中的每个成分都是一个节点。整个文档是一个根节点;每个 XML 标签是一个元素节点;包含在 XML 元素中的文本是文本节点;每一个 XML 属性是一个属性节点;注释则属于注释节点
其中,元素节点可以包含任意的元素节点、文本节点或属性节点,而文本节点或属性节点则不能包含节点。

XPath语法的特点:
XPath 使用路径表达式在 XML 文档中进行导航
XPath 包含一个标准函数库
XPath 表达式可返回节点集、字符串、逻辑值以及数字。

常用的XPath 开发工具:
1.开源的XPath表达式编辑工具:XMLQuire(仅XML格式文件可用)。
2.Firefox插件 Try XPath。
3.Chrome插件 XPath Helper。推荐!可以直接在网页上使用xpath定位元素,非常方便。(安装详情见参考文献2

一、XPath语法介绍

1. 选取节点

下表列出了常用的路径表达式:

表达式 描述
nodename 选取此节点的所有子节点
/ 如果在路径最前面代表从根节点选取,否则表示选择某节点的子节点
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 表示当前节点
.. 表示当前节点的父节点
@href 节点href属性的值(/@href:当前节点href属性的值;//@href:当前节点和子孙节点href属性的值)
text() 节点的文本内容(/text():当前节点的文本内容;//text():当前节点和子孙节点的文本内容)

2.谓语

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

  1. /bookstore/book[1]:选取属于 bookstore 子元素的第一个 book 元素。注意位置顺序是从1开始的,和python不一样!
  2. /bookstore/book[last()]:选取属于 bookstore 子元素的最后一个 book 元素。
  3. /bookstore/book[last()-1]:选取属于 bookstore 子元素的倒数第二个 book 元素。
  4. /bookstore/book[position()<3]:选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
  5. //title[@lang]:选取拥有 lang 属性的 title 元素。
  6. //title[@lang='eng']:选取 lang属性值为'eng'的 title 元素。
  7. /bookstore/book[@price>35.00]:选取 bookstore子元素中满足条件的book 元素,条件:price属性的值须大于 35.00。

3.XPath的运算符
  1. 加法:+
  2. 减法:-
  3. 乘法:*
  4. 除法:div
  5. =:等于
  6. !=:不等于
  7. <:小于
  8. <=:小于或等于
  9. >:大于
  10. >:大于或等于
  11. or:或
  12. and:与
  13. mod:计算除法的余数

4.XPath的轴

轴可定义相对于当前节点的节点集。
语法:轴名称::节点[谓语]

常用的轴:

  1. ancestor:选取当前节点的所有先辈节点(父、祖父等)。
  2. ancestor-or-self:选取当前节点的所有先辈节点(父、祖父等)以及当前节点本身。
  3. attribute:选取当前节点的所有属性。
  4. self:选取当前节点。
  5. child:选取当前节点的所有子节点。
  6. parent:选取当前节点的父节点。
  7. descendant:选取当前节点的所有后代节点(子、孙等)。
  8. descendant-or-self:选取当前节点的所有后代节点(子、孙等)以及当前节点本身。

示例:

  1. //li[@data="one"]/ancestor::div:选取属性data="one"的li节点的所有div祖先节点。
  2. //li[@data="one"]/ancestor::*:选取属性data="one"的li标签的所有祖先节点。
  3. //div[@id="testid"]/attribute::*:选取id="testid"的div节点的所有属性值。
  4. //div[@id]/self::div[@data-h]/attribute::*:选取含id属性和data-h属性的div标签的所有属性值
  5. //div[@id="testid"]/child::*:选取id="testid"的div节点的所有子节点。
  6. //li[@data="one"]/parent::ol/li[last()]/text():选取属性data="one"的li节点的父节点ol,其最后一个li子节点的文本值。
    注意:由于每个元素节点只有唯一的一个父节点,所以“parent::父节点”等价于“parent::*” 。

5.XPath常用函数
  1. contains ()函数:
    //div[ contains(@class, 'in') ]:选择class属性值中包含有’in’字符串的div节点。
  2. text()函数:节点的文本值
    //a[text()='baidu']:选取文本值等于'baidu'的a节点。
    //a/text():获取a节点文本内容。
  3. last()函数:
    /bookstore/book[last()]:选取属于 bookstore 子节点的最后一个 book 节点。
  4. position()函数:返回节点的索引位置(从1开始)
    /bookstore/book[position()<=3]:选取属于 bookstore 子节点的前三个 book 节点。
  5. starts-with()函数:
    //div[starts-with(@class,'in')]:选择class属性值以字符串’in’开头的div节点。
  6. ends-with()函数:
    //div[ends-with(@class,'in')]:选择class属性值以字符串’in’结尾的div节点。
  7. not()函数:表示否定
    //input[@name=‘identity’ and not( contains(@class,‘a’) )]:
    选择属性name=‘identity’ 并且 class属性值中不包含字符’a’的input节点。
    not()函数通常与返回值为true or false的函数组合起来用,比如contains(),starts-with()等。但有一种特别情况需注意一下://input[@id]:匹配出含有id属性的input节点;//input[not(@id)]:匹配出不含有id属性的input节点。

6.其他

1.通配符
使用通配符“*”可用来选取未知的 XML 元素。
*:匹配任何元素节点。
@*:匹配任何属性节点。
node():匹配任意类型的节点(元素、属性、文本、注释以及根节点)。

2.选取多条路径
通过在路径表达式中使用“|”运算符,可以选取多条路径。
//book/title | //book/price:选取 book 元素的所有 title 和 price 元素。
//title | //price:选取文档中的所有 title 和 price 元素。


二、xpath在python中的具体运用

lxml库是一个XML、HTML的解析器,主要用于解析和提取XML、HTML数据。lxml库先将HTML文档解析,然后就可以使用XPath 搜索或遍历HTML文档中的节点。

使用lxml解析HTML数据的两种方式:

1.解析HTML字符串:

from lxml import etree

# HTML字符串
text = '''
<bookstore>
  <book>
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <name>杰克罗琳</name>
    <year>2005</year>
    <price>29.99</price>
  </book>
</bookstore>
'''

# 使用HTML()方法解析字符串
# HTML()默认使用的就是HTML解析器,如果遇到不规范的HTML代码,会自动补全。
html_element = etree.HTML(text)

# 解析后就可以调用xpath方法了
name = html_element.xpath('//book/name/text()')
print(name)


# 返回值:
# ['杰克罗琳']

2.直接解析HTML文件:

from lxml import etree

# 创建HTML解析器,指定解析器使用的编码格式(需要和文件编码格式一致)
parser = etree.HTMLParser(encoding='utf-8')

# parse()默认的是XML解析器,在解析HTML代码时,如果HTML代码不规范,则会报错!
# 因此需要指定解析器为HTML解析器,这样不规范的HTML代码,会自动补全!
html_element = etree.parse('./test.html', parser=parser)

#解析后就可以调用xpath方法了
name = html_element.xpath('//book/name/text()')
print(name)


# 返回值:
# ['杰克罗琳']

注意:
1.xpath返回的永远是列表,即使没有匹配到任何元素也返回的是空列表。
2.根节点和任意元素节点都可以使用xpath方法去搜索、遍历其子节点。

# tostring()方法可以将节点对象转换成字节,解码后就可以查看节点内容
author_ele = html_element.xpath('//book/author')[0]
result = etree.tostring(author_ele, encoding='utf-8').decode('utf-8')
print(result)


# 返回值:
# <author>J K. Rowling</author>&#13;

参考文献:

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