Python爬虫 之数据解析之xpath


一、xpath解析

1、xpath是什么

xpath是最常用且最高效的一种解析方式。通用性很高,不止在Python语言中可以使用,在其它语言中也可以使用。

xpath 全称 XML Path Language,即 XML 路径语言,它是一门 XML 文档中查找信息的语言。它最初是用来搜寻 XML 文档的,但是它同样适用于 HTML 文档的搜索。所以在做爬虫时,我们完全可以使用 xpath 来做相应的信息抽取。

2、xpath解析原理:

① 实例化一个 etree 的对象,且需要将被解析的页面源码数据加载到该对象中。
② 调用 etree 对象中的 xpath 方法,结合着xpath表达式实现标签的定位和内容的捕获。


二、环境的安装

使用xpath解析,只需要安装一个解析器:lxml。而这个解析器在bs4中也需要安装。lxml是一个解析器,也是下面的xpath要用到的库。

环境安装:

pip install lxml

在这里插入图片描述


三、实例化etree对象

首先需要导入模块

from lxml import etree

etree对象同样的有两个操作

1、将本地的 html 文档中的源码数据加载到 etree 对象中:

etree.parse(filePath)
# filePath为文件的路径

2、可以从互联网上获取的源码数据加载到该对象中:

etree.HTML('page_text')
# page_text为从页面获取的源码数据

调用 xpath 方法
使用 xpath 方法需要传入参数,这个参数是一个字符串,而这个字符串就是 xpath表达式。

xpath('xpath表达式')

也就是说在数据解析过程中,最重要的步骤就是xpath表达式的书写。意味着可以根据不同形式的xpath表达式定义不同的标签,并且可以将定位标签的相关文本数据进行获取。


四、xpath表达式

符号含义
/表示的是从根节点开始定位。表示的是一个层级。
//表示的是多个层级。可以表示从任意位置开始定位。

属性定位:

tag[@attrName="attrValue"]
# 例如:
//div[@class="book-mulu"]

索引定位:

# 索引是从[1]开始的
tag[@attrName="attrValue"]/a[num]	# a为标签名,num为第几行
# 例如
//div[@class="book-mulu"]/ul/li[3]
# 也可以表达为://div[@class="book-mulu"]//li[3]

取文本:

/text()			# 获取标签中直系的文本内容
//text()		# 获取标签中非直系文本内容(所有文本内容)

取属性:

/@attrName
# 例如:img/@src

五、项目实例

实例一

需求:
爬取58同城二手房源信息(以北京市为例)。解析出所有房源的名称,并进行持久化存储。
网址:https://bj.58.com/ershoufang/

思路:

主要就是观察页面的结构,看每个房源的名字所在的标签是哪个。然后写出xpath表达式即可。
在这里插入图片描述
因为所有房源名称都在li标签中,所以我们先将li标签的所有内容都爬取下来。

# xpath表达式
tree.xpath('//ul[@class="house-list-wrap"]/li')

在这里插入图片描述

我们观察到li标签下第二个div标签的h2标签下有名称信息,所有写下xpath表达式。因为这个是局部解析,div上一层标签是li所有div前面要加 “ . ”

# xpath表达式
title = li.xpath('./div[2]/h2/a/text()')[0]

在这里插入图片描述

代码实现:

# 需求:爬取58通常二手房源信息
import requests
from lxml import etree

if __name__ == '__main__':
    url = 'https://bj.58.com/ershoufang/'
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36'
    }
    # 爬取页面源码数据
    page_text = requests.get(url=url,headers=header).text

    # 页面解析
    tree =etree.HTML(page_text)
    li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
    fp = open('58.txt','w',endcoding='utf-8')
    for li in li_list:
        # 局部解析
        # 一定要加 . 这个 . 表示的就是局部定位到的标签
        title = li.xpath('./div[2]/h2/a/text()')[0]
        print(title)
        # 存入文件
        fp.write(title + '/n')

实例二

需求:
爬取《红楼梦》所有章节的标题。
网址:https://www.shicimingju.com/book/hongloumeng.html

思路:

主要是对xpath表达式的书写。通过观察标签写出xpath表达式。
在这里插入图片描述

# xpath表达式
tree = etree.xpath('//div[@class="card bookmark-list"]/div[4]/ul/li/a/text()')

代码实现:

import requests
from lxml import etree

