[CP_10] Python数据清洗之XPath表达式、实践案例
目录结构
一、XPath功能应用
1. xpath简介、lxml安装
2. 解析html字符串 & 解析本地html文件
1)解析字符串形式的html(字符串)
2) 解析本地html文件
3. 获取某一类标签所包含的内容
4. 获取指定属性的标签所包含的文本内容
5. 获取标签的属性
6. 获取子标签
7. 获取标签内容&标签名
二、运用xpath进行Python爬虫案例
1. 案例:爬取全球排名前20的商业学校信息
2. 案例:爬取贴吧帖子内的图片
一、XPath功能应用&案例实践
1. xpath简介、lxml安装
XPath引入:其前用正则匹配处理响应返回的字符串数据,本文则通过将HTML文件转换成XML文档,然后再利用XPath查找HTML节点or元素来处理HTML数据(解析HTML文档),以应对网页爬虫or元素定位测试过程中经常需要处理HTML文档的场景,而支持XPath的功能则需要安装lxml模块
安装lxml模块:
pip install lxml
已安装完成
2. 解析html字符串 & 解析本地html文件
爬虫中的网页处理方式:
①数据获取、数据清洗同时进行: HTML()
②先数据获取,再数据清洗: parse()
1)解析字符串形式的html(字符串)
lxml001.py
from lxml import etree
text='''
<title>搜狗搜索引擎 - 上网从搜狗开始</title>
<link rel="shortcut icon" href="/images/logo/new/favicon.ico?v=4" type="image/x-icon">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
'''
# 将字符串解析为特殊的html对象
html=etree.HTML(text)
# 将html对象转换为字符串,并对中文格式解码处理
result=etree.tostring(html,encoding="utf-8").decode()
print(result)
2) 解析本地html文件
lxml002.py
from lxml import etree
# 获取本地html文档进行解析
html=etree.parse(r"D:\CI_Env\Python_Test\file\test.html")
# 将html对象转为字符串
result=etree.tostring(html,encoding="utf-8").decode()
print(result)
test.html
<div class="contents topic">
<p class="topic-title first"><a id="contents" name="contents">lxml关联内容</a></p>
<ul class="simple">
<li><a class="reference" id="id1" name="id1">lxml.etree</a></li>
<li><a class="reference" id="id2" name="id2">Other Element APIs</a></li>
<li><a class="reference" id="id3" name="id3">Trees and Documents</a></li>
</ul>
</div>
执行结果:
3. 获取某一类标签所包含的内容
方式://标签名
根据标签名称获取标签下包含的文本内容:
getLabel.py
from lxml import etree
# 获取本地html文档
html=etree.parse(r"D:\CI_Env\Python_Test\file\test.html")
# 获取所有a标签下包含的信息
result=html.xpath("//a") # 列表类型;可用print(type(result))判断
# 循环提取每个a标签所直接包含的文本内容
for i in range(len(result)):
print(result[i].text)
执行结果:
4. 获取指定属性的标签所包含的文本内容
方式:标签名[@属性取值]
test.html
<div class="contents topic">
<p class="topic-title first"><a id="contents" name="contents">lxml关联内容</a></p>
<ul class="simple">
<li><a class="reference" id="id1" name="id1">lxml.etree</a></li>
<li><a class="reference" id="id2" name="id2">Other Element APIs</a></li>
<li><a class="reference" id="id3" name="id3">Trees and Documents</a></li>
<li class="item-1">这是li标签内容</li>
</ul>
</div>
根据标签属性获取标签中包含的文本内容:
getSpecifiedLabel.py
from lxml import etree
# 获取本地html文档
html=etree.parse(r"D:\CI_Env\Python_Test\file\test.html")
result1=html.xpath("//li[@class='item-1']") # 所有li标签下,指定属性的标签
result2=html.xpath("//li/a[@id='id2']") # 所有li标签下的a标签下,指定属性的标签
print(result1[0].text)
print(result2[0].text)
5. 获取标签的属性
方式:@属性名
test.html
<div class="contents topic">
<p class="topic-title first"><a id="contents" name="contents">lxml关联内容</a></p>
<ul class="simple">
<li><a class="reference" href="link1" id="id1">lxml.etree</a></li>
<li><a class="reference" href="link2" id="id2">Element APIs</a></li>
<li><a class="reference" href="link3" id="id3">Trees Documents</a></li>
<li class="item-1">这是li标签内容</li>
</ul>
</div>
getTagAttribute.py
from lxml import etree
import requests
# 获取本地html文档
html=etree.parse(r"D:\CI_Env\Python_Test\file\test.html")
result1=html.xpath("//li/@class") # li标签的class属性(列表类型)
result2=html.xpath("//li/a/@class") # li标签下a标签的class属性
result3=html.xpath("//li/a/@href") # li标签下a标签的href属性
print(result1)
print(result2)
print("-----------------------")
for url in result3:
print(url)
# requests.get(url)
执行结果:
6. 获取子标签
//
:获取所有符合条件的子标签
/
:获取下一级子标签
getChildTag.py
from lxml import etree
# 获取本地html文档
html=etree.parse(r"D:\CI_Env\Python_Test\file\test.html")
result1=html.xpath("//ul/li") # 下一级子标签
result2=html.xpath("//ul//a") # 所有符合条件的标签
result3=html.xpath("//li/a//@id") # li标签下a标签的所有id属性
print(result1[0].text)
print(result2[0].text)
print(result3)
执行结果:
7. 获取标签内容&标签名
.text
:获取标签内容
.tag
:获取标签名
getTagInfo.py
from lxml import etree
import requests
# 获取本地html文档
html=etree.parse(r"D:\CI_Env\Python_Test\file\test.html")
# 获取倒数第2个li标签下a标签的内容
result1=html.xpath("//li[last()-1]/a")
print(result1[0].text)
print("--------------------------")
# 获取li标签下倒数第2个a标签的内容
result2=html.xpath("//li/a")
print(result2[-2].text)
print("--------------------------")
# 获取指定class取值的标签名
result3=html.xpath("//*[@class='topic-title first']")
print(result3[0].tag)
执行结果:
二、运用xpath进行Python爬虫案例
1. 案例:爬取全球排名前20的商业学校信息
目标:从USNews网站爬取最新全球排名前20的商业学校名称、位置、排名
分析:
确保标签中属性的唯一定位到学校名称、学校位置:
代码实现:
schoolRank-MBA.py
import requests
from lxml import etree
url="https://www.usnews.com/best-graduate-schools/top-business-schools/mba-rankings"
header={
"User-Agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
}
# 发送GET请求,获取响应数据(字符串类型)
resp=requests.get(url,headers=header)
data=resp.text
# 将响应字符串解析为html对象
html=etree.HTML(data)
# 根据定位获取学校名称、学校位置
schoolName=html.xpath('//div[@class="s85n6m5-0-Box-cwadsP clSECZ"]//a[@class="Anchor-s1x2hjz6-0 gqKfEY"]')
schoolLocation=html.xpath('//div[@class="s85n6m5-0-Box-cwadsP clSECZ"]//p[@class="Paragraph-fqygwe-0 bstttc"]')
# 遍历输出排名前20的所有学校信息(排名-名称-位置)
for i in range(len(schoolName)):
school="Rank"+str(i+1)+"-----"+schoolName[i].text+"-------"+schoolLocation[i].text
print(school)
执行结果:
2. 案例:爬取贴吧帖子内的图片
分析:
进入指定主题的贴吧列表页,从列表页获取每个贴子的href属性,然后拼接成完成的链接访问对应详细页,进入详细页获取图片元素的定位
页码链接:
第1页:http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=0
第2页:http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=50
第3页:http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=100
==> pn=(页码-1)*50
列表页-帖子href属性:
详细页-图片src属性(对应图片下载链接):
主体框架:
创建爬虫类:初始化方法、构造方法(构造url、爬取页码内容、爬取图片、保存图片)、main函数执行代码
注意事项:
爬取响应数据过程中,可能存在响应数据被注释,致使xpath无法有效定位,返回的结果列表也是空列表。因此,获取响应数据之后,需要先替换掉注释符,再用xpath识别定位
代码实现:
getTbImage.py
import urllib
from urllib import request
import requests
from lxml import etree
import re
# 创建爬虫类
class Spider(object):
# 定义初始化方法
def __init__(self):
self.url="http://tieba.baidu.com/f?"
self.ua_header={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:65.0) Gecko/20100101 Firefox/65.0"}
self.tbName="python"
self.startPage=1
self.endPage=3
self.fileName=1 # 图片名称的初始编号
# 构造url
def tbSpider(self):
# 遍历构造3个页码的url
for page in range(self.startPage,self.endPage+1):
pn=(page-1)*50
param={"kw":self.tbName,"pn":pn} # 处理参数项
word=urllib.parse.urlencode(param) # 解析url参数
my_url=self.url+word # 拼接为完整的页码访问url
# 传入url,调用爬取页面的方法
self.loadPage(my_url)
# 爬取网页内容
def loadPage(self,url):
# urllib库使用:创建GET请求对象
req=urllib.request.Request(url,headers=self.ua_header)
resp=urllib.request.urlopen(req).read() # 发送请求,获取响应数据
# # requests库使用:发送GET请求,获取响应数据
# resp=requests.get(url,headers=self.ua_header)
# resp.encoding="utf-8" # 避免乱码
# resp=resp.text
# 利用正则对响应数据取消注释,以便后续可以xpath定位
pat=re.compile(r"<!--|--!>")
data=pat.sub('"',str(resp)) # 采用requests库时,此处不需要强转resp为字符串
# data=str(resp).replace(r'<!--','"').replace(r'-->','"')
# 解析响应数据
html=etree.HTML(data)
links=html.xpath('//div[@class="threadlist_title pull_left j_th_tit "]//a/@href')
# 遍历拼接列表中每个帖子的完整链接
for link in links:
detail_url="http://tieba.baidu.com"+link
# 传入detail_url,调用获取图片链接的方法
self.loadImage(detail_url)
# 爬取帖子详细页,获取图片链接
def loadImage(self,detail_url):
req=urllib.request.Request(detail_url,headers=self.ua_header)
resp=urllib.request.urlopen(req).read()
# 解析响应数据;获取图片的链接
html=etree.HTML(resp)
links=html.xpath('//img[@class="BDE_Image"]/@src')
# 遍历获取每一个图片链接,作为参数传入,调用存储图片的方法
for img_url in links:
self.storeImage(img_url)
# 通过图片链接,爬取图片并保存到本地
def storeImage(self,img_url):
print("正在存储图片:",self.fileName,"...")
image=urllib.request.urlopen(img_url).read() # 二进制文件
file=open(r"D:\CI_Env\Python_Test\file\image\\"+str(self.fileName)+".jpg","wb")
file.write(image)
file.close()
self.fileName+=1
# 定义main函数,调用执行代码
if __name__ == '__main__':
mySpider=Spider() # 创建Spider对象
mySpider.tbSpider() # 调用tbSpider()方法
执行结果: