Python

[CP_10] Python数据清洗之XPath表达式、实践案例

2019-04-05  本文已影响0人  Fighting_001

目录结构

一、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()方法

执行结果:

上一篇下一篇

猜你喜欢

热点阅读