Date: 2019-07-03
Author: Sun
XPath简介
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。
1、辅助工具
- Chrome插件 XPath Helper
- Firefox插件 XPath Checker
附加:XPath Helper安装过程
(1)找到chrome_xpath_tools目录
(2)方式一:先导入crx文件,看是否可行
安装方式一:直接拖拽chrome.crx插件到谷歌浏览器的扩展程序里。使用快捷钱ctrl+shift+x调出插件,如果没有成功,安装方式二进行安装
(3)方式二:把你的插件扩展名改成rar,然后解压到chrome目录。再重新添加chrome目录到你的谷歌浏览的扩展程序里面。使用快捷键查看是否安装成功
2、lxml
- lxml 是一个支持XPATH语法的HTML/XML的解析库,主要的功能是解析和提取 HTML/XML 数据,我们可以利用XPath语法,来快速的定位特定元素以及节点信息。
- lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器。
- 兼容python2.7和python3.x的所有版本
- lxml 官方文档
3. 语法基础
3.1、语法格式
- 路径 = 相对路径 | 绝对路径
"/"处在XPath表达式开头则表示文档根元素,(表达式中间作为分隔符用以分割每一个步进表达式)如:
/html/body/div
是一种绝对路径表示法,它表明是从文档根开始查找节点。如果路径前没有"/"这种表示法称为相对路径,表明从当前节点开始查找。
如:
body/div
- 路径表达式 = 步进表达式 或者 相对路径 "/"步进表达式。
- 步进表达式 = 轴 节点测试 谓词等组成
- 其中轴表示步进表达式选择的节点和当前上下文节点间的树状关系(层次关系),节点测试指定步进表达式选择的节点名称扩展名,谓词即相当于过滤表达式以进一步过滤细化节点集。
- 谓词可以是0个或多个。多个谓词用逻辑操作符and, or连接。取逻辑非用not()函数。
3.2、选取节点
说明
XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的
所谓节点(node),就是XML文件的最小构成单位,一共分成7种
- element(元素节点)常用
- attribute(属性节点)常用
- text (文本节点)常用
- root (根节点)
- comment (注释节点)
- namespace (名称空间节点xml)
- processing-instruction (处理命令节点(xml))
语法
表达式 描述 实例 nodename 选取nodename节点的所有子节点 xpath(‘//div’) 选取了div节点的所有子节点 / 从根节点选取 xpath(‘/div’) 从根节点上选取div节点 // 选取所有的当前节点,不考虑他们的位置 xpath(‘//div’) 选取所有的div节点 . 选取当前节点 xpath(‘./div’) 选取当前节点下的div节点 .. 选取当前节点的父节点 xpath(‘..’) 回到上一个节点 @ 选取属性 xpath(’//@calss’) 选取所有的class属性
3.3、谓语
说明
谓语被嵌在方括号内,用来查找某个特定的节点或包含某个制定的值的节点
语法
表达式 结果 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.4、通配符
说明
Xpath通过通配符来选取未知的XML元素
语法
表达式 结果 xpath(’/div/*’) 选取div下的所有子节点 xpath(‘/div[@*]’) 选取所有带属性的div节点
3.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::*’) 选取当前节点
3.6、功能函数
说明
使用功能函数能够更好的进行模糊搜索
语法
函数 用法 解释 starts-with xpath(‘//div[starts-with(@id,”test”)]‘) 选取id值以test开头的div节点 contains xpath(‘//div[contains(@id,”test”)]‘) 选取id值包含test的div节点 and xpath(‘//div[contains(@id,”test”) and contains(@id,”in”)]‘) 选取id值包含ma和in的div节点 text() xpath(‘//div[contains(text(),”test”)]‘) 选取节点文本包含test的div节点
4. xpath应用
4.1、准备工作
安装lxml
pip install lxml==4.3.0
pip install lxml-4.3.0-cp36-cp36m-win_amd64.whl
4.2、基本使用
导入lxml
实例化ElementTree
用来解析字符串格式的HTML文档对象
从本地读取HTML文件
root = etree.parse('test.html',etree.HTMLParser()) result = etree.tostring(html, pretty_print=True)
利用xpath语法解析
注意事项
先确保html经过了utf-8解码,即code = html.decode('utf-8', 'ignore'),否则会出现解析出错情况。因为中文被编码成utf-8之后变成 '/u2541' 之类的形式,lxml一遇到 “/”就会认为其标签结束
4.3 案例分析
案例1:
爬取百度贴吧楼主页面中图片
#coding=utf-8
from lxml import etree
import requests
num = 0
header = {'User-Agent':'Mozilla/5.0(Macintosh;IntelMacOSX10_7_0)AppleWebKit/535.11(KHTML,likeGecko)Chrome/17.0.963.56Safari/535.11'}
def load_data(key, page_start, page_end):
url = 'https://tieba.baidu.com/f?ie=utf-8&kw=%s&pn=%d'
for page in range(page_start,page_end+1):
full_url = url%(key,(page-1)*50)
headers = {'User_Agent':'User-Agent:Mozilla/5.0(Macintosh;IntelMacOSX10_7_0)AppleWebKit/535.11(KHTML,likeGecko)Chrome/17.0.963.56Safari/535.11'}
response = requests.get(full_url,headers = headers,verify = False)
data = response.text
xml = etree.HTML(data)
lzs = xml.xpath('//div[@class="threadlist_lz clearfix"]/div/a/@href')
#进入楼主的帖子
for lz in lzs:
load_lz_detail(lz)
def load_lz_detail(lz_url):
url = 'https://tieba.baidu.com'+lz_url
header = {'User-Agent':'Mozilla/5.0(Macintosh;IntelMacOSX10_7_0)AppleWebKit/535.11(KHTML,likeGecko)Chrome/17.0.963.56Safari/535.11'}
#使用requests进行数据加载
response = requests.request('GET',url,headers = header,verify = False)
html = response.text
xml = etree.HTML(html)
image_url = xml.xpath('//img[@class="BDE_Image"]/@src')
#获取了楼主详情页面的图片
for url in image_url:
load_lz_detail_image(url)
def load_lz_detail_image(url):
global num
response = requests.get(url,headers =header,verify = False)
num+=1
with open('./images/'+str(num)+'.jpg','wb') as file:
file.write(response.content)
if __name__ == '__main__':
key = input('请输入贴吧名称:')
page_start = int(input("请输入起始页"))
page_end = int(input('请输入末尾页'))
load_data(key, page_start, page_end)
案例2:
采用xpath爬取格言网数据
练习:
58同城租房信息