Xpath是一门在XML文档中查找信息的语言,对HTML文档也有很好的支持。
1 节点选择
Xpath使用路径表达式在XML文档中选取节点。节点是通过沿着路径或者step来选取的。
表达式 | 描述 | 示例 | 注释 |
---|---|---|---|
nodename | 选取此节点的所有子节点 | xpath(‘//div’) | 选取div节点的所有子节点 |
/ | 从根节点选取 | xpath(‘/div’) | 从根节点上选取div节点 |
// | 选取所有的当前节点,不考虑他们的位置 | xpath(‘//div’) | 选取所有的div节点 |
. | 选取当前节点 | xpath(‘./div’) | 选取当前节点下的div节点 |
.. | 选取当前节点的父节点 | xpath(‘..’) | 回到上一个节点 |
@ | 选取属性 | xpath(’//@calss’) | 选取所有的class属性 |
2 谓语
Xpath语法中的谓语用来查找某个特定的节点或者包含某个指定值的节点,谓语被嵌在方括号中。常见的谓语如下:
表达式 | 结果 |
---|---|
xpath(‘/body/div[1]’) | 选取body下的第一个div节点 |
xpath(‘/body/div[last()]’) | 选取body下最后一个div节点 |
xpath(‘/body/div[last()-1]’) | 选取body下倒数第二个div节点 |
xpath(‘/body/div[positon()<3]’) | 选取body下前两个div节点 |
xpath(‘/body/div[@class]’) | 选取body下带有class属性的div节点 |
xpath(‘/body/div[@class=”main”]’) | 选取body下class属性为main的div节点 |
xpath(‘/body/div[price>35.00]’) | 选取body下price元素值大于35的div节点 |
3 通配符
Xpath中也可以使用通配符来选取位置的元素,常用的就是“ * ” 通配符,它可以匹配任何元素节点。
表达式 | 结果 |
---|---|
xpath(’/div/*’) | 选取div下的所有子节点 |
xpath(‘/div[@*]’) | 选取所有带属性的div节点 |
4 取多个路径
使用“|”运算符可以同时选取多个路径
表达式 | 结果 |
---|---|
xpath(‘//div|//table’) | 选取所有的div和table节点 |
5 Xpath轴
轴可以定义相对于当前节点的节点集。
轴名称 | 表达式 | 描述 |
---|---|---|
ancestor | xpath(‘./ancestor::*’) | 选取当前节点的所有先辈节点(父、祖父) |
ancestor-or-self | xpath(‘./ancestor-or-self::*’) | 选取当前节点的所有先辈节点以及节点本身 |
attribute | xpath(‘./attribute::*’) | 选取当前节点的所有属性 |
child | xpath(‘./child::*’) | 返回当前节点的所有子节点 |
descendant | xpath(‘./descendant::*’) | 返回当前节点的所有后代节点(子节点、孙节点) |
following | xpath(‘./following::*’) | 选取文档中当前节点结束标签后的所有节点 |
following-sibing | xpath(‘./following-sibing::*’) | 选取当前节点之后的兄弟节点 |
parent | xpath(‘./parent::*’) | 选取当前节点的父节点 |
preceding | xpath(‘./preceding::*’) | 选取文档中当前节点开始标签前的所有节点 |
preceding-sibling | xpath(‘./preceding-sibling::*’) | 选取当前节点之前的兄弟节点 |
self | xpath(‘./self::*’) | 选取当前节点 |
6 功能函数
使用功能函数能够更好的进行模糊搜索。
函数 | 用法 | 解释 |
---|---|---|
starts-with | xpath(‘//div[starts-with(@id,”ma”)]‘) | 选取id值以ma开头的div节点 |
contains | xpath(‘//div[contains(@id,”ma”)]‘) | 选取id值包含ma的div节点 |
and | xpath(‘//div[contains(@id,”ma”) and contains(@id,”in”)]‘) | 选取id值包含ma和in的div节点 |
text() | xpath(‘//div[contains(text(),”ma”)]‘) | 选取节点文本包含ma的div节点 |
7 xpath使用技巧
在爬虫实战中,Xpath路径也可以通过浏览器复制得到,同selector选择器中介绍的方法。
当需要进行批量爬取时,类似于BeautifulSoup中的selector()方法删除谓语部分是不可行的。这时的思路为“先抓大后抓小,寻找循环点”。以从糗事百科的网站爬取用户id为例,打开浏览器进行“检查”,通过“三角形符号”折叠元素,找到每个段子完整的信息标签。
(1)首先通过复制构造div标签路径,此时的路径为:
'div[@class="article block untagged mb15"]'
这样就定位到了每个段子信息,这就是“循环点”。
(2)通过谷歌浏览器进行“检查”定位用户ID,复制Xpath到记事本中:
//*[@id="qiushi_tag_121251148"]/div[1]/a[2]/h2
因为第一部分为循环部分,将其删除得到:
div[1]/a[2]/h2
这便是用户ID的信息。
完整获取用户ID的代码如下:
import requests
from lxml import etree
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
url = 'https://www.qiushibaike.com/text/'
r = requests.get(url, headers=headers)
selector = etree.HTML(r.text)
url_infos = selector.xpath('//div[@class="article block untagged mb15"]')
for url_info in url_infos:
user_id = url_info.xpath('div[1]/a[2]/h2/text()')[0] #尾部加上text()获取文本
print(user_id)
starts-with()
有时候会遇到相同的字符开头的多个标签:
<li class="tag-1">需要的内容 1</li>
<li class="tag-2">需要的内容 2</li>
<li class="tag-3">需要的内容 3</li>
想同时爬取时,不需要构造多个Xpath路径,通过starts-with()便可获取多个便签内容,接上例,目前糗事百科按上例的方法已经行不通了,看下图:
可以发现在 **article block untagged mb15 **的后面增加了typs_long、typs_hot、typs_old等,还按上例那样是匹配不到标签的,这时候就可以用到starts-with(),来提取属性类似的标签信息。
代码改变如下:
import requests
from lxml import etree
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
url = 'http://www.qiushibaike.com/text'
r = requests.get(url, headers=headers)
selector = etree.HTML(r.text)
url_infos = selector.xpath('//div[starts-with(@class,"article block untagged mb15")]')
for url_info in url_infos:
try:
user_id = url_info.xpath('div[1]/a[2]/h2/text()')[0] #尾部加上text()获取文本
except IndexError:
user_id = '匿名用户' #此处遇到匿名用户会抛出IndexError
print(user_id)
string(.)
当遇到标签套标签情况时:
<div class="red">需要的内容 1
<h1>需要的内容2</h1>
</div>
想同时爬取文本内容,可以通过string(.)完成:
from lxml import etree
html2 = '''
<div class="red">需要的内容 1
<h1>需要的内容2</h1>
</div>
'''
selector = etree.HTML(html2)
content1 = selector.xpath('//div[@class="red"]')[0]
content2 = content1.xpath('string(.)')
print(content2) #string(.)方法可用于标签套标签情况