if __name__ == '__main__':
    url = 'https://www.shicimingju.com/book/hongloumeng.html'
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36'
    }
    # 爬取页面源码数据
    page_text = requests.get(url=url,headers=header).text
    # 实例化etree
    tree = etree.HTML(page_text)
    # xpath表达式
    list1 = tree.xpath('//div[@class="card bookmark-list"]/div[4]/ul/li/a/text()')
    for li in list1:
        title = li
        print(title)
# 爬取《三国演义》小说,与上述《红楼梦》原理相同
import  requests
from lxml import etree

if __name__ == '__main__':
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'
    }
    url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
    page_text = requests.get(url=url,headers=header).text
    # 实例化etree对象
    tree = etree.HTML(page_text)
    list1 = tree.xpath('//*[@id="main_left"]/div/div[4]/ul/li')
    for li in list1:
    	# 标题
        title = li.xpath('./a/text()')[0]
        print(title)
        new_url = 'https://www.shicimingju.com' + li.xpath('./a/@href')[0]
        
        new_page_text = requests.get(url=new_url,headers=header).text
        new_tree = etree.HTML(new_page_text)
        # 文章内容
        list2 = new_tree.xpath('//*[@id="main_left"]/div[1]/div/p')
        for i in list2:
            words = i.xpath('./text()')[0]
            print(words)

实例三

需求:
解析下载图片数据。
网址:http://pic.netbian.com/4kfengjing/

思路:
主要是对 xpath表达式的书写,和怎样处理中文乱码。
xpath表达式可以从< div class = “slist”>标签开始,也可以从更上面的标签开始,比如< idv id = “main” > 可以从这里开始。
当然两个写法的含义是一样的。

# 1
src_list = tree.xpath('///div[@class="slist"]/ul/li/a/img/@src')
# 2
src_list = tree.xpath('//div[@id="main"]/div[3]/ul/li/a/img/@src')
# 3也可以选择简写
src_list = tree.xpath('///div[@class="slist"]//li/a/img/@src')

在这里插入图片描述

代码实现:

import requests
from lxml import etree
import os

if __name__ == '__main__':
    url = 'http://pic.netbian.com/4kfengjing/'
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36'
    }
    # 爬取页面源码数据 获取相应对象
    reponse = requests.get(url=url,headers=header)
    # 手动设置响应数据编码格式
    #reponse.encoding = 'utf-8'
    page_text = reponse.text
    # 数据解析,解析src的属性值,解析alt的属性值
    # 实例化etree
    tree = etree.HTML(page_text)
    # xpath表达式
    #src_list = tree.xpath('//div[@id="main"]/div[3]/ul/li/a/img/@src')
    #alt_list = tree.xpath('//div[@id="main"]/div[3]/ul/li/a/img/@alt')
    # 也可以这样写
    src_list = tree.xpath('//div[@class="slist"]/ul/li')

    # 创建一个文件夹
    if not os.path.exists('./tupian'):
        os.mkdir('./tupian')

    for li in src_list:
        img_src = 'http://pic.netbian.com' + li.xpath('./a/img/@src')[0]
        img_alt = li.xpath('./a/img/@alt')[0] + '.jpg'
        # 统用处理解决中文乱码的解决方法
        img_alt = img_alt.encode('iso-8859-1').decode('gbk')
        print(img_alt + " : " + img_src)
        # 图片地址转化成二进制
        img_data = requests.get(url=img_src,headers=header).content
        img_path = 'tupian/' + img_alt
        # 存储
        with open(img_path,'wb') as fp:
            fp.write(img_data)

案例四

需求:
解析出所给网址中全国城市的名称。
网址:https://www.aqistudy.cn/historydata/

思路:

首先实例化xpath对象,然后根据热门城市和全部城市的标签层级关系写出xpath表达式。解析表达式所对应的a标签,然后xpath函数返回一个列表,列表中存的就是a标签对应的城市。然后我们遍历列表即可。

在这里插入图片描述

# 热门城市
hot_city_list = tree.xpath('//div[@class="bottom"]/ul/li')

在这里插入图片描述

# 全部城市
all_city_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')

我们无法只通过一共 xpath表达式,将两个层级标签都表示数量,但是我们可以将两个层级标签写在一起,只需要用按位或 “ | ” 进行分割。这个意味着将第一个 xpath表达式或者第二个 xpath表达式,作用到 xpath函数当中。这样可以解析第一个表达式所对应的a标签定位到,也可以将第二个表达式所对应的a标签定位到。

# 用按位或进行分割 “ | ”
a_city_list = tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')

xpath返回一个列表,这个列表里面存的是热门城市加全部城市a标签所对应的一个列表。

然后遍历列表即可。

代码实现:
第一种写法:分别解析热门城市和所有城市,然后把这些城市的名字存入列表中。

import requests
from lxml import etree
import os

if __name__ == '__main__':
    url = 'https://www.aqistudy.cn/historydata/'
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36'
    }
    # 爬取页面源码数据 获取相应对象
    page_text = requests.get(url=url,headers=header).text

    # 数据解析
    # 实例化对象
    tree = etree.HTML(page_text)
    all_city = [] #所有的城市
    # 热门城市
    hot_city_list = tree.xpath('//div[@class="bottom"]/ul/li')
    for li in hot_city_list:
        hot_city_name = li.xpath('./a/text()')[0]
        all_city.append(hot_city_name)
        #print(hot_city_name)
    # 全部城市
    all_city_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')
    for li in all_city_list:
        all_city_name = li.xpath('./a/text()')[0]
        all_city.append(all_city_name)
        #print(all_city_name)
    print(all_city," 一共有:",len(all_city),"个城市")

第二种写法:用按位或将两个层级关系连接。

import requests
from lxml import etree

if __name__ == '__main__':
    url = 'https://www.aqistudy.cn/historydata/'
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36'
    }
    # 爬取页面源码数据 获取相应对象
    page_text = requests.get(url=url,headers=header).text

    # 数据解析
    # 实例化对象
    tree = etree.HTML(page_text)
    all_city = [] #所有的城市
    # 解析到热门城市和所有城市对应的a标签
    # 热门城市对应a标签层级关系://div[@class="bottom"]/ul/li/a
    # 所有城市对应a标签层级关系://div[@class="bottom"]/ul/div[2]/li/a
    # 用按位或进行分割 “ | ”
    a_city_list = tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')
    for a in a_city_list:
        city_name = a.xpath('./text()')[0]
        all_city.append(city_name)
    print(all_city," 一共有:",len(all_city),"个城市")

案例五

需求:

获取大学生求职简历模板的封面和名称,并且打印下来。
网址:http://sc.chinaz.com/jianli/daxuesheng.html

思路:

首先实例化xpath对象,然后根据图片和名称的标签层级关系写出xpath表达式。解析表达式所对应的a标签,然后xpath函数返回一个列表,列表中存的就是a标签对应的图片和名字。然后我们遍历列表即可。
在这里插入图片描述
当然我们发现每个模板的名字(中文信息)打印出来是乱码,这就需要我们更改编码格式。由于统用处理解决中文乱码的解决方法会报错,无法正常使用,所以我们使用手动设置响应数据编码格式。

reponse = requests.get(url=url,headers=header)
# 手动设置响应数据编码格式
reponse.encoding = 'utf-8'
page_text = reponse.text

我们如何打印所有页的数据呢?
我们观察发现,模板只有13页
而网址也很相似:

第一页:http://sc.chinaz.com/jianli/daxuesheng.html
第二页:http://sc.chinaz.com/jianli/daxuesheng_2.html
第三页:http://sc.chinaz.com/jianli/daxuesheng_3.html
第四页:http://sc.chinaz.com/jianli/daxuesheng_4.html
...
第十三页:http://sc.chinaz.com/jianli/daxuesheng_13.html

我们观察发现除了第一页外其它12页的url只有后面的数字不同,我们可以用字符串拼接的方法将url拼接出来,而那个不同的数字用for循环即可,然后将循环变量强制转换成str型的,最后拼接字符串。而第一页的特殊处理,独立输出,其他页(2到13页)的循环输出。

代码实现:

import requests
from lxml import etree

if __name__ == '__main__':
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36'
    }
    
    # 第一页
    # 第一页的url和其它页的有所不同
    url1 ='http://sc.chinaz.com/jianli/daxuesheng.html'
    # 爬取页面源码数据 获取相应对象
    reponse = requests.get(url=url1, headers=header)
    # 手动设置响应数据编码格式
    reponse.encoding = 'utf-8'
    page_text = reponse.text
    # 数据解析
    # 实例化对象
    tree = etree.HTML(page_text)
    src_list = tree.xpath('//div[@id="main"]/div/div')
    for a in src_list:
        img_src = a.xpath('./a/img/@src')[0]
        img_alt = a.xpath('./a/img/@alt')[0]
        # 统用处理解决中文乱码的解决方法
        # 会报错:UnicodeDecodeError: 'gbk' codec can't decode byte 0xbf in position 32: incomplete multibyte sequence
        # img_alt = img_alt.encode('iso-8859-1').decode('gbk')
        print(img_alt, ' : ', img_src)

    # 其他页(2到13页)
    url = 'http://sc.chinaz.com/jianli/daxuesheng'
    for num in range(2,13+1):
        num = str(num)
        new_url = url + '_' + num + '.html'
        # 爬取页面源码数据 获取相应对象
        reponse = requests.get(url=new_url, headers=header)
        # 手动设置响应数据编码格式
        reponse.encoding = 'utf-8'
        page_text = reponse.text
        # 数据解析
        # 实例化对象
        tree = etree.HTML(page_text)
        src_list = tree.xpath('//div[@id="main"]/div/div')
        for a in src_list:
            img_src = a.xpath('./a/img/@src')[0]
            img_alt = a.xpath('./a/img/@alt')[0]
            # 统用处理解决中文乱码的解决方法
            # 会报错:UnicodeDecodeError: 'gbk' codec can't decode byte 0xbf in position 32: incomplete multibyte sequence
            #img_alt = img_alt.encode('iso-8859-1').decode('gbk')
            print(img_alt,' : ',img_src)

六、补充知识

爬取的中文数据出现乱码

解决方法一:

手动设置响应数据编码格式。

reponse = requests.get(url=url,headers=header)
# 手动设置响应数据编码格式
reponse.encoding = 'utf-8'
page_text = reponse.text

解决方法二:

找到发生乱码对应的数据,对该数据进行:encode(‘iso-8859-1’).decode(‘gbk’) 操作。

# 统用处理解决中文乱码的解决方法
img_alt = img_alt.encode('iso-8859-1').decode('gbk')

案例三和案例五就是对这两种处理中文乱码的方法的应用。
案例三:是方法二。统用处理解决中文乱码的解决方法。
案例五:是方法一。手动设置响应数据编码格式。


按位或分割标签

当两个页面数据的层级标签类似时,我们可以使用按位或“ | ”,进行标签分割。这样可以将多个 xpath表达式解析。之间是或的关系。
这个意味着将第一个 xpath表达式或者第二个 xpath表达式,作用到 xpath函数当中。这样可以解析第一个表达式所对应的a标签定位到,也可以将第二个表达式所对应的a标签定位到。

样例:

# 用按位或进行分割 “ | ”
a_city_list = tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')

注:详细内容请看案例四

  • 9
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!要使用Python进行爬虫解析网页内容,可以使用xpath进行数据提取。我可以帮你了解如何使用xpath进行爬虫。 首先,你需要安装相关的库。在Python中,你可以使用`lxml`库来解析HTML或XML文档,并使用`requests`库来发送HTTP请求并获取网页内容。你可以使用以下命令安装这些库: ``` pip install lxml pip install requests ``` 安装完成后,你可以在Python脚本中导入这些库: ```python import requests from lxml import etree ``` 接下来,你可以使用`requests`库发送HTTP请求来获取网页的内容。例如,你可以使用以下代码获取一个网页的内容: ```python url = "http://example.com" response = requests.get(url) html_content = response.text ``` 获取到网页内容后,你可以使用`lxml`库中的`etree.HTML`函数将其转换为可供解析的HTML对象。然后,你可以使用xpath表达式从HTML对象中提取数据。例如,假设你要提取网页中所有`<a>`标签的文本和链接,你可以使用以下代码: ```python html = etree.HTML(html_content) links = html.xpath("//a") for link in links: text = link.text href = link.get("href") print(text, href) ``` 在上述代码中,`//a`是xpath表达式,用于匹配所有`<a>`标签。然后,通过遍历匹配结果,你可以获取每个`<a>`标签的文本和链接。 这只是一个简单的示例,你可以根据具体的网页结构和需求来编写更复杂的xpath表达式来提取数据。 希望这些信息能对你有所帮助!如果你有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值