xpath(XML Path Language)是一门在XML和HTML文档中查找信息的语言,可用来在XML和HTML文档中对元素和属性进行遍历。
XML文档中的节点跟HTML是一样的,用xpath来解析网页数据是非常合适的。
Chrome插件XPath Helper
Firefox插件Try XPath
使用//标签名 获取整个页面当中的某个节点元素,然后通过子节点、属性、谓词进行提取。
'//div[@class="abc"]'
/如果是在最前面,代表从根节点选取,否则选择某节点下的节点。
/代表只获取直接子节点,//是获取子孙节点。
'//div[@class="abc"]/a[2]' 表示在整个页面(根节点)寻找class="abc"的div标签下的第二个a标签
'/p[not(@*)]' 不包含任何属性
'//div/a[not(@class)]' 不包含class属性
相关文档
xpath介绍
关于xml的介绍
xml-dom-节点
XPath使用路径表达式来选取XML文档中的节点或节点集。
节点是通过沿着路径(path)或者步(steps)来选取的。
表达式 | 作用 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取某个节点的属性。 |
例子 | 说明 |
---|---|
bookstore | 选取bookstore元素下的所有子节点。 |
/bookstore | 选取根元素下所有的bookstore节点;注:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
bookstore/book | 选取属于bookstore的子元素的所有book元素。 |
//book | 选取所有book节点,而不管它们在文档中的位置。 |
bookstore//book | 选择属于bookstore元素的后代的所有book元素,而不管它们位于bookstore之下的什么位置。 |
//@lang | 选取名为lang的所有属性。 |
./a | 选取当前节点下的a标签。 |
谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中。
表达式 | 说明 |
---|---|
/bookstore/book[1] | 选取bookstore下的第一个book子元素。 |
/bookstore/book[last()] | 选取属于bookstore子元素的最后一个book元素。 |
/bookstore/book[last()-1] | 选取bookstore下的倒数第二个book子元素。 |
/bookstore/book[position() < 3] | 选取bookstore下前面两个book子元素。 |
//book[@price] | 选取所有拥有price属性的book元素。 |
//book[@price=10] | 选取所有属性price等于10的book元素。 |
/bookstore/book[price>35.00] | 选取bookstore元素的所有book元素,且其中的price元素的值须大于35.00。 |
/bookstore/book[price>35.00]/title | 选取bookstore元素中的book元素的所有title元素,且其中的price元素的值须大于35.00。 |
注意:谓词中的下标是从1开始的,不是从0开始的
例子 | 说明 |
---|---|
* | 匹配任意元素节点。 |
@* | 匹配节点中的任何属性节点(只要带有属性的节点都会被匹配)。 |
node() | 匹配任何类型的节点。 |
– | – |
/bookstore/* | 选取bookstore元素下的所有子元素。 |
//* | 选取文档中的所有元素。 |
//book[@*] | 选取所有带有属性的book元素。 |
通过在路径表达式中使用“|”运算符,可以选取若干个路径。
例子 | 说明 |
---|---|
//bookstore/book | //book/title | 选取所有book元素以及book元素下所有的title元素。 |
//title | //price | 选取文档中的所有title和price元素。 |
/bookstore/book/title | //price | 选取属于bookstore元素的book元素的所有title元素,以及文档中所有的price元素。 |
XPath 表达式可返回节点集、字符串、逻辑值以及数字。
多个属性条件可以用'//div[@属性1="" and 属性2=""]'这种方式
属性还可以用 = != > < 这种运算符,具体可以参考一些文档
'//tr[position()>1 and position()<12]' 获取第2个到第12个tr标签
运算符 | 作用 | 例子 | 说明 |
---|---|---|---|
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。 |
mod | 计算除法的余数(取余) | 5 mod 2 | 结果是 1 |
其他
+ - * div(除法) = != < <= > >=
函数 | 用法 | 解释 |
---|---|---|
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节点 |
contains
某个元素的属性包含了多个值,可以使用contains函数(模糊匹配)
例如在网页中某标签的属性:class=“job_detail container gclearfix”
用xpath匹配:
'//div[contains(@class, "job_detail")]'
# 或者
'//div[contains(@class, "container gclearfix")]'
# 都可以匹配到
text()
在selenium中使用find_element_by_xpath() 这种方法时,经常会无法使用text()
selenium以定位为主,需要获取网页中文本、标签属性值以及一些元素时,可以先定位到标签上,再用etree解析
selenium之后会讲到
lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则一样,也是用C实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用XPath语法,来快速的定位特定元素以及节点信息。
lxml python 官方文档:http://lxml.de/index.html
需要安装C语言库,可使用pip安装:pip install lxml
1、解析html字符串:使用’lxml.etree.HTML’进行解析
我们可以利用它来解析HTML代码,并且在解析HTML代码的时候,如果HTML代码不规范,会自动进行补全。
<div>
<ul>
<li class="item-0"><a href="link1.html">first itema>li>
<li class="item-1"><a href="link2.html">second itema>li>
<li class="item-inactive"><a href="link3.html">third itema>li>
<li class="item-1"><a href="link4.html">fourth itema>li>
<li class="item-0"><a href="link5.html">fifth itema>
ul>
div>
<html><body><div>
<ul>
<li class="item-0"><a href="link1.html">first itema>li>
<li class="item-1"><a href="link2.html">second itema>li>
<li class="item-inactive"><a href="link3.html">third itema>li>
<li class="item-1"><a href="link4.html">fourth itema>li>
<li class="item-0"><a href="link5.html">fifth itema>
li>ul>
div>body>html>
2、解析html文件:使用’lxml.eterr.parse’进行解析
除了直接使用字符串进行解析,lxml还支持从文件中读取内容。
这个方法默认使用的是’XML’解析器,如果碰到一些不规范的html代码 会出现解析错误,这时就要自己创建-定义html解析器
from lxml import etree # 引入模块
parser = etree.HTMLParser(encoding='utf-8')
# .parse默认是xml解析器,所以要自己定义解析器,防止遇到不规范的html代码导致解析错误
html = etree.parse('tencent.html', parser=parser)
# 返回的是etree对象,之后的操作都可以通过html.操作
# 解析文件用.parse(),如果解析网页返回的html或者粘贴到python代码里的字符串,用.HTML()
print(etree.tostring(html, encoding='utf-8').decode('utf-8')) # 解码
# 通过tostring方法可以把解析后的HTML文档 按字符串序列化,不解码是字节数据
解析结果也是经过规范化的,会自动加上一些标准的HTML标签
案例
trs = html.xpath('//tr')
print(trs)
for tr in trs:
# xpath返回的是一个列表,这里需要遍历并使用tostring方法,指定编码格式 解码
print(etree.tostring(tr, encoding='utf-8').decode('utf-8'))
tr = html.xpath('//tr[2]')[0]
print(tr)
print(etree.tostring(tr, encoding='utf-8').decode('utf-8'))
tr[2]表示只取第二个tr标签,[2]是谓词。
因为xpath返回的是个列表,后面加个[0]相当于直接把它匹配到的第一个值取出来了:取零。
因为需要匹配的结果只有一个,只需要第一个符合条件的;所以可以用这种取零方式直接取值。
html代码里是已经确定有要查找的tr标签,所以这里不用担心列表越界
trs = html.xpath('//tr[@class="even"]')
for tr in trs:
print(etree.tostring(tr, encoding='utf-8').decode('utf-8'))
aList = html.xpath('//a[@target="_blank" and @href]')
for a in aList:
print(etree.tostring(a, encoding='utf-8').decode('utf-8'))
'''
用[]是代表获取拥有某个属性的某个标签
用/或者//是直接取出某个标签下某个属性所对应的值
'''
aList = html.xpath('//a[@target="_blank"]/@href')
for a in aList:
print('https://hr.tencent.com/' + a)
# 这里不需要指定编码格式再解码了,因为获取到的是字符串(href里面的url链接)
# trs = html.xpath('//tr[position()>1 and @class!="f"]')
'''position()是设置tr标签出现位置,设置需要取的范围'''
# trs = html.xpath('//tr[position()>1 and position()<12]')
trs = html.xpath('//tr[@class!="h" and @class!="f"]')
positions = []
for tr in trs:
# 在某个标签下,再次执行xpath函数,获取这个标签下的子孙元素
# 应该在//或/前面加.,代表是在当前元素下获取,不加.的话就会在整个网页代码寻找匹配的标签
href = tr.xpath('.//a/@href')[0] # 两个斜杠//代表获取子孙节点(在整个tr标签下查找a标签)
url = 'https://hr.tencent.com/' + href
title = tr.xpath('./td[1]//text()')[0]
category = tr.xpath('./td[2]/text()')[0]
nums = tr.xpath('./td[3]/text()')[0]
address = tr.xpath('./td[4]/text()')[0]
# time = tr.xpath('./td[5]/text()')[0]
time = tr.xpath('./td[5]')[0].text
'''
td标签是在tr标签子节点的,所以一个斜杠即可./td[1]代表获取第一个
.text和text()可以拿到标签里面的文本
'''
position = {
'url': url,
'职位': title,
'分类': category,
'工作经验': nums,
'地区': address,
'发布时间': time
}
positions.append(position)
print(positions)
总结:
lxml结合xpath注意事项:
如:html.xpath('//tr[position()>1]')
获取某个标签的属性:用/或者//这种路径方式,不要用[],[]匹配到的是整个标签代码
获取标签里面文本是通过xpath中的text()方法,文本指的是:<开始标签> 这里的文本 结束标签>
如:a标签里的文本 h1标签里文本
...
案例中的html文件
<table class="tablelist" cellpadding="0" cellspacing="0">
<tbody><tr class="h">
<td class="l" width="374">职位名称td>
<td>职位类别td>
<td>人数td>
<td>地点td>
<td>发布时间td>
tr>
<tr class="even">
<td class="l square"><a target="_blank" href="position_detail.php?id=45570&keywords=&tid=87&lid=0">22989-腾讯云web前端高级工程师a>td>
<td>技术类td>
<td>2td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=45567&keywords=&tid=87&lid=0">21882-腾讯医典Android开发工程师(深圳)a>td>
<td>技术类td>
<td>1td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="even">
<td class="l square"><a target="_blank" href="position_detail.php?id=45566&keywords=&tid=87&lid=0">21882-腾讯医典IOS开发工程师(深圳)a>td>
<td>技术类td>
<td>1td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=45555&keywords=&tid=87&lid=0">18427-理财通后台开发工程师a><span class="hot"> span>td>
<td>技术类td>
<td>1td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="even">
<td class="l square"><a target="_blank" href="position_detail.php?id=45545&keywords=&tid=87&lid=0">TEG13-高级系统测试工程师(深圳)a>td>
<td>技术类td>
<td>1td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=45547&keywords=&tid=87&lid=0">SD9-手游客户端开发工程师(深圳)a><span class="hot"> span>td>
<td>技术类td>
<td>1td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="even">
<td class="l square"><a target="_blank" href="position_detail.php?id=45554&keywords=&tid=87&lid=0">PCG04-PCG研发部前端架构师(深圳)a>td>
<td>技术类td>
<td>1td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=45544&keywords=&tid=87&lid=0">PCG17-QQ钱包客户端开发(深圳)a>td>
<td>技术类td>
<td>1td>
<td>深圳td>
<td>2018-11-11td>
tr>
<tr class="even">
<td class="l square"><a target="_blank" href="position_detail.php?id=45534&keywords=&tid=87&lid=0">22989-高级AI后台开发工程师(上海/深圳)a>td>
<td>技术类td>
<td>2td>
<td>上海td>
<td>2018-11-11td>
tr>
<tr class="odd">
<td class="l square"><a target="_blank" href="position_detail.php?id=45535&keywords=&tid=87&lid=0">22989-高级AI前端研发工程师(上海/深圳)a>td>
<td>技术类td>
<td>2td>
<td>上海td>
<td>2018-11-11td>
tr>
<tr class="f">
<td colspan="5">
<div class="left">共<span class="lightblue total">1379span>个职位div>
<div class="right"><div class="pagenav"><a href="javascript:;" class="noactive" id="prev">上一页a><a class="active" href="javascript:;">1a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=10#a">2a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=20#a">3a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=30#a">4a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=40#a">5a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=50#a">6a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=60#a">7a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=70#a">...a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=1370#a">138a><a href="position.php?lid=&tid=87&keywords=请输入关键词&start=10#a" id="next">下一页a><div class="clr">div>div>div>
<div class="clr">div>
td>
tr>
tbody>table>