上接:第三章:数据解析(bs4法)
下接:第四章:验证码识别
1.xpath解析简介
最常用且最便捷高效的一种解析方式。通用性很好
xpath解析原理
- 实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象中。
- 调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获
环境的安装
pip insatll lxml
如何实例化一个etree对象
- ①.将本地的html文档中的源码数据加载到etree对象中:
etree.parse(filePath)
- ②可以从互联网上获取的源码数据加载到该对象中
etree.HTML('page_text')
- xpath(‘xpath表达式’):根据不同形式的xpath表达式去定位到不同的标签并且将定位到不同位置的标签数据进行捕获。
2.不同的xpath表达式
本地用于测试的test.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>此页面以作为测试</title>
</head>
<body>
<div>
<p>百里守约</p>
</div>
<div class="song">
<p>李清照</p>
<p>王安石</p>
<p>苏轼</p>
<p>柳宗元</p>
<a href="http://www/song.com/" title="赵匡胤" target="_self">
<span>this is span</span>
宋朝时最强大内王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
<a href="" class="du">总为浮云能避日,长安不见使人愁</a>
<img src="http://www.baidu.com/moinv.jpg" alt="" />
</div>
<div class="tang">
<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a href="http://www.126.com" title="qi">岐王宅里寻常见,崔久堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a href="http://www.sina.com" title="du">杜甫</a></li>
<li><a href="http://www.dudu.com" title="du">杜牧</a></li>
<li><b>hello world</b></li>
<li><i>happy</i></li>
<li><a herf="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋秦径,晋代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>
2.1 /
表示的是从根节点开始定位,表示的是一个层级。
r=tree.xpath('/html/head/title')
r = tree.xpath('/html//title')
2.2 //
表示的是多个层级,可以表示从任意位置开始定位
r = tree.xpath('//title')
2.3 属性定位
tag[@attrName="attrValue"]
r=tree.xpath('//div[@class="song"]')
2.4 索引定位
r=tree.xpath('//div[@class="song"]/p[3]')
2.5 获取标签中的文本内容
如果是直系文本内容,就在path末尾加/text()
,但如果文本内容不是该标签下的直系文本,就使用//text()
r=tree.xpath('//div[@class="tang"]//li[5]/a/text()')[0]
2.6 获取属性标签中的链接/@src
r=tree.xpath('//div[@class="song"]/img/@src')
代码验证:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from lxml import etree
import requests
if __name__ == "__main__":
#实例化一个etree对象,且将被解析的源码加载了对象中
tree=etree.parse('test.html')
#分级查询
r=tree.xpath('/html/body/div')
print(r)
print('-------分界线-------')
r = tree.xpath('/html//div')
print(r)
print('-------分界线-------')
r = tree.xpath('//div')
print(r)
print('-------分界线-------')
r=tree.xpath('//div[@class="song"]')
print(r)
print('-------分界线-------')
r=tree.xpath('//div[@class="song"]/p[3]')
print(r)
print('-------分界线-------')
r=tree.xpath('//div[@class="tang"]//li[5]/a/text()')[0]
print(r)
print('-------分界线-------')
r=tree.xpath('//div[@class="song"]/img/@src')
print(r)
pass
输出结果:
[<Element div at 0x23254d95340>, <Element div at 0x23255915e40>, <Element div at 0x23255938f00>]
-------分界线-------
[<Element div at 0x23254d95340>, <Element div at 0x23255915e40>, <Element div at 0x23255938f00>]
-------分界线-------
[<Element div at 0x23254d95340>, <Element div at 0x23255915e40>, <Element div at 0x23255938f00>]
-------分界线-------
[<Element div at 0x23255915e40>]
-------分界线-------
[<Element p at 0x23255939e80>]
-------分界线-------
杜牧
-------分界线-------
['http://www.baidu.com/moinv.jpg']
进程已结束,退出代码0
3.代码实践
3.1爬取58二手房中的房源信息
分析层级关系
找到房源信息题目文本所在的标签
xpath的获取可以在抓包工具中右键——复制——对应的xpath
代码实现:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from lxml import etree
import requests
if __name__ == "__main__":
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.11 Safari/537.36'
}
url='https://ls.58.com/ershoufang/?PGTID=0d000000-0000-0595-081a-b95e593d6437'
#爬取到页面源码数据
page_text =requests.get(url=url,headers=headers).text
#实例化一个etree对象,进行数据解析
#由于是网页获取,所以函数使用HTML
tree=etree.HTML(page_text)
#存储的就是a标签对象
div_list=tree.xpath('//*[@id="__layout"]/div/section/section[3]/section[1]/section[2]/div')
fp=open('58.txt','w',encoding='utf-8')
#将a标签中对应的题目信息所存的位置找出
for div in div_list:
title=div.xpath('./a/div[2]/div[1]/div[1]/h3/text()')[0]
print(title)
fp.write(title+'\n')
pass
print("爬取信息成功")
输出:
这里要注意,58同城的界面如果隔一段时间没有操作会自动停止访问,之后运行代码访问url的话爬取不到任何信息也不会报错,这时就需要我们对该页面进行刷新填写验证码。
3.2 4k高清图片下载
选用网址:http://pic.netbian.com/4kdongman/
代码部分:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from lxml import etree
import requests
import os
if __name__ == "__main__":
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.11 Safari/537.36'
}
url='http://pic.netbian.com/4kdongman/'
page_text = requests.get(url=url,headers=headers).text
#创建一个etree对象
tree=etree.HTML(page_text)
#数据解析:src的属性值,alt属性可作为图片名称
li_list=tree.xpath('//div[@class="slist"]/ul/li')
# 创建一个文件夹,保存所有的图片
if not os.path.exists('./xpath_images'):
os.mkdir('./xpath_images')
pass
for li in li_list:
img_src='http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
img_name=li.xpath('./a/img/@alt')[0]+'.jpg'
#通用处理中文乱码的解决方案
img_name=img_name.encode('iso-8859-1').decode('gbk')
# 通用图片进行持久化存储
img_data=requests.get(url=img_src,headers=headers).content
img_path='xpath_images/'+img_name
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_name+"下载成功!!")
运行结果:
3.3 全国城市名称爬取
选用网址:
https://www.aqistudy.cn/historydata/
代码
①热门城市与全部城市分开计数
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from lxml import etree
import requests
import os
if __name__ == "__main__":
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.11 Safari/537.36'
}
url='https://www.aqistudy.cn/historydata/'
page_text = requests.get(url=url,headers=headers).text
#创建一个etree对象
tree=etree.HTML(page_text)
#数据解析
li_list=tree.xpath('//div[@class="bottom"]/ul/li')
cities_name = []
for li in li_list:
hot_city_name=li.xpath('./a/text()')[0]
cities_name.append(hot_city_name)
city_name_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')
for li in city_name_list:
city_name=li.xpath('./a/text()')[0]
cities_name.append(city_name)
print(cities_name)
print("城市总个数为:%d"%(len(cities_name)))
输出结果:
②热门城市与全部城市共用一个xpath表达式
代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from lxml import etree
import requests
import os
if __name__ == "__main__":
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.11 Safari/537.36'
}
url='https://www.aqistudy.cn/historydata/'
page_text = requests.get(url=url,headers=headers).text
#创建一个etree对象
tree=etree.HTML(page_text)
#数据解析
#热门城市对应的层级关系:div/ul/li/a
#全部城市对应的层级关系:div/ul/div[2]/li/a
li_list=tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')
cities_name = []
for li in li_list:
city_name=li.xpath('./text()')[0]
cities_name.append(city_name)
print(cities_name)
print("城市总个数为:%d"%(len(cities_name)))
去重的话调用python对列表元素重复的函数处理:
cities_name=list(set(cities_name))