当前位置: 首页 > article >正文

【Python爬虫(10)】解锁XPath:Python爬虫的精准导航仪(京东、淘宝实例)

【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取,还涉及数据处理与分析。无论是新手小白还是进阶开发者,都能从中汲取知识,助力掌握爬虫核心技能,开拓技术视野。

目录

  • 一、XPath 语法规则详解
    • 1.1 节点选择
    • 1.2 路径表达式
    • 1.3 谓语(Predicates)
    • 1.4 通配符与选取若干路径
  • 二、在 Python 中使用 lxml 库结合 XPath 解析网页
    • 2.1 lxml 库简介与安装
    • 2.2 lxml 库的基本使用
    • 2.3 XPath 与 lxml 结合实例
  • 三、实例:利用 XPath 爬取电商网站商品详情
    • 3.1 确定目标电商网站与商品
    • 3.2 分析网页结构
    • 3.3 编写爬虫代码
    • 3.4 运行与优化


一、XPath 语法规则详解

XPath(XML Path Language)是一种用于在 XML 文档中定位和选择元素的查询语言,在爬虫领域,它常用于从 HTML 或 XML 页面中提取数据,通过特定的语法规则,能够精准地定位到页面中的各种元素,为数据提取提供了极大的便利。接下来,我们将详细介绍 XPath 的语法规则。

1.1 节点选择

在 XPath 中,节点是构成 XML 或 HTML 文档的基本单元,包括元素节点、属性节点、文本节点等。通过不同的方式,可以选择特定的节点。

  • 通过元素名称选择:直接使用元素名称,可以选取该元素的所有子节点。例如,在一个 HTML 文档中,如果要选择所有的<div>元素,可以使用div,这将返回文档中所有的<div>元素节点。
  • 通过路径选择:使用路径表达式来指定节点的位置。路径表达式由一系列的步骤组成,每个步骤之间用斜杠(/)分隔。例如,/html/body/div表示从根节点开始,依次选择<html>元素、<body>元素下的所有<div>元素,这种方式能够精确地定位到特定层级结构下的元素。
  • 通过属性选择:利用@符号来选择具有特定属性的元素。例如,//div[@class=‘container’]表示选择所有具有class属性且属性值为container的<div>元素,这在根据元素的属性特征来筛选元素时非常有用。
  • 通过位置选择:使用方括号[]并在其中指定位置索引来选择特定位置的元素。需要注意的是,XPath 中的索引从 1 开始。例如,/html/body/div[1]表示选择<html>元素下<body>元素的第一个<div>子元素;/html/body/div[last()]表示选择最后一个<div>子元素 ,通过这种方式可以灵活地获取指定位置的元素。
  • 使用逻辑运算符:可以使用and、or等逻辑运算符来组合多个条件,实现更复杂的节点选择。例如,//div[@class=‘container’ and @id=‘main’]表示选择同时具有class属性值为container且id属性值为main的<div>元素;//div[@class=‘container’ or @class=‘sidebar’]表示选择class属性值为container或者sidebar的<div>元素,满足多种筛选需求。
  • 通过文本内容选择:使用text()函数来选择包含特定文本内容的元素。例如,//div[text()=‘Hello, World!’]表示选择文本内容为Hello, World!的<div>元素;//div[contains(text(), ‘Hello’)]表示选择文本内容包含Hello的<div>元素,方便根据文本信息定位元素。

1.2 路径表达式

