python3 lxml
python 库安装 lxml
#pip安装
pip3 install lxml
#wheel安装
#下载对应系统版本的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml
pip3 install lxml-4.2.1-cp36-cp36m-win_amd64.whl
yum install -y epel-release libxslt-devel libxml2-devel openssl-devel
pip3 install lxml
xpath 常用规则
表达式 |
描述 |
nodename |
选取此节点的所有子节点 |
/ |
从当前节点选取直接子节点 |
// |
从当前节点选取子孙节点 |
. |
选取当前节点 |
.. |
选取当前节点的父节点 |
@ |
选取属性 |
* |
通配符,选择所有元素节点与元素名 |
@* |
选取所有属性 |
[@attrib] |
选取具有给定属性的所有元素 |
[@attrib='value'] |
选取给定属性具有给定值的所有元素 |
[tag] |
选取所有具有指定元素的直接子节点 |
[tag='text'] |
选取所有具有指定元素并且文本内容是text节点 |
xpath 中的运算符
运算符 |
描述 |
实例 |
返回值 |
or |
或 |
age=19 or age = 20 |
如果 age 等于 19 或者等于 20 则返回 True, 否则返回 False |
an |
与 |
age > 19 and age < 22 |
如果 age 大于 19 小于 22 则返回 True, 否则返回 False |
mod |
取余 |
5 mod 2 |
取 5 除以 2 的余数 1 |
丨【管道符】 |
取两个节点的集合 |
//book 丨【管道符】 //cd |
返回拥有 book 和 cd 元素的节点集合 |
+, -, *, div |
加, 减, 乘, 除 |
8 加/减/乘/除 4 |
12/4/32/2 |
=, !=, <, <=, >, >= |
等于, 不等于, 小于, 小于等于, 大于, 大于等于 |
age =/ != / < / <= / > / >= 19 |
age 等于 / 不等于 / 小于 / 小于等于 / 大于 / 大于等于 19, 则返回 True, 否则返回 False |
HTML解析
<head><title>The Dormouse's story</title></head>
<p class="story">
this is P label
<a href="http://www.baidu.com" class="baidu" id="link1"><span>baidu</span></a><span>this is span</span>
<a href="http://www.cnblogs.com" class="cnblogs" id="link2"><span>cnblogs</span></a>
</p>
<div>
<ul>
<li class="item-0 li" name="item0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
<li class="aaa li-aaa"><a href="link6.html">aaaaa item</a></li>
<li class="li li-first" name="item6"><a href="link6.html"><span>six item</span></a></li>
<li class="li li-first" name="item7"><a href="link7.html"><span>seven item</span></a></li>
</ul>
<ul class="ul2">
<li class="item-10 li" name="item10"><a href="link10.html">10 item</a></li>
<li class="item-11 li" name="item11"><a href="link11.html">11 item</a></li>
<li class="item-12 li" name="item12"><a href="link12.html">12 item</a></li>
<li class="item-13 li" name="item13"><a href="link13.html">13 item</a></li>
<li class="item-14 li" name="item14"><a href="link14.html">14 item</a></li>
<li class="item-15 li" name="item15"><a href="link15.html">15 item</a></li>
<li class="item-16 li" name="item16"><a href="link16.html">16 item</a></li>
</ul>
</div>
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">第一个</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-0"><a href="link5.html">a属性</a>
</ul>
</div>
'''
html = etree.HTML(text) # 初始化生成一个XPath解析对象
result = etree.tostring(html, encoding='utf-8') # 解析对象输出代码
print(type(html))
print(type(result))
print(result.decode('utf-8'))
from lxml import etree
html_path = os.path.join(os.path.dirname(os.getcwd()), 'html_dir', 'test_lxml.html')
html = etree.parse(html_path, etree.HTMLParser()) #指定解析器HTMLParser会根据文件修复HTML文件中缺失的如声明信息
result = etree.tostring(html) # 解析成字节
# result=etree.tostringlist(html) # 解析成列表
print(type(html))
print(type(result))
print(result)
获取节点
html = etree.parse(html_path, etree.HTMLParser())
# 从 HTML 节点选取所有的子孙节点
result = html.xpath('//*')
print(f'选择 HTML 节点及所有子孙节点 返回的数据类型: {type(result)}, 返回节点长度: {len(result)}')
print(f'选择 HTML 节点及所有子孙节点: {result}')
html = etree.parse(html_path, etree.HTMLParser())
# 从 li 节点选取所有的直接子节点 a 节点
result = html.xpath('//li/a')
print(f'选择所有 li 节点下的 a 节点 返回的数据类型: {type(result)}, 返回节点长度: {len(result)}')
print(f'选择所有 li 节点下的 a 节点 返回的: {result}')
html = etree.parse(html_path, etree.HTMLParser())
# 获取所有 li 节点节点的直接父节点
result = html.xpath('//li/..')
print(f'获取所有 li 节点的直接父节点 返回的数据类型: {type(result)}, 返回节点长度: {len(result)}')
print(f'获取所有 li 节点的直接父节点 返回的: {result}')
html = etree.parse(html_path, etree.HTMLParser())
# 获取所有 属性class='item-1' 的所有 li 节点
result = html.xpath("//li[@class='item-1']")
print(f"获取所有 属性 class='item-1' 的所有 li 节点 返回的: {result}")
# 获取所有 属性name='item0' 的所有 li 节点
result = html.xpath("//li[@name='item0']")
print(f"获取所有 属性 name='item0' 的所有 li 节点 返回的: {result}")
获取文本或属性
html = etree.parse(html_path, etree.HTMLParser())
# 获取 p 节点文本
result = html.xpath("//p[@class='story']/text()")
print(f'属性 class="story" 的 p 节点的文本: {result}')
html = etree.parse(html_path, etree.HTMLParser())
# 获取属性 class=story 的节点 p 下面所有节点 a 的 id 属性
result = html.xpath("//p[@class='story']/a/@id")
print(f'获取属性 class=story 的节点 p 下面所有节点 a 的 id 属性: {result}')
- 属性多值匹配
- 如果某个属性的值有多个时,我们可以使用contains()函数来获取
- contains 方法单独使用时只会匹配到第一个节点, 如果有多个不会继续匹配后续的节点
html = etree.parse(html_path, etree.HTMLParser())
# 匹配 class 属性中包含 aaa 的 li 节点下的子节点 a 的文本
result = html.xpath("//li[@class='aaa']/a/text()") # 没有匹配到值
print(f'匹配 class 属性中包含 aaa 的 li 节点下的子节点 a 的文本: {result}')
result = html.xpath("//li[contains(@class, 'aaa')]/a/text()") # 可以匹配到值
print(f'匹配 class 属性中包含 aaa 的 li 节点下的子节点 a 的文本: {result}')
- 多属性匹配
- 我们还会遇到根据多个属性确定一个节点,这时就需要同时匹配多个属性,此时可用运用and运算符来连接使用
html = etree.parse(html_path, etree.HTMLParser())
# 匹配 class 属性中包含 li-first 且 name=item7 的 li 节点下的子节点 aa/span 的文本
result = html.xpath("//li[contains(@class, 'li-first') and @name='item7']/a/span/text()")
print(f'匹配 class 属性中包含 li-first 且 name=item7 的 li 节点下的子节点 a/span文本: {result}')
- 按序选择
- 有时候,我们在选择的时候某些属性可能同时匹配多个节点,但我们只想要其中的某个节点,如第二个节点或者最后一个节点,这时可以利用中括号引入索引的方法获取特定次序的节点
- 在 xpath 中索引位置从1开始, 和 python 的索引起始位置不同
- last(): 最后一个节点, 如果后面跟了子节点, 只有一个节点; 如果没有子节点, 而且有多个符合条件的节点, 返回多个节点
- position(): 返回所有的节点, 不写效果一样
- position() < 3: 返回索引位置小于 3 的节点, 即节点位置为 1 和 2
- [index]: 使用这种方式时, 有可能会有多个元素;
- 例如: 有两个同级节点 ul, 下面都有10的 li 子节点, 此时通过索引会同时取到两个 ul 下符合条件的 li 节点
html = etree.parse(html_path, etree.HTMLParser())
result = html.xpath('//li[1]/a/text()')
print(f'两个符合条件的 ul 都匹配到了, 获取第一个 li 节点下 a 节点文本: {result}')
result = html.xpath('//li[3]/a/text()')
print(f'两个符合条件的 ul 都匹配到了, 获取第三个 li 节点下 a 节点文本: {result}')
result = html.xpath('//li[last()]/a/text()')
print(f'只匹配到了 li 最多的 ul , 获取最后一个 li 节点下 a 节点文本: {result}')
result = html.xpath('//li[position() < 3]/a/text()')
print(f'两个符合条件的 ul 都匹配到了, 获取位置小于3的 li 节点下 a 节点文本: {result}')
result = html.xpath('//li[position()]/a/text()')
print(f'两个符合条件的 ul 都匹配到了, 获取位置小于3的 li 节点下 a 节点文本: {result}')
print('获取祖先节点')
html = etree.parse(html_path, etree.HTMLParser())
result = html.xpath('//li[1]/ancestor::*')
print(f'获取 第一个 li 的所有祖先节点: {result}')
result = html.xpath('//li[last()]/ancestor::*')
print(f'获取 最后一个 li 所有祖先节点: {result}')
result = html.xpath('//li[last()]/ancestor::div')
print(f'获取 最后一个 li 所有 div 祖先节点: {result}')
print('获取属性值')
# 2个 ul 都被匹配到了
result = html.xpath('//li[1]/attribute::*')
print(f'获取 第一个 li 的所有属性值: {result}')
# 2个 ul 都被匹配到了
result = html.xpath('//li[last()]/attribute::*')
print(f'获取 第一个 li 的所有属性值: {result}')
print('获取所有的直接子节点')
result = html.xpath('//li[1]/child::*')
print(f'li[index] 获取所有的直接子节点: {result}')
result = html.xpath('//li[last()]/child::*')
print(f'li[last() ]获取所有的直接子节点: {result}')
result = html.xpath('//li[1]/descendant::a/text()')
print(f'第一个 li 节点, 获取所有的子孙节点的 a 节点的文本: {result}')
result = html.xpath('//li[1]/following::*')
print(f'获取当前子节点之后的所有节点【与当前节点同级并且包括其子节点】: {result}')
result = html.xpath('//li[1]/following-sibling::*')
print(f'获取当前子节点之后的所有节点【与当前节点同级不包括其子节点】: {result}')