XPath 概述
XPath是 XML路径语言,全名为“XML Path Language“,是一门可以在XML文件中查找信息的语言。不仅可以实现XML文件的搜索,还可以在HTML文件中进行搜索。XPath常用路径表达式:
XPath常用路径表达式及描述
XPath的解析操作
lxml模块可以解析HTML与XML,并且支持XPath解析方式。
解析HTML
• 1. parse()方法
parse()方法主要用于实现解析本地的HTML文件,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/4/22 5:58 AM
# 文件 :parse()方法.py
# IDE :PyCharm
from lxml import etree # 导入etree子模块
parser = etree.HTMLParser() # 创建HTMLParser对象
html = etree.parse('demo.html',parser=parser) # 解析demo.html文件
html_txt = etree.tostring(html,encoding = "utf-8") # 转换字符串类型,并进行编码
print(html_txt.decode('utf-8')) # 打印解码后的HTML代码
程序运行结果如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- saved from url=(0038)http://sck.rjkflm.com:666/spider/auth/ --><html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>标题文档</title>
</head>
<body>
<img src="./demo_files/logo1.png" />
<br />
hello 明日科技 ~
</body></html>
• 2. HTML()方法
etree子模块还提供了一个HTML()方法,该方法可以实现解析字符串类型的HTML代码。示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/4/22 6:21 AM
# 文件 :解析字符串类型的HTML代码.py
# IDE :PyCharm
# 导入etree子模块
from lxml import etree
# 定义HTML字符串
html_str = '''<title>标题文档</title>
</head>
<body>
<img src="./demo_files/logo1.png" />
<br />
hello 明日科技 ~
</body></html>'''
# 解析HTML字符串
html = etree.HTML(html_str)
# 转换字符串类型,并进行编码
html_txt = etree.tostring(html, encoding = 'utf-8')
print(html_txt.decode('utf-8'))
程序运行结果如下:
<html><head><title>标题文档</title>
</head>
<body>
<img src="./demo_files/logo1.png"/>
<br/>
hello 明日科技 ~
</body></html>
解析服务器返回的HTML代码,实际开发中,发送网络请求后,多数情况下都会将返回的响应结果转换为字符串类型,如果返回的结果是HTML代码,则需要使用HTML()方法来进行解析。示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/4/22 6:28 AM
# 文件 :解析服务器返回的HTML代码.py
# IDE :PyCharm
from lxml import etree # 导入etree子模块
import requests # 导入requests模块
from requests.auth import HTTPBasicAuth # 导入HTTPBasicAuth类
# 定义请求地址
url = 'http://sck.rjkflm.com:666/spider/auth/'
ah = HTTPBasicAuth('admin','admin') # 创建HTTPBasicAuth对象,参数为用户名与密码
response = requests.get(url=url,auth=ah) # 发送网络请求
if response.status_code==200: # 如果请求成功
html = etree.HTML(response.text) # 解析html字符串
html_txt = etree.tostring(html,encoding = "utf-8") # 转换字符串类型,并进行编码
print(html_txt.decode('utf-8')) # 打印解码后的HTML代码
程序运行结果如下:
说 明
图中红框内的" "表示Unicode编码的回车字符
获取所有节点
在获取HTML代码中的所有节点时,可以使用"//*"的方式,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/4/22 7:04 AM
# 文件 :*"获取HTML代码的所有节点.py
# IDE :PyCharm
from lxml import etree # 导入etree子模块
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li> <a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java API文档">Java API文档</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的下载">JDK的下载</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的安装">JDK的安装</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="配置JDK">配置JDK</a> </li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
node_all = html.xpath('//*') # 获取所有节点
print('数据类型:',type(node_all)) # 打印数据类型
print('数据长度:',len(node_all)) # 打印数据长度
print('数据内容:',node_all) # 打印数据内容
# 通过推导式打印所有节点名称,通过节点对象.tag获取节点名称
print('节点名称:',[i.tag for i in node_all])
程序运行结果如下:
数据类型: <class 'list'>
数据长度: 16
数据内容: [<Element html at 0x7fd73360f280>, <Element body at 0x7fd73376b640>, <Element div at 0x7fd73376b680>, <Element ul at 0x7fd73376b6c0>, <Element li at 0x7fd73376b700>, <Element a at 0x7fd73376b780>, <Element li at 0x7fd73376b7c0>, <Element a at 0x7fd73376b800>, <Element li at 0x7fd73376b840>, <Element a at 0x7fd73376b740>, <Element li at 0x7fd73376b880>, <Element a at 0x7fd73376b8c0>, <Element li at 0x7fd73376b900>, <Element a at 0x7fd73376b940>, <Element li at 0x7fd73376b980>, <Element a at 0x7fd73376b9c0>]
节点名称: ['html', 'body', 'div', 'ul', 'li', 'a', 'li', 'a', 'li', 'a', 'li', 'a', 'li', 'a', 'li', 'a']
获取HTML代码中所有指定名称的节点,可以在“//”的后面添加节点的名称。以获取所有“li“节点为例,关键代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/4/22 7:49 AM
# 文件 :获取指定名称的节点.py
# IDE :PyCharm
from lxml import etree # 导入etree子模块
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li> <a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java API文档">Java API文档</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的下载">JDK的下载</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的安装">JDK的安装</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="配置JDK">配置JDK</a> </li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串,html字符串为上一示例的html字符串
li_all = html.xpath('//li') # 获取所有li节点
print('所有li节点',li_all) # 打印所有li节点
print('获取指定li节点:',li_all[1]) # 打印指定li节点
li_txt = etree.tostring(li_all[1],encoding = "utf-8") # 转换字符串类型,并进行编码
# 打印指定节点的HTML代码
print('获取指定节点HTML代码:',li_txt.decode('utf-8'))
程序运行结果如下:
所有li节点 [<Element li at 0x7fd6ca76b300>, <Element li at 0x7fd6ca76b340>, <Element li at 0x7fd6ca76b380>, <Element li at 0x7fd6ca76b3c0>, <Element li at 0x7fd6ca76b400>, <Element li at 0x7fd6ca76b480>]
获取指定li节点: <Element li at 0x7fd6ca76b340>
获取指定节点HTML代码: <li> <a href="javascript:" onclick="login(0)" title="Java的版本">Java的版本</a> </li>
如果需要获取一个节点中的直接子节点可以使用“/”,例如获取li节点中所有子节点a,可以使用“//li/a”的方式进行获取,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/4/22 8:02 AM
# 文件 :"获取一个节点中的直接子节点.py
# IDE :PyCharm
from lxml import etree # 导入etree子模块
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li>
<a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a>
<a>Java</a>
</li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java API文档">Java API文档</a> </li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
a_all = html.xpath('//li/a') # 获取li节点中所有子节点a
print('所有子节点a',a_all) # 打印所有a节点
print('获取指定a节点:',a_all[1]) # 打印指定a节点
a_txt = etree.tostring(a_all[1],encoding = "utf-8") # 转换字符串类型,并进行编码
# 打印指定节点的HTML代码
print('获取指定节点HTML代码:',a_txt.decode('utf-8'))
程序运行结果如下:
所有子节点a [<Element a at 0x7f7c2c76b340>, <Element a at 0x7f7c2c76b380>, <Element a at 0x7f7c2c76b3c0>, <Element a at 0x7f7c2c76b400>]
获取指定a节点: <Element a at 0x7f7c2c76b380>
获取指定节点HTML代码: <a>Java</a>
使用"//"实现获取直接子孙节点,以获取ul节点中所有子孙节点a为例,示例代码如下:
#_*_coding:utf-8_*_
# 作者 :liuxiaowei
# 创建时间 :2/4/22 9:47 AM
# 文件 :实现获取子孙节点.py
# IDE :PyCharm
from lxml import etree # 导入etree子模块
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li>
<a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a>
<a>Java</a>
</li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
<li>
<a href="javascript:" οnclick="login(0)" title="Java API文档">
<a>a节点中的a节点</a>
</a>
</li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
a_all = html.xpath('//ul//a') # 获取ul节点中所有子孙节点a
print('所有子节点a',a_all) # 打印所有a节点
print('获取指定a节点:', a_all[4]) # 打印指定a节点
a_txt = etree.tostring(a_all[4], encoding = "utf-8") # 转换字符串类型,并进行编码
# 打印指定节点的HTML代码
print('获取指定节点HTML代码:', a_txt.decode('utf-8'))
程序运行结果如下:
所有子节点a [<Element a at 0x7ff605767400>, <Element a at 0x7ff605767440>, <Element a at 0x7ff605767480>, <Element a at 0x7ff6057674c0>, <Element a at 0x7ff605767500>]
获取指定a节点: <Element a at 0x7ff605767500>
获取指定节点HTML代码: <a>a节点中的a节点</a>
说 明
在获取ul子孙节点时,如果使用“//ul/a“的方式获取,是无法匹配任何结果的。因为“/”用来获取直接子节点,ul的直接子节点为li,并没有a节点,所以无法匹配。
接下文 XPath解析(二)https://developer.aliyun.com/article/1615872