XPath,全称 XML Path Language,即 XML 路径语言。它使用路径表达式来选取 XML 文档中的节点或节点集。

Python 使用 XPath 要先安装 lxml 库

XPath的简单调用方法:

1
2
3
from lxml import etree
html = etree.HTML(源码) # 将源码转化为能被 XPath 匹配的格式
result = html.xpath(表达式) # 返回列表




节点、元素、标签的关系

将 HTML 文档视作树结构,每个 HTML 元素都是元素节点。节点还包括文本节点、属性节点等。

HTML 源码中被尖括号(<>)包起来的是标签,比如<head>、<body>。大多数标签都成对使用 <p></p>,成对标签和它们之间的一切形成一个元素,标签名就是元素名。




节点关系

  • a 节点包括 b 节点,则 a 就是 b 的父节点,b 就是 a 的子节点。
  • 拥有相同父节点的是同胞节点
  • 某节点的父,父的父,等等,是其先辈节点
  • 某节点的子,子的子,等等,是其后代节点

示例:

1
2
3
4
5
6
7
8
9
10
<bookstore>

<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>

</bookstore>

<bookstore><book> 的父节点

<title>、<author>、<year>、<price> 都是 <book> 的子节点

<title>、<author>、<year>、<price> 互相之间是同胞节点

<bookstore> 、<book><title> 的先辈节点

<book> 、 <title><bookstore> 的后代节点




语法

常用路径表达式

/ 从当前节点选取直接子节点,或对当前节点进行操作

// 从当前节点选取后代节点

.. 选取当前节点的父节点

@ 选取属性

示例:

/bookstore 选取根元素 bookstore

bookstore/book 选取 bookstore 元素的直接子元素中的 book 元素

//book 选取文档中的所有 book 元素

bookstore//book 选取 bookstore 元素的所有后代 book 元素

//@lang 选取文档中包含 lang 属性的所有元素


谓语

示例:

/bookstore/book[1] 选取 bookstore 子元素中的第一个 book 元素

/bookstore/book[last()] 选取 bookstore 子元素中的最后一个 book 元素

/bookstore/book[last()-1] 选取 bookstore 子元素中的倒数第二个 book 元素

//title[@lang] 选取所有拥有 lang 的属性的 title 元素

//title[@lang='eng'] 选取所有拥有 lang 属性且值为 eng 的 title 元素


通配符

示例:

/bookstore/* 选取 bookstore 元素的所有子元素

//* 选取文档中的所有元素

//title[@*] 选取所有带有属性的 title 元素


运算符

| 选取若干路径

示例:

//title | //price 选取文档中的所有 title 和 price 元素


属性

attribute::lang 选取当前节点的 lang 属性

不含某属性

//p[not(@class)] 选取不含 class 属性的所有 p 节点

//p[not(@class or @id)] 选取不含 class 属性和 id 属性的所有 p 节点


文本

/text() 获取节点中的文本




示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# python3
# -*- coding: utf-8 -*-
# Filename: xpath_demo.py
"""
练习使用 XPath 提取网页内容

@author: v1coder
"""

from lxml import etree
import requests

headers = {'User-Agent': ''}

def get_content(url):
data = requests.get(url, headers=headers).content
return data

# 简书
def jianshu():
url = 'https://www.jianshu.com/p/713415f82576'
data = get_content(url)
html = etree.HTML(data)
# p[not(@class)] 为不含 class 属性的 p 节点
texts = html.xpath('//p[not(@class)]/text()')
for text in texts[:-1]:
print(text)
#jianshu()
# 代码解释:
# //p 为所有 p 元素
# //p[not(@class)] 为所有不含 class 属性的 p 元素
# /text() 为获取当前元素的文本

# 盗版小说网站
def biqukan_com():
url = 'https://www.biqukan.com/1_1094/5403177.html'
data = get_content(url)
html = etree.HTML(data)
texts = html.xpath('//div[@id="content"]//text()')
for text in texts:
print(text)
#biqukan_com()
# 代码解释:
# //div[@id="content"] 为获取包含 id 属性且属性值为 content 的所有 div 元素
# //text() 为获取当前节点及所有后代节点的文本

# 豆瓣电影 TOP250
def top_250():
url = 'https://movie.douban.com/top250'
data = get_content(url)
html = etree.HTML(data)
# 第一个 span 节点用 span[1]
texts = html.xpath('//div[@class="hd"]/a/span[1]/text()')
for text in texts:
print(text)
#top_250()
# 代码解释:
# /a 为当前节点的直接子节点中的 a 节点
# /span[1] 为直接子节点中的第一个 span 节点

# 妹子图1
def mmjpg_com():
url = 'http://www.mmjpg.com/'
data = get_content(url)
html = etree.HTML(data)
# 获取属性 src 的值,用 attribute::src
urls = html.xpath('//img[@width="220"]/attribute::src')
for url in urls:
print(url)
#mmjpg_com()
# 代码解释:
# /attribute::src' 为获取当前元素的属性 src 的值

# 妹子图2
def haopic():
url = 'http://www.haopic.me/tag/meizitu'
data = get_content(url)
html = etree.HTML(data)
urls = html.xpath('//div[@class="post"]/a/img/attribute::src')
for url in urls:
print(url)
#haopic()
# 代码解释:
# //div 获取所有 div 元素
# '//div[@class="post"] 获取包含属性 class 且属性值为 post 的 div 元素
# /a 获取前面节点的直接子节点中的 a 节点
# /img 获取前面节点的直接子节点中的 img 节点
# /attribute::src 获取前面节点的属性 src 的值

# 妹子图3
def mzitu():
url = 'https://www.mzitu.com/'
data = get_content(url)
html = etree.HTML(data)
urls = html.xpath('//img[@width="236"]/attribute::data-original')
for url in urls:
print(url)
#mzitu()




获取网页某节点 xpath 的方法:

Chrome浏览器,打开开发者工具(Windows 快捷键 F12,Mac 快捷键 command+option+I)

在 elements 中找到想要获取 XPath 的标签,

单击右键,选择 Copy 》 Copy XPath,XPath路径就复制到剪切板了

或者:

把鼠标放在目标数据上,点击右键,点击”检查”

在弹出的 Elements 窗口,就已经定位到了该元素的位置

然后右键,选择 Copy 》 Copy XPath,XPath路径就复制到剪切板了




报错:

1
lxml.etree.XPathEvalError: Invalid predicate

通常是使用 xpath 的语法里方括号没写全,例如:

1
[@width="220"

解决方法:补全符号

1
[@width="220"]



鸣谢:
XPath 语法 - w3school
使用XPath - 静觅

2018-12-29