Xpath
预备知识
HTML DOM 模型示例
HTML DOM 定义了访问和操作 HTML 文档的标准方法,以树型结构表示HTML 文档。
如何实现爬虫数据的解析?
1、 定位html文档中的节点
2、 提取指定节点的属性,比如href,class等
3、 获取指定节点的文本,比如a、p,div,span,div等的文本
什么是XPath?
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性迚行遍历。
XPath可以先将 HTML文件 转换成 XML文档,然后用 XPath 查找 HTML 节点或元素。
什么是XML
可扩展标记语言(EXtensible Markup Language),W3C 的推荐标准,类似 HTML,标签需要我们自行定义,具有自我描述性,设计宗旨是传输数据,而非显示数据
XML 和 HTML 的区别
XPath语法
选取节点
XPath 使用路径表达式来选取 XML 文档中的节点和或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似
下面列出了最常用的路径表达式:
一些路径表达式以及表达式的结果:
谓语(Predicates)
谓语用来查找某个特定的节点戒者包含某个指定的值的节点,被嵌在方括号中。
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
选取未知节点
XPath 通配符可用来选取未知的 XML 元素
在下面的表格中列出了一些路径表达式,以及这些表达式的结果:
Xpath轴
轴可以定义相对于当前节点的节点集
选取若干路径
通过在路径表达式中使用“|”运算符,可以选取若干个路径。
XPath的运算符
下面列出了可用在 XPath 表达式中的运算符:
lxml库
lxml 是一款高性能的 Python HTML/XML 的解析器,用 C 实现的,主要的功能是利用XPath语法解析和提取 HTML/XML 数据,来快速的定位特定元素以及节点信息。 lxml python 官方文档:http://lxml.de/index.html
安装
pip install lxml
解析 HTML 代码
# 使用 lxml 的 etree 库
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><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> 闭合标签
</ul>
</div>
'''
#利用 etree.HTML,将字符串解析为 HTML 文档
html = etree.HTML(text)
# 按字符串序列化 HTML 文档
result = etree.tostring(html).decode()
print(result)
lxml 可以自动修正html代码,例子里不仅补全了 li 标签,还添加了 body,html 标签。
html文件解析
hello.html 文件:
<div>
<ul>
<li class="item-0"><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"><span class="bold">third item</span></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>
</ul>
</div>
# lxml_parse.py
from lxml import etree
# 读取外部文件 hello.html
html = etree.parse('./hello.html')
result = etree.tostring(html, pretty_print=True).decode()
print(result)
XPath实例测试
1. 获取所有的- 标
# xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
print(type(html)) # 显示 etree.parse() 返回类型
result = html.xpath('//li')
print(result) # 打印<li>标签的元素集合
print(len(result))
print(type(result))
print(type(result[0]))
2. 获取- 标签下的所有 标签
# xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
#result = html.xpath('//li/span')
#注意这么写是不对的:
#因为 / 是用来获取子元素的,而 <span> 并不是 <li> 的子元素,所以,要用双斜杠
result = html.xpath('//li//span')
print(result)
3. 获取- 标签下 href为 link1.html 的 标签
# xpath_li.py
from lxml import etree
html = etree.parse('hello.html'
result = html.xpath('//li/a[@href="link1.html"]')
print(result)
4. 获取- 标签的所有 class属性
# xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/@class')
print(result)
运行结果
[<Element a at 0x10ffaae18>]
5. 获取 - 标签下的标签里的所有 class属性
# xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/a//@class')
print(result)
7. 获取倒数第二个元素的内容
# xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a')
# text 方法可以获取元素内容
print(result[0].text)
8. 获取 class 值为 bold 的标签名
# xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//*[@class="bold"]')
# tag 方法可以获取标签名
print(result[0].tag)
小思考
1、Python 中单引号,双引号,3个单引号及3个双引号的区别
当你用单引号’ '定义字符串的时候,它就会认为你字符串里面的双引号" "是普通字符,从而不需要转义。反之当你用双引号定义字符串的时候,就会认为你字符串里面的单引号是普通字符无需转义。3个引号实现多行输出效果戒者加注释
2、xpath中/和//的区别
/用来获取子元素,//用来获取子孙后代的元素
3、如何获取节点内容?
用text 选取文本内容
** 4、如何获取节点属性?**
用@选取属性。
接下来给大家带来我对链家二手房进行的数据爬取的代码案例:
# 案例:链家二手房
# https://sh.lianjia.com/ershoufang/
# 提取标题,链接,单价,总价,基本信息,房源特色信息
# 保存到mysql数据库
import requests
from lxml import etree
# 首先定义个基本路由
url = 'https://sh.lianjia.com/ershoufang/'
# 设置头信息
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
}
# 开始网页的爬取
response = requests.get(url, headers=headers)
html = response.content # bytes字符串
# 将爬取下来的问本使用lxml里的 etree 进行转换为树桩结构
html = etree.HTML(html)
# 首先找到这个大类
list1 = html.xpath('//div[@class="info clear"]')
# 然后开始剥取所选大类内的数据
for each in list1:
# 选取标题
title = each.xpath('./div/a/text()')[0]
print('title:', title)
print("-" * 500)
# 选取路由
detail_url = each.xpath('./div/a/@href')[0]
print('detail url:', detail_url)
print("-" * 500)
# 选取单价
price = each.xpath('./div/div[@class="unitPrice"]/span/text()')[0]
print(price)
print("-" * 500)
# 选取总价
total = each.xpath('./div/div[@class="totalPrice"]/span/text()')[0]
# 获取价格单位
unit = each.xpath('./div/div[@class="totalPrice"]/text()')[0]
print("总价:", total + unit)
print("-" * 500)
# 开始二次爬取 先获取到详情路由
url = detail_url
# 开始进去爬取
response = requests.get(url, headers=headers)
html = response.content
# 爬取到了 进行html转换
html = etree.HTML(html)
# 然后开始找大类
# 这个是基本属性
list2 = html.xpath('//div[@class="box-l"]/div/div/div/div[@class="base"]/div[@class="content"]/ul/li')
for ask in list2:
title_1 = ask.xpath('./span/text()')[0]
detail_1 = ask.xpath('./text()')[0]
print(title_1, ":", detail_1)
print("-" * 5)
# 这个是交易属性
list3 = html.xpath('//div[@class="box-l"]/div/div/div/div[@class="transaction"]/div[@class="content"]/ul/li')
for ask in list2:
title_1 = ask.xpath('./span/text()')[0]
detail_1 = ask.xpath('./text()')[0]
print(title_1, ":", detail_1)
print("-" * 500)
# 这个是房源标签
list4 = html.xpath('//div[@class="box-l"]/div[@class="newwrap baseinform"]/div[@class="introContent showbasemore"]')
for ask1 in list4:
title_1 = ask1.xpath('./div[@class="tags clear"]/div[@class="name"]/text()')[0]
detail_1 = ask1.xpath('./div[@class="tags clear"]/div/a/text()')[0].strip()
print(title_1, ":", detail_1)
print("-" * 500)
# 这个是房屋特色
list5 = html.xpath('//div[@class="box-l"]/div[@class="newwrap baseinform"]/div[@class="introContent showbasemore"]/div[@class="baseattribute clear"]')
for ask1 in list5:
title_1 = ask1.xpath('./div[@class="name"]/text()')
if len(title_1) > 0:
title_1 = title_1[0]
else:
title_1 = " "
detail_1 = ask1.xpath('./div[@class="content"]/text()')
if len(detail_1) > 0:
detail_1 = detail_1[0].strip()
else:
detail_1 = " "
print(title_1, ":", detail_1)
print("-"*500)
纯属手打 给个赞呗!!!