路径表达式是 XPath 中用于定位节点的关键语法,它描述了如何在文档的节点树中导航到目标节点。

  • 绝对路径:以斜杠(/)开头,表示从根节点开始的完整路径。例如,/html/body/div/p表示从根节点<html>开始,依次经过<body>、<div>,最终选择<p>元素,这种路径明确指定了元素在文档中的层级位置,定位精确,但如果文档结构发生变化,可能会导致路径失效。
  • 相对路径:不以斜杠(/)开头,表示相对于当前节点的路径。例如,div/p表示选择当前节点下的<div>元素中的<p>元素;…/表示选择当前节点的父节点,相对路径更加灵活,能够适应文档结构的一些变化。
  • 路径表达式中的符号
    • 斜杠(/):用于分隔路径中的各个步骤,表示直接子节点关系。例如,/html/head/title表示选择<html>元素的直接子元素<head>,再选择<head>的直接子元素<title>。
    • 双斜杠(//):表示在文档中选择任意位置的节点,无论其在文档层次结构中的深度如何。例如,//div表示选择文档中所有的<div>元素,而不考虑它们的嵌套层次,这在快速定位特定类型元素时非常方便。
    • 点(.):表示当前节点。例如,./p表示选择当前节点下的<p>元素。
    • 双点(…):表示当前节点的父节点。例如,如果当前节点是<p>元素,…/则表示选择其父节点。
    • @:用于选择属性。例如,//div[@id]表示选择所有具有id属性的<div>元素;//@class表示选择所有的class属性节点。

1.3 谓语(Predicates)

谓语是 XPath 中用于进一步筛选节点的重要机制,它被嵌入在方括号[]中,通过指定条件来筛选出符合要求的节点。

  • 选取特定位置的节点:如前面提到的,使用索引来选取特定位置的节点。例如,/bookstore/book[2]表示选择<bookstore>元素下的第二个<book>元素;/bookstore/book[last()-1]表示选择<bookstore>元素下倒数第二个<book>元素,通过这种方式可以精确获取特定顺序的元素。
  • 选取具有特定属性值的节点:通过在方括号中指定属性和属性值来筛选节点。例如,//book[@category=‘fiction’]表示选择所有category属性值为fiction的<book>元素;//img[@src=‘image.jpg’]表示选择src属性值为image.jpg的<img>元素,根据属性特征筛选目标元素。
  • 选取具有特定文本内容的节点:结合text()函数,使用谓语来选取具有特定文本内容的节点。例如,//li[text()=‘Item 1’]表示选择文本内容为Item 1的<li>元素;//p[contains(text(), ‘重要内容’)]表示选择文本内容包含重要内容的<p>元素,方便根据文本信息筛选节点。

1.4 通配符与选取若干路径

在 XPath 中,通配符和选取若干路径的语法为数据提取提供了更高的灵活性。

  • 通配符

    • 星号(*):匹配任何元素节点。例如,/bookstore/*表示选择元素的所有子元素,无论这些子元素是什么类型。
    • @:匹配任何属性节点。例如,//div[@]表示选择所有带有属性的<div>元素,而不关心具体的属性名称。
    • node():匹配任何类型的节点,包括元素节点、文本节点、属性节点等。例如,//node()表示选择文档中的所有节点。
  • 选取若干路径:使用 “|” 运算符可以选取若干个路径。例如,//book/title | //book/price表示选取<book>元素的所有<title>和<price>元素;//title | //description表示选取文档中的所有<title>和<description>元素,通过这种方式可以一次性获取多个不同路径下的元素。

二、在 Python 中使用 lxml 库结合 XPath 解析网页

了解了 XPath 的语法规则后,接下来我们看看如何在 Python 中使用 lxml 库结合 XPath 来解析网页。lxml 库是 Python 中一个功能强大的解析库,它提供了高效的 XML 和 HTML 解析功能,并且对 XPath 有很好的支持,使得我们能够方便地从网页中提取所需的数据。

2.1 lxml 库简介与安装

lxml 是一个基于 C 语言的 libxml2 和 libxslt 库的 Python 库,它具有高性能、功能丰富的特点。在处理 XML 和 HTML 文档时,lxml 库提供了简单而直观的 API,能够快速地解析和提取数据。

安装 lxml 库非常简单,如果你已经安装了 pip(Python 的包管理工具),可以在命令行中使用以下命令进行安装:

pip install lxml

安装过程中,pip 会自动下载并安装 lxml 库及其依赖项。安装完成后,你就可以在 Python 项目中使用 lxml 库了。

2.2 lxml 库的基本使用

在使用 lxml 库之前,需要先导入相关的模块。通常,我们使用以下方式导入 lxml.etree 模块:

from lxml import etree
  • 解析本地文件:如果要解析本地的 HTML 或 XML 文件,可以使用etree.parse()方法。例如,假设我们有一个名为example.html的本地 HTML 文件,代码如下:
# 解析本地HTML文件
tree = etree.parse('example.html')
# 获取根节点
root = tree.getroot()

在上述代码中,etree.parse(‘example.html’)会读取并解析example.html文件,返回一个ElementTree对象,我们可以通过getroot()方法获取该对象的根节点。

  • 解析网页文件:当我们需要解析从网络获取的 HTML 内容时,可以使用etree.HTML()方法。例如,通过requests库获取网页内容后,使用etree.HTML()进行解析:
import requests
from lxml import etree

# 发送HTTP请求获取网页内容
url = 'https://example.com'
response = requests.get(url)
html_content = response.text

# 解析网页内容
html_tree = etree.HTML(html_content)

在这个例子中,首先使用requests.get(url)获取指定 URL 的网页内容,然后将返回的文本内容传递给etree.HTML()方法,该方法会将 HTML 内容解析为一个Element对象,后续就可以使用 XPath 表达式对其进行操作。

2.3 XPath 与 lxml 结合实例

下面通过一个具体的例子来展示如何使用 XPath 表达式在 lxml 解析的网页中提取数据。假设我们要从一个电商网站的商品列表页面中提取商品的标题、价格和链接信息。

以京东商城的手机商品列表页面(https://search.jd.com/Search?keyword=手机)为例,代码如下:

import requests
from lxml import etree

# 发送HTTP请求获取网页内容
url = 'https://search.jd.com/Search?keyword=手机'
headers = {
    'User - Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers)
html_content = response.text

# 解析网页内容
html_tree = etree.HTML(html_content)

# 使用XPath表达式提取商品信息
# 提取商品标题
titles = html_tree.xpath('//div[@class="gl-i-wrap"]//div[@class="p-name p-name-type-2"]//em/text()')
# 提取商品价格
prices = html_tree.xpath('//div[@class="gl-i-wrap"]//div[@class="p-price"]//i/text()')
# 提取商品链接
links = html_tree.xpath('//div[@class="gl-i-wrap"]//div[@class="p-name p-name-type-2"]//a/@href')

# 打印提取到的信息
for title, price, link in zip(titles, prices, links):
    print(f'商品标题: {title.strip()}')
    print(f'商品价格: {price}')
    print(f'商品链接: https:{link}')
    print('-' * 50)

在上述代码中:

  • 首先使用requests.get()方法发送 HTTP 请求获取网页内容,并设置了User - Agent头信息来模拟浏览器访问,以避免被网站识别为爬虫而拒绝访问。
  • 然后使用etree.HTML()方法将获取到的网页内容解析为Element对象。
  • 接着使用 XPath 表达式来提取商品的标题、价格和链接信息。例如,//div[@class=“gl-i-wrap”]//div[@class=“p-name p-name-type-2”]//em/text()表示选择所有具有gl-i-wrap类的div元素下,具有p-name p-name-type-2类的div元素下的em元素的文本内容,即商品标题;//div[@class=“gl-i-wrap”]//div[@class=“p-price”]//i/text()用于提取商品价格;//div[@class=“gl-i-wrap”]//div[@class=“p-name p-name-type-2”]//a/@href用于提取商品链接。
  • 最后,通过zip()函数将提取到的标题、价格和链接信息一一对应,并打印输出。

三、实例:利用 XPath 爬取电商网站商品详情

3.1 确定目标电商网站与商品

我们以淘宝为例,确定要爬取的商品为 “运动鞋”。淘宝作为国内知名的电商平台,商品种类丰富,页面结构较为典型,非常适合作为爬虫学习的实践对象。通过爬取淘宝上的运动鞋商品信息,我们可以深入了解如何在复杂的电商页面中运用 XPath 提取数据。

3.2 分析网页结构

在开始编写爬虫代码之前,需要仔细分析淘宝搜索 “运动鞋” 后的结果页面的 HTML 结构。打开浏览器,访问淘宝搜索页面(https://s.taobao.com/search?q=运动鞋),使用浏览器的开发者工具(通常可以通过右键点击页面,选择 “检查” 或 “审查元素” 打开)来查看页面的 HTML 代码。

  • 商品列表区域:在开发者工具中,可以发现商品信息都包含在特定的
    元素中,例如具有class属性为"item J_MouserOnverReq "的<div>元素,这个div元素可以看作是每个商品的容器,里面包含了商品的各种详细信息。
  • 商品名称:商品名称通常在<div>元素内部的<a>标签下的<img>标签的alt属性中,例如//div[@class="item J_MouserOnverReq "]/div/div/div/a/img/@alt,通过这个 XPath 表达式可以定位到商品名称。
  • 商品价格:价格信息在<div>元素内部的<div>标签下,且该<div>标签具有class属性为"price g_price g_price-highlight",具体的价格数值在<strong>标签内,对应的 XPath 表达式可以是//div[@class="item J_MouserOnverReq "]/div[2]/div/div/strong/text()。
  • 商品销量:销量信息在<div>元素内部的<div>标签下,该<div>标签具有class属性为"deal-cnt",使用 XPath 表达式//div[@class="item J_MouserOnverReq "]/div[2]/div/div[@class=“deal-cnt”]/text()可以提取到销量数据。
  • 店铺名称:店铺名称在<div>元素内部的<div>标签下,该<div>标签具有class属性为"shop",进一步在<a>标签下的<span>标签中可以找到店铺名称,对应的 XPath 表达式为//div[@class="item J_MouserOnverReq "]/div[2]/div[3]/div/a/span[2]/text()。
  • 商品链接:商品链接在<div>元素内部的<div>标签下,该<div>标签具有class属性为"pic",在<a>标签的href属性中存储着商品链接,XPath 表达式为//div[@class="item J_MouserOnverReq "]/div/div/div/a/@href。通过分析这些元素的位置和属性,我们能够准确地构建 XPath 表达式来提取所需的商品详情信息。

3.3 编写爬虫代码

import requests
from lxml import etree
import csv


def crawl_taobao_shoes():
    # 发送请求获取网页内容
    url = 'https://s.taobao.com/search?q=运动鞋'
    headers = {
        'User - Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()  # 检查请求是否成功
    except requests.exceptions.RequestException as e:
        print(f"请求发生异常: {e}")
        return

    # 使用lxml库结合XPath提取商品信息
    html_tree = etree.HTML(response.text)
    items = html_tree.xpath('//div[@class="item J_MouserOnverReq  "]')
    data = []
    for item in items:
        # 提取商品名称
        title = item.xpath('div/div/div/a/img/@alt')[0].strip() if item.xpath('div/div/div/a/img/@alt') else ''
        # 提取商品价格
        price = item.xpath('div[2]/div/div/strong/text()')[0].strip() if item.xpath('div[2]/div/div/strong/text()') else ''
        # 提取商品销量
        sales = item.xpath('div[2]/div/div[@class="deal-cnt"]/text()')[0].strip() if item.xpath(
            'div[2]/div/div[@class="deal-cnt"]/text()') else ''
        # 提取店铺名称
        shop_name = item.xpath('div[2]/div[3]/div/a/span[2]/text()')[0].strip() if item.xpath(
            'div[2]/div[3]/div/a/span[2]/text()') else ''
        # 提取商品链接
        link = 'https:' + item.xpath('div/div/div/a/@href')[0].strip() if item.xpath('div/div/div/a/@href') else ''

        data.append([title, price, sales, shop_name, link])

    # 保存提取数据到CSV文件
    with open('taobao_shoes.csv', 'w', newline='', encoding='utf - 8') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(['商品名称', '商品价格', '商品销量', '店铺名称', '商品链接'])
        for row in data:
            writer.writerow(row)


if __name__ == "__main__":
    crawl_taobao_shoes()

在上述代码中:

  • 首先,定义了要爬取的 URL 和请求头headers,通过设置User - Agent头信息,模拟浏览器发送请求,以避免被淘宝的反爬虫机制检测到。
  • 使用requests.get()方法发送 HTTP GET 请求,获取网页内容。如果请求过程中发生异常,例如网络问题、请求被拒绝等,会捕获requests.exceptions.RequestException异常,并打印异常信息。
  • 利用etree.HTML()方法将获取到的网页内容解析为Element对象,以便后续使用 XPath 表达式进行数据提取。
  • 通过 XPath 表达式从解析后的 HTML 树中提取商品名称、价格、销量、店铺名称和商品链接等信息。在提取过程中,使用了条件判断来处理可能出现的提取失败情况,例如当某个商品的某个信息不存在时,将其设置为空字符串。
  • 将提取到的数据存储在一个列表data中,然后使用 Python 的csv模块将数据写入到名为taobao_shoes.csv的 CSV 文件中。在写入文件时,首先写入表头,然后逐行写入数据。

3.4 运行与优化

运行上述爬虫代码,可能会遇到一些问题,需要进行相应的处理和优化:

  • 异常处理
    • 网络请求失败:尽管在代码中已经使用了try - except块来捕获requests.exceptions.RequestException异常,但还可以进一步细化异常处理。例如,可以根据不同的异常类型进行不同的处理,对于网络超时异常requests.exceptions.Timeout,可以设置重试机制,增加请求成功的概率;对于连接错误异常requests.exceptions.ConnectionError,可以提示用户检查网络连接。
    • 数据提取失败:在提取数据时,可能会因为网页结构的变化或者其他原因导致数据提取失败。可以在代码中增加更多的容错处理,例如在提取数据前,先检查对应的 XPath 表达式是否能找到节点,如果找不到,可以记录日志并跳过该商品的处理,或者尝试使用其他的 XPath 表达式进行提取。
  • 优化爬虫
    控制请求频率:淘宝等电商网站通常会有反爬虫机制,为了避免被封禁 IP,需要控制请求频率。可以在每次请求之间添加适当的时间间隔,例如使用time.sleep()函数,设置一个合理的睡眠时间,模拟人类正常的浏览行为。例如,在发送请求获取网页内容的代码部分添加如下代码:
import time

# 发送请求获取网页内容
url = 'https://s.taobao.com/search?q=运动鞋'
headers = {
    'User - Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
for _ in range(3):  # 尝试3次
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()  # 检查请求是否成功
        break
    except requests.exceptions.Timeout:
        print("请求超时,重试中...")
        time.sleep(5)  # 等待5秒后重试
    except requests.exceptions.RequestException as e:
        print(f"请求发生异常: {e}")
        break
else:
    print("多次请求均失败,终止程序")
    exit()

在上述代码中,使用了一个for循环来尝试发送请求 3 次,每次请求失败如果是超时异常,就等待 5 秒后重试,如果 3 次都失败,则打印提示信息并终止程序。

  • 提高代码效率:如果需要爬取多页数据,可以使用多线程或异步编程来提高爬取效率。例如,使用asyncio库结合aiohttp库进行异步请求,减少请求的等待时间。同时,可以对提取到的数据进行预处理,例如去除数据中的空格、特殊字符等,提高数据的质量,方便后续的数据分析。

http://www.kler.cn/a/550690.html

相关文章:

  • DeepSeek本地企业知识库搭建思路
  • 机器学习_17 K近邻算法知识点总结
  • 【R语言】GitHub Copilot安装-待解决
  • 【ETL】从理论到Python实践的数据处理
  • vue 判断字符串开头是http或者https
  • 如何通过阿里云内容安全在数字时代获得“第一防线”?
  • NBT群落物种级丰度鉴定新方法sylph
  • Qt常用控件之按钮QPushButton
  • 【Java基础】数组性能优化
  • STM32 CubeMx配置串口收发使用DMA并调用Idle模式(二)
  • qt小项目:表白窗口(窗口,信号与槽)
  • UI自动化教程 —— 元素定位技巧:精确找到你需要的页面元素
  • Node.js 中的 fs 模块详解
  • AI时代:前端开发的职业发展路径拓宽
  • 洛谷P8707 [蓝桥杯 2020 省 AB1] 走方格
  • Qt 中使用 SQLite 数据库的完整指南
  • 【MyBatis】预编译SQL与即时SQL
  • 『大模型笔记』Jason Wei: 大语言模型的扩展范式!
  • 使用Navicat for MySQL工具连接本地虚拟机上的MySQL
  • Ubantu安装Prometheus、Grafana、node_exporter、elasticsearch_exporter监控运行状态