本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Scrapy 是一个高效的 Python 网络爬虫框架,适用于自动化抓取和处理网页数据。本文档基于 Python 3.7,通过一个简单爬虫项目,演示了如何使用 Scrapy 抓取网站 http://www.okhqb.com/ 的文章信息。内容涵盖 Scrapy 环境搭建、爬虫类定义、CSS 选择器提取数据、分页处理、数据结构定义、配置设置及爬虫运行等完整流程。本项目经过实操验证,适合初学者掌握 Scrapy 基础开发技巧,并为后续复杂项目打下基础。
python3.7 scrapy简单爬虫入门

1. Scrapy 框架简介与核心概念

Scrapy 是一个基于 Python 的高效、可扩展的网络爬虫框架,专为大规模网页抓取任务设计。它内置了异步请求处理机制,基于 Twisted 库实现高并发网络操作,显著提升了爬虫效率。Scrapy 的设计采用了模块化架构,包含六大核心组件:引擎(Engine)、调度器(Scheduler)、下载器(Downloader)、Spider、Item Pipeline 和 Downloader Middleware,各组件之间通过事件驱动模型协同工作。

下图展示了 Scrapy 的核心组件及其交互流程:

graph TD
    A[Engine] --> B[Scheduler]
    A --> C[Downloader]
    A --> D[Spider]
    D --> A
    C --> A
    A --> E[Item Pipeline]
    B --> A

引擎负责控制整个数据流动,Spider 负责定义初始请求并解析响应提取数据或生成新请求,Downloader 负责实际的网络请求处理,而 Item Pipeline 则用于数据清洗与持久化存储。这种结构使得 Scrapy 既灵活又强大,适用于从简单网页采集到复杂数据抓取系统等多种场景。

2. Python 3.7 环境搭建与 Scrapy 安装

在进入 Scrapy 的实际开发之前,搭建一个稳定、高效的 Python 开发环境是至关重要的。本章将详细讲解如何在不同操作系统下安装 Python 3.7,并配置 Scrapy 框架及其相关依赖库。此外,还将介绍开发工具的配置与调试方法,为后续 Scrapy 项目的开发打下坚实的基础。

2.1 Python 3.7 的安装与环境配置

Python 3.7 是一个被广泛使用的版本,因其稳定性和兼容性在许多项目中依然被采用。为了确保 Scrapy 框架能够顺利运行,我们需要正确安装并配置 Python 3.7 及其相关工具。

2.1.1 Windows、Linux 和 macOS 平台下的 Python 安装方法

Windows 平台安装 Python 3.7

在 Windows 上安装 Python 最简单的方法是通过官方安装包进行安装:

  1. 访问 Python 官方网站 ,下载适用于 Windows 的 Python 3.7 安装包(推荐使用 Windows x86-64 executable installer )。
  2. 双击下载的 .exe 文件,勾选“Add Python 3.7 to PATH”选项,点击“Install Now”进行安装。
  3. 安装完成后,打开命令行(cmd)输入以下命令验证是否安装成功:
python --version

如果输出 Python 3.7.x ,说明安装成功。

Linux 平台安装 Python 3.7

在 Ubuntu 或 Debian 系统中,可以通过以下命令安装 Python 3.7:

sudo apt update
sudo apt install python3.7

验证安装:

python3.7 --version
macOS 平台安装 Python 3.7

macOS 通常自带 Python 2.x,推荐使用 Homebrew 安装 Python 3.7:

brew install python@3.7

验证安装:

python3.7 --version

2.1.2 pip 工具的使用与虚拟环境配置(venv/virtualenv)

pip 是 Python 的包管理工具,用于安装和管理第三方库。虚拟环境可以为每个项目创建独立的 Python 环境,避免依赖冲突。

使用 venv 创建虚拟环境(Python 3.7 内置)
# 创建虚拟环境
python3.7 -m venv myenv

# 激活虚拟环境
# Windows
myenv\Scripts\activate.bat

# Linux/macOS
source myenv/bin/activate

激活后命令行前缀会显示虚拟环境名称。

使用 virtualenv

如果使用 virtualenv ,需先安装:

pip install virtualenv

创建虚拟环境:

virtualenv -p python3.7 myenv
source myenv/bin/activate  # Linux/macOS
pip 常用命令示例
# 安装库
pip install requests

# 升级库
pip install --upgrade requests

# 卸载库
pip uninstall requests

# 导出依赖
pip freeze > requirements.txt

# 安装依赖
pip install -r requirements.txt

表格:常用 pip 命令总结

命令 说明
pip install package_name 安装指定包
pip uninstall package_name 卸载指定包
pip list 查看已安装包列表
pip show package_name 查看包详细信息
pip freeze > requirements.txt 导出当前环境依赖列表

2.2 Scrapy 框架的安装与验证

Scrapy 是一个强大的爬虫框架,依赖较多的库,如 lxml Twisted 等。下面将介绍如何正确安装 Scrapy 及其依赖,并验证是否安装成功。

2.2.1 使用 pip 安装 Scrapy 及其依赖库

在虚拟环境中执行以下命令安装 Scrapy:

pip install scrapy

注意:Scrapy 在 Windows 上依赖 Microsoft C++ Build Tools,如果没有安装,会提示错误。

2.2.2 测试 Scrapy 安装是否成功

安装完成后,执行以下命令验证是否安装成功:

scrapy version

如果输出类似 Scrapy 2.10.1 ,说明安装成功。

也可以创建一个测试项目验证:

scrapy startproject testproject
cd testproject
scrapy crawl example  # 假设你已经创建了一个名为 example 的 Spider

2.2.3 常见安装问题及解决办法

问题 1:lxml 安装失败

解决方案

pip install --upgrade pip
pip install --pre lxml

或使用 wheel 文件安装:

pip install lxml-4.9.3-cp37-cp37m-win_amd64.whl
问题 2:Windows 上缺少 Microsoft C++ Build Tools

解决方案

  1. 访问 Microsoft C++ Build Tools 下载安装包。
  2. 安装时勾选 “C++ build tools” 工作负载。
  3. 安装完成后重新尝试安装 Scrapy。
问题 3:Twisted 安装失败

解决方案

pip install twisted

若仍失败,可下载对应的 .whl 文件手动安装:

pip install Twisted-22.10.0-cp37-cp37m-win_amd64.whl
问题 4:SSL 证书错误(macOS)

解决方案

/Applications/Python\ 3.7/Install\ Certificates.command

运行此脚本可修复 macOS 上的 SSL 证书问题。

mermaid 流程图:Scrapy 安装流程图

graph TD
    A[安装 Python 3.7] --> B[安装 pip]
    B --> C[安装虚拟环境工具]
    C --> D[创建虚拟环境]
    D --> E[使用 pip 安装 Scrapy]
    E --> F{是否安装成功?}
    F -->|是| G[验证 Scrapy 版本]
    F -->|否| H[查看错误日志]
    H --> I[解决依赖问题]
    I --> E

2.3 开发工具推荐与集成开发环境搭建

良好的开发工具可以显著提高开发效率。本节将介绍 PyCharm 和 VS Code 两款主流 Python 开发工具的配置方法,并讲解如何调试 Scrapy 项目。

2.3.1 PyCharm、VS Code 配置 Python 开发环境

PyCharm 配置
  1. 下载安装 PyCharm Community Edition
  2. 打开 PyCharm,创建新项目,选择之前创建的虚拟环境路径(如 myenv )。
  3. 在 Settings(设置)中选择 Project Interpreter,确认当前使用的是虚拟环境的 Python 解释器。
  4. 安装 Scrapy 插件(File → Settings → Plugins → 搜索 Scrapy)。
VS Code 配置
  1. 下载安装 VS Code
  2. 安装 Python 插件(搜索 Python by Microsoft)。
  3. 打开项目文件夹,按下 Ctrl + Shift + P ,输入 Python: Select Interpreter ,选择虚拟环境中的 Python 3.7。
  4. 安装 Scrapy 插件(可选)。
示例代码:Scrapy 项目结构(在 VS Code 或 PyCharm 中打开)
myproject/
├── myproject/
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders/
│       └── example_spider.py
└── scrapy.cfg

2.3.2 调试 Scrapy 项目的基本方法

Scrapy 本身不支持直接在 IDE 中运行调试,但可以通过以下方式实现调试:

方法一:使用 scrapy crawl 命令配合日志
scrapy crawl example -L DEBUG

通过日志查看运行过程中的请求、响应、数据提取等信息。

方法二:使用 pdb 调试器插入断点

在 Spider 中插入以下代码:

import pdb; pdb.set_trace()

运行命令后程序会在断点处暂停,可进行变量查看、单步执行等操作。

方法三:在 PyCharm 中配置运行脚本
  1. 在项目根目录创建一个 main.py 脚本:
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from myproject.spiders.example import ExampleSpider

process = CrawlerProcess(get_project_settings())
process.crawl(ExampleSpider)
process.start()
  1. 在 PyCharm 中右键点击 main.py ,选择 “Debug” 即可进行断点调试。
方法四:使用 scrapy shell 进行交互式调试
scrapy shell 'https://example.com'

进入交互式 shell 后,可使用 response 对象测试 CSS 选择器等提取逻辑。

表格:调试工具对比

工具/方法 是否支持断点 是否支持变量查看 是否支持日志输出 推荐使用场景
scrapy crawl 简单运行测试
pdb 快速插入断点调试
PyCharm Debug 专业调试,复杂项目
scrapy shell ✅(交互式) 快速验证提取逻辑

mermaid 流程图:Scrapy 调试流程图

graph TD
    A[选择调试方式] --> B{是否需要断点?}
    B -->|是| C[pdb 调试]
    B -->|否| D[scrapy crawl 日志]
    A --> E{是否使用 IDE?}
    E -->|是| F[PyCharm Debug]
    E -->|否| G[scrapy shell 交互式]

本章详细讲解了 Python 3.7 的安装与环境配置、Scrapy 的安装与常见问题解决,以及开发工具的配置与调试方法。通过本章内容,读者应能顺利完成开发环境的搭建,并具备运行和调试 Scrapy 项目的能力,为后续章节的内容学习打下坚实基础。

3. Scrapy 项目创建与结构说明

在掌握了 Scrapy 的基本概念与环境搭建后,接下来我们将正式进入 Scrapy 的实战阶段。本章将从创建第一个 Scrapy 项目开始,逐步介绍其目录结构、核心组件的功能划分,以及 Spider 类的基本结构和运行机制。此外,我们还将深入解析 settings.py 文件的配置方式,帮助读者构建一个完整且可扩展的 Scrapy 爬虫项目。

3.1 创建第一个 Scrapy 项目

3.1.1 使用 scrapy startproject 命令创建项目

Scrapy 提供了一个便捷的命令行工具 startproject ,可以快速生成一个标准的项目结构。执行以下命令即可创建一个新的 Scrapy 项目:

scrapy startproject myproject

执行后,系统会在当前目录下生成一个名为 myproject 的目录,其结构如下:

myproject/
├── myproject/
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders/
│       └── __init__.py
└── scrapy.cfg

参数说明:

  • scrapy startproject 是 Scrapy 的项目初始化命令。
  • myproject 是用户自定义的项目名称,可根据需要替换。

逻辑分析:

  • scrapy.cfg 是项目的配置文件,用于指定项目模块路径和部署设置。
  • 内层 myproject/ 是项目的 Python 包目录,包含多个核心模块文件。

3.1.2 项目结构解析:spiders、items、pipelines、settings 等目录与文件的作用

文件/目录 作用说明
scrapy.cfg 项目配置文件,定义了项目的模块名称和部署配置。
items.py 定义爬取数据的数据结构(Item 类),用于规范数据字段。
spiders/ 存放所有的 Spider 类文件,每个 Spider 负责爬取特定网站的数据。
pipelines.py 数据处理管道,用于数据清洗、去重、存储等操作。
settings.py 项目的全局配置文件,控制爬虫行为(如 User-Agent、下载延迟等)。
middlewares.py 中间件模块,用于修改请求和响应,实现反爬策略、动态 User-Agent 等功能。

代码示例:items.py

import scrapy

class MyprojectItem(scrapy.Item):
    title = scrapy.Field()
    link = scrapy.Field()
    desc = scrapy.Field()

逻辑分析:

  • MyprojectItem 继承自 scrapy.Item ,定义了三个字段: title link desc
  • 每个字段使用 scrapy.Field() 声明,表示一个数据项。

3.2 Spider 爬虫类的基本结构与运行机制

3.2.1 Spider 类的继承与基本方法(start_requests、parse)

Scrapy 的核心是 Spider 类,所有自定义爬虫都必须继承自 scrapy.Spider 。其基本结构如下:

import scrapy

class ExampleSpider(scrapy.Spider):
    name = "example"
    def start_requests(self):
        urls = [
            'http://example.com/page1',
            'http://example.com/page2',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        self.log(f'Visited {response.url}')

参数说明:

  • name :Spider 的唯一标识符,用于启动爬虫时指定。
  • start_requests() :定义初始请求,返回一个或多个 Request 对象。
  • parse() :回调函数,处理响应内容,提取数据或进一步发起请求。

逻辑分析:

  • start_requests() 方法生成初始请求,并通过 yield 返回多个 Request
  • 每个 Request 指定一个 callback 函数(如 self.parse ),用于处理响应。
  • 当爬虫获取响应后,Scrapy 自动调用对应的回调函数。

3.2.2 请求与响应对象(Request、Response)的工作流程

Scrapy 的请求与响应机制是异步非阻塞的,基于 Twisted 库实现。其工作流程如下:

graph TD
    A[Spider生成Request] --> B[引擎接收请求]
    B --> C[调度器将请求入队]
    C --> D[下载器发起网络请求]
    D --> E[获取Response]
    E --> F[引擎传递Response给Spider]
    F --> G{是否提取数据或继续请求}
    G -->|是| H[调用parse方法处理]
    G -->|否| I[结束]
    H --> J[生成Item或新Request]
    J --> K{是否继续}
    K -->|是| A
    K -->|否| L[保存Item]

流程说明:

  1. Spider 生成初始请求(Request)。
  2. Scrapy 引擎将请求交给调度器(Scheduler)。
  3. 调度器将请求放入队列,并由下载器(Downloader)发起网络请求。
  4. 下载器获取响应(Response)后,Scrapy 引擎将响应交还给 Spider。
  5. Spider 的 parse 方法处理响应,提取数据或生成新的请求。
  6. 如果仍有请求未处理,流程重复;否则保存提取的数据(Item)。

3.3 项目配置与全局设置(settings.py)

3.3.1 用户代理、请求头、下载延迟等基本配置项

settings.py 是 Scrapy 项目的全局配置文件,用于控制爬虫行为。以下是一些常用配置项:

# 设置 User-Agent
USER_AGENT = 'myproject (+http://www.example.com)'

# 设置请求头
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
}

# 设置下载延迟(单位:秒)
DOWNLOAD_DELAY = 1.5

# 启用 Cookie
COOKIES_ENABLED = True

# 启用重试机制
RETRY_ENABLED = True
RETRY_TIMES = 3

参数说明:

  • USER_AGENT :设置爬虫的 User-Agent,用于伪装浏览器访问。
  • DEFAULT_REQUEST_HEADERS :默认请求头信息,可自定义 Accept、Accept-Language 等。
  • DOWNLOAD_DELAY :两次请求之间的延迟时间,防止频繁访问导致被封 IP。
  • COOKIES_ENABLED :是否启用 Cookie 支持。
  • RETRY_ENABLED :是否启用请求失败重试机制。
  • RETRY_TIMES :最大重试次数。

3.3.2 启用中间件与 Item Pipeline 的方式

启用中间件

Scrapy 支持多种中间件,包括下载中间件(Downloader Middleware)和蜘蛛中间件(Spider Middleware)。启用方式如下:

# settings.py
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomUserAgentMiddleware': 543,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
}

参数说明:

  • RandomUserAgentMiddleware :自定义的中间件,用于随机切换 User-Agent。
  • HttpProxyMiddleware :Scrapy 自带的代理中间件。
  • 数字代表中间件的优先级,数字越小越早执行。
启用 Item Pipeline

Item Pipeline 用于处理爬取的数据,如清洗、去重、存储等。启用方式如下:

# settings.py
ITEM_PIPELINES = {
    'myproject.pipelines.ValidatePipeline': 300,
    'myproject.pipelines.SaveToDatabasePipeline': 800,
}

参数说明:

  • ValidatePipeline :数据校验管道,用于检查字段是否完整。
  • SaveToDatabasePipeline :数据存储管道,将数据保存到数据库。
  • 数字表示执行顺序,数字越小越先执行。

代码示例:ValidatePipeline

class ValidatePipeline:
    def process_item(self, item, spider):
        if not item['title']:
            raise DropItem("Missing title in %s" % item)
        return item

逻辑分析:

  • process_item() 是每个 Pipeline 必须实现的方法。
  • 如果 item['title'] 为空,抛出 DropItem 异常,该数据将被丢弃。
  • 否则返回 item ,继续传递给下一个 Pipeline。

小结

本章从项目创建入手,详细介绍了 Scrapy 项目的目录结构与各组件的作用,帮助开发者构建一个规范的爬虫项目。随后我们解析了 Spider 类的基本结构和运行机制,重点讲解了 start_requests() parse() 方法的使用,以及 Request Response 的工作流程。最后,我们深入探讨了 settings.py 的配置方法,包括用户代理、请求头、下载延迟等基础设置,以及中间件和 Item Pipeline 的启用方式。

通过本章内容,开发者已经具备了独立创建和配置 Scrapy 项目的能力,为后续数据提取与处理打下了坚实基础。

4. 数据提取与 CSS 选择器实战

数据提取是网络爬虫的核心任务之一,而 Scrapy 提供了强大的 CSS 选择器支持,使得开发者可以高效地从 HTML 页面中提取所需信息。本章将从 HTML 结构分析入手,逐步讲解 CSS 选择器的使用方法,并结合 Scrapy 的实际应用场景,演示如何提取网页数据、定义数据结构(Item)并进行基本清洗。通过本章的学习,读者将掌握从网页中提取结构化数据的完整流程,并具备独立开发数据抓取模块的能力。

4.1 网页结构分析与选择器基础

4.1.1 HTML 文档结构概述

HTML(HyperText Markup Language)是构建网页内容的基础语言。一个典型的 HTML 文档由多个嵌套的标签组成,形成树状结构,称为 DOM(Document Object Model)。例如,以下是一个简单的 HTML 片段:

<!DOCTYPE html>
<html>
<head>
    <title>示例页面</title>
</head>
<body>
    <div class="content">
        <h1 id="main-title">欢迎来到示例网站</h1>
        <p class="description">这是一个用于演示的网页。</p>
        <ul>
            <li>项目1</li>
            <li>项目2</li>
            <li>项目3</li>
        </ul>
    </div>
</body>
</html>

在这个 HTML 文档中, <div> <h1> <p> <ul> 等标签构成了页面的结构。其中:

  • class 属性用于指定 CSS 类名,多个元素可以共享相同的类;
  • id 属性用于唯一标识一个元素;
  • <ul> <li> 表示无序列表及其列表项。

了解 HTML 的基本结构是使用 CSS 选择器进行数据提取的前提。

4.1.2 CSS 选择器语法详解(元素选择、类选择、ID 选择、属性选择等)

CSS 选择器是一种用于定位 HTML 元素的语法。Scrapy 的 response.css() 方法支持 CSS 选择器语法,以下是常见选择器的使用方法:

选择器类型 示例 说明
元素选择器 div 选择所有 <div> 元素
类选择器 .content 选择所有 class="content" 的元素
ID 选择器 #main-title 选择 id="main-title" 的元素
属性选择器 [href] 选择所有包含 href 属性的元素
层级选择器 div p 选择 <div> 下的所有 <p> 元素
伪类选择器 li:nth-child(2) 选择第二个 <li> 元素
通配符选择器 * 选择所有元素

下面通过几个示例来演示如何使用这些选择器:

# 假设 response 是 Scrapy 的响应对象
title = response.css('h1#main-title::text').get()  # 提取 ID 为 main-title 的 h1 标签的文本内容
description = response.css('.description::text').get()  # 提取 class 为 description 的 p 标签的文本内容
items = response.css('ul li::text').getall()  # 提取所有 li 标签的文本内容,返回列表

代码逐行解读:

  1. response.css('h1#main-title::text') :选择所有 h1 标签中 ID 为 main-title 的元素,并提取其文本内容。
  2. .get() :返回第一个匹配项的内容,适用于提取唯一结果。
  3. .getall() :返回所有匹配项的内容,适用于提取多个结果。

逻辑说明 ::text 表示提取元素的文本内容,如果不加 ::text ,则返回的是 Selector 对象。Scrapy 支持链式调用,例如 response.css('div.content').css('p.description::text')

4.2 使用 Scrapy 提取网页数据

4.2.1 在 parse 方法中使用 response.css 提取数据

Scrapy 的 Spider 类中, parse 方法用于处理响应并提取数据。我们以一个简单的网页为例,演示如何使用 response.css() 提取数据。

import scrapy

class ExampleSpider(scrapy.Spider):
    name = 'example_spider'
    start_urls = ['http://example.com']

    def parse(self, response):
        # 提取标题
        title = response.css('h1#main-title::text').get()
        # 提取描述
        description = response.css('.description::text').get()
        # 提取列表项
        items = response.css('ul li::text').getall()

        yield {
            'title': title,
            'description': description,
            'items': items
        }

代码逐行解读:

  1. class ExampleSpider(scrapy.Spider) :定义一个 Spider 类,继承 Scrapy 的基类 scrapy.Spider
  2. name = 'example_spider' :设置爬虫的唯一标识。
  3. start_urls = ['http://example.com'] :设置起始 URL 列表。
  4. def parse(self, response): :定义解析方法,接收响应对象。
  5. title = response.css(...) :使用 CSS 选择器提取标题、描述和列表项。
  6. yield { ... } :将提取的数据以字典形式返回,供后续处理(如 Item Pipeline)使用。

执行逻辑 :当 Spider 请求 http://example.com 并获取响应后,会调用 parse 方法处理响应内容,提取所需数据,并通过 yield 传递给后续的处理流程。

4.2.2 提取文本、属性值及多层级嵌套数据

除了提取文本内容,CSS 选择器还可以提取元素的属性值(如 href src )以及嵌套结构中的数据。

提取属性值
links = response.css('a::attr(href)').getall()

该语句提取所有 <a> 标签的 href 属性值,适用于提取链接列表。

提取嵌套结构数据
for product in response.css('div.product'):
    name = product.css('h2::text').get()
    price = product.css('.price::text').get()
    yield {
        'name': name,
        'price': price
    }

该代码使用嵌套选择器,先提取每个商品的 <div> 容器,再在容器内部提取商品名称和价格。

4.3 数据字段定义与 Items 使用

4.3.1 定义 Item 类规范数据结构

在 Scrapy 中,Item 是一种用于定义爬取数据结构的类,类似于 Python 的数据模型。使用 Item 可以提高代码的可读性和可维护性。

import scrapy

class ProductItem(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    url = scrapy.Field()

参数说明:

  • name :商品名称;
  • price :商品价格;
  • url :商品链接。

4.3.2 在 Spider 中填充 Item 字段

在 Spider 中,我们可以使用定义好的 Item 类来填充数据:

def parse(self, response):
    for product in response.css('div.product'):
        item = ProductItem()
        item['name'] = product.css('h2::text').get()
        item['price'] = product.css('.price::text').get()
        item['url'] = product.css('a::attr(href)').get()
        yield item

代码逻辑说明:

  1. item = ProductItem() :实例化一个 ProductItem 对象。
  2. item['name'] = ... :为 Item 的字段赋值。
  3. yield item :将填充好的 Item 对象返回,供后续处理使用。

4.3.3 数据清洗与验证(可选)

Scrapy 支持在 Item 中定义数据清洗逻辑,例如:

import scrapy
from scrapy.loader.processors import MapCompose, TakeFirst

def clean_price(value):
    return value.replace('$', '').strip()

class ProductItem(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field(
        input_processor=MapCompose(clean_price),
        output_processor=TakeFirst()
    )
    url = scrapy.Field()

参数说明:

  • MapCompose(clean_price) :对输入的每个值应用 clean_price 函数进行清洗;
  • TakeFirst() :在输出时只取第一个值。

逻辑说明 clean_price 函数将价格中的 $ 符号去掉,并去除前后空格,适用于清洗货币格式数据。

本章小结与下一章预告

本章系统讲解了如何使用 CSS 选择器在 Scrapy 中提取网页数据,并通过 Item 类规范数据结构。我们从 HTML 结构分析入手,介绍了 CSS 选择器的基本语法和使用技巧,随后通过实际代码示例演示了如何在 Spider 的 parse 方法中提取数据,并定义了数据清洗逻辑。

下一章我们将深入探讨分页爬取与链接提取器的高级应用,包括如何构造分页请求、使用 LinkExtractor CrawlSpider 实现自动链接提取,以及下载中间件的使用技巧。这些内容将进一步提升爬虫的自动化能力和应对复杂网页结构的能力。

5. 分页爬取与链接提取器的高级应用

在爬虫开发过程中,面对结构化数据源时,尤其是具有分页机制的网页,分页爬取成为必不可少的技术手段。Scrapy 提供了强大的工具支持,如使用 yield 构建多个请求对象,以及通过 LinkExtractor CrawlSpider 自动提取页面链接,从而实现高效、可扩展的爬取逻辑。本章将深入讲解这些高级应用,并结合下载中间件的配置,提升爬虫的健壮性与反爬应对能力。

5.1 分页爬取的基本原理与实现方式

5.1.1 构造分页请求 URL

在 Web 页面中,许多内容是以分页形式呈现的,例如商品列表、新闻列表、论坛帖子等。每一页的 URL 通常具有一定的规律,例如:

https://example.com/page/1
https://example.com/page/2
https://example.com/page/3

构造分页请求的关键在于识别 URL 中的分页参数,并通过 Python 的字符串拼接或格式化方式动态生成。

示例代码:构造分页 URL

import scrapy

class PaginatedSpider(scrapy.Spider):
    name = 'paginated_spider'
    base_url = 'https://example.com/page/'

    def start_requests(self):
        for page in range(1, 6):  # 爬取前5页
            url = self.base_url + str(page)
            yield scrapy.Request(url=url, callback=self.parse)

代码逻辑分析:

  • start_requests() 方法用于生成初始请求列表。
  • 使用 for 循环构造 1 到 5 的页码,并拼接为完整 URL。
  • yield scrapy.Request() 生成多个请求对象,Scrapy 引擎会异步调度这些请求。

5.1.2 使用 yield 生成多个 Request 对象

Scrapy 的异步特性依赖于 yield 关键字,它使得函数在执行过程中可以暂停并返回一个对象(如 Request ),随后在事件循环中恢复执行。

yield 的优势:

  • 节省内存 :无需一次性生成所有请求对象。
  • 非阻塞处理 :每个请求独立执行,不影响其他请求。
  • 流程清晰 :适用于递归式或链式请求。

示例:递归分页请求

def parse(self, response):
    # 提取当前页数据
    for item in response.css('div.item'):
        yield {
            'title': item.css('h2::text').get(),
            'price': item.css('span.price::text').get()
        }

    # 提取下一页链接
    next_page = response.css('a.next::attr(href)').get()
    if next_page:
        yield response.follow(next_page, callback=self.parse)

代码解释:

  • parse() 方法首先提取当前页的数据。
  • 然后查找下一页链接,如果存在则通过 response.follow() 构造新的请求。
  • 通过 yield 实现递归分页爬取,直到没有“下一页”链接为止。

5.2 LinkExtractor 与 CrawlSpider 实战

5.2.1 LinkExtractor 的基本用法(allow、deny、restrict_css 等参数)

LinkExtractor 是 Scrapy 提供的一个类,用于从页面中提取超链接。它通常与 CrawlSpider 配合使用,实现自动链接提取和爬取。

LinkExtractor 常用参数:

参数名 描述
allow 正则表达式,匹配允许提取的链接
deny 正则表达式,排除不希望提取的链接
restrict_css 使用 CSS 选择器限定链接提取的区域
tags 指定提取哪些标签中的链接(默认为 ‘a’ 和 ‘area’)

示例:使用 LinkExtractor 提取特定区域链接

from scrapy.linkextractors import LinkExtractor

# 创建 LinkExtractor 实例
link_extractor = LinkExtractor(
    allow=r'/category/\d+',  # 匹配 /category/数字 的路径
    restrict_css='div.categories'  # 只提取 div.categories 区域内的链接
)

# 提取链接
links = link_extractor.extract_links(response)

逻辑分析:

  • allow 指定了只提取 /category/数字 格式的链接。
  • restrict_css 限定只在 div.categories 区域中提取,避免页面其他区域干扰。

5.2.2 使用 CrawlSpider 实现自动链接提取与爬取

CrawlSpider 是 Scrapy 提供的一个高级爬虫类,适用于自动提取链接并继续爬取的场景。它通过定义 rules 来控制链接提取规则和回调函数。

示例:CrawlSpider 示例代码

from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class MyCrawlSpider(CrawlSpider):
    name = 'my_crawl_spider'
    allowed_domains = ['example.com']
    start_urls = ['https://example.com']

    rules = (
        Rule(LinkExtractor(allow=r'/category/\d+'), callback='parse_category', follow=True),
        Rule(LinkExtractor(restrict_css='div.pagination'), follow=True),
    )

    def parse_category(self, response):
        # 解析分类页面数据
        for item in response.css('div.product'):
            yield {
                'name': item.css('h3::text').get(),
                'price': item.css('span.price::text').get()
            }

代码解析:

  • rules 定义了两个规则:
    1. 第一个规则提取 /category/数字 的链接,调用 parse_category 方法解析。
    2. 第二个规则提取 div.pagination 区域中的链接(如分页),并继续爬取。
  • follow=True 表示该规则提取的链接将继续被用于爬取。

流程图展示:

graph TD
    A[开始爬取] --> B{提取链接}
    B --> C[匹配 allow 规则]
    C --> D[进入回调函数]
    B --> E[匹配 pagination 规则]
    E --> F[继续爬取下一页]
    D --> G[提取数据]
    F --> H[递归提取链接]
    G --> I[输出数据]

5.3 下载中间件与请求设置

5.3.1 中间件的作用与执行顺序

Scrapy 中间件(Middleware)是处理请求和响应的插件式组件,可以插入请求/响应的生命周期中,用于实现各种功能,如设置 User-Agent、下载延迟、代理 IP、反爬策略等。

下载中间件的执行顺序:

  • 请求阶段:从 Downloader Middleware process_request 开始,按 DOWNLOADER_MIDDLEWARES 中定义的顺序依次执行。
  • 响应阶段:响应返回后,按相反顺序执行 process_response 方法。

示例:自定义中间件设置 User-Agent

from scrapy import signals
import random

class RandomUserAgentMiddleware:
    @classmethod
    def from_crawler(cls, crawler):
        return cls()

    def process_request(self, request, spider):
        user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
        ]
        ua = random.choice(user_agents)
        request.headers['User-Agent'] = ua

启用中间件:

settings.py 中添加:

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomUserAgentMiddleware': 543,
}

5.3.2 设置下载延迟与 User-Agent 随机切换

为了避免被目标网站封禁,合理设置请求频率和 User-Agent 是关键策略。

下载延迟设置(settings.py)

DOWNLOAD_DELAY = 1.5  # 每次请求间隔 1.5 秒
RANDOMIZE_DOWNLOAD_DELAY = True  # 随机化延迟(默认为 True)

说明:

  • DOWNLOAD_DELAY 控制请求之间的最小延迟。
  • RANDOMIZE_DOWNLOAD_DELAY 会在 DOWNLOAD_DELAY * 0.5 ~ DOWNLOAD_DELAY * 1.5 之间随机延迟,减少规律性。

5.3.3 模拟浏览器请求与反爬策略应对

现代网站往往通过检测请求头、JavaScript 执行等手段进行反爬。Scrapy 作为纯请求库,需通过模拟浏览器请求头、使用代理等方式应对。

模拟浏览器请求头:

DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
}

反爬策略建议:

策略 说明
设置随机 User-Agent 避免被识别为爬虫
启用 Cookie 池 维持登录状态或模拟用户行为
使用代理 IP 防止 IP 被封禁
降低请求频率 减少对服务器压力
设置 Referer 模拟真实页面跳转来源

总结

本章深入讲解了 Scrapy 在分页爬取、链接提取与下载中间件配置方面的高级应用。通过构造分页请求、使用 LinkExtractor CrawlSpider 实现自动链接提取,以及配置下载中间件来应对反爬策略,可以显著提升爬虫的灵活性与稳定性。在实际项目中,这些技术的组合使用将帮助开发者高效完成复杂网页的数据采集任务。

6. 数据处理与爬虫运行规范

6.1 Item Pipeline 的功能与扩展

Scrapy 的 Item Pipeline 是用于处理爬取到的数据的核心组件之一。它负责数据清洗、去重、校验以及持久化存储等任务。一个 Scrapy 项目可以定义多个 Pipeline ,并根据优先级依次执行。

6.1.1 数据存储到本地文件、数据库(如 MySQL、MongoDB)

存储到本地文件(如 JSON、CSV)

Scrapy 原生支持将爬取结果导出为 JSON、CSV 等格式,使用方式如下:

scrapy crawl myspider -o output.json

也可以在 settings.py 中配置:

FEEDS = {
    'output.json': {'format': 'json'},
    'output.csv': {'format': 'csv'}
}
存储到数据库

MySQL 示例:

首先安装依赖:

pip install mysqlclient

然后在 pipelines.py 中定义 MySQL Pipeline:

import pymysql

class MySQLPipeline:
    def __init__(self, host, database, user, password, port):
        self.host = host
        self.database = database
        self.user = user
        self.password = password
        self.port = port

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            host=crawler.settings.get('MYSQL_HOST'),
            database=crawler.settings.get('MYSQL_DATABASE'),
            user=crawler.settings.get('MYSQL_USER'),
            password=crawler.settings.get('MYSQL_PASSWORD'),
            port=crawler.settings.get('MYSQL_PORT')
        )

    def open_spider(self, spider):
        self.connection = pymysql.connect(
            host=self.host,
            user=self.user,
            password=self.password,
            database=self.database,
            port=self.port,
            charset='utf8mb4'
        )
        self.cursor = self.connection.cursor()

    def close_spider(self, spider):
        self.connection.commit()
        self.connection.close()

    def process_item(self, item, spider):
        sql = """
        INSERT INTO news(title, url, publish_date)
        VALUES (%s, %s, %s)
        """
        values = (item['title'], item['url'], item['publish_date'])
        self.cursor.execute(sql, values)
        return item

settings.py 中启用并配置:

ITEM_PIPELINES = {
    'myproject.pipelines.MySQLPipeline': 300,
}

MYSQL_HOST = 'localhost'
MYSQL_DATABASE = 'scrapy_db'
MYSQL_USER = 'root'
MYSQL_PASSWORD = 'password'
MYSQL_PORT = 3306

MongoDB 示例:

安装依赖:

pip install pymongo

定义 MongoDB Pipeline:

import pymongo

class MongoDBPipeline:
    collection_name = 'news'

    def __init__(self, uri, database):
        self.uri = uri
        self.database = database

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            uri=crawler.settings.get('MONGO_URI'),
            database=crawler.settings.get('MONGO_DATABASE')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.uri)
        self.db = self.client[self.database]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.collection_name].insert_one(dict(item))
        return item

配置 settings.py

MONGO_URI = 'mongodb://localhost:27017'
MONGO_DATABASE = 'scrapy_data'

6.1.2 数据去重、清洗与校验逻辑实现

数据清洗:

items.py 中可以使用 scrapy.Field() input_processor output_processor 来处理字段:

from scrapy import Item, Field
from scrapy.loader.processors import TakeFirst, MapCompose, Join

def clean_date(text):
    return text.strip().replace('发布于:', '')

class NewsItem(Item):
    title = Field(output_processor=TakeFirst())
    url = Field(output_processor=TakeFirst())
    publish_date = Field(
        input_processor=MapCompose(clean_date),
        output_processor=TakeFirst()
    )

数据去重:

可以通过数据库的唯一索引实现去重,例如在 MySQL 中对 url 字段设置唯一索引,插入重复数据时忽略:

ALTER TABLE news ADD UNIQUE (url);

或者在 Pipeline 中手动判断是否已存在:

def process_item(self, item, spider):
    if not self.exists(item['url']):
        self.cursor.execute(sql, values)
    return item

def exists(self, url):
    self.cursor.execute("SELECT COUNT(*) FROM news WHERE url=%s", (url,))
    return self.cursor.fetchone()[0] > 0

6.2 启动与运行 Scrapy 爬虫

6.2.1 使用 scrapy crawl 命令启动爬虫

启动爬虫的基本命令如下:

scrapy crawl myspider

其中 myspider 是在 Spider 类中定义的 name 属性。

6.2.2 日志查看与错误排查技巧

Scrapy 默认会输出日志信息到终端,也可以配置日志输出到文件:

LOG_ENABLED = True
LOG_LEVEL = 'DEBUG'  # 可选 DEBUG/INFO/WARNING/ERROR/CRITICAL
LOG_FILE = 'scrapy.log'

日志中常见错误包括:

  • DNS lookup failed :网络连接问题或目标域名解析失败。
  • TimeoutError :请求超时,可能需要调整 DOWNLOAD_TIMEOUT
  • ConnectionRefusedError :目标服务器拒绝连接,检查 IP 是否被封或端口是否开放。

6.2.3 将爬虫结果输出为 JSON、CSV 等格式

除了使用 -o 参数,也可以在 settings.py 中统一配置输出格式:

FEED_FORMAT = 'json'
FEED_URI = 'output.json'

支持的格式包括: json , jsonlines , csv , xml , pickle , marshal 等。

6.3 遵守网络爬虫道德规范与 robots.txt

6.3.1 robots.txt 协议解读与遵守方法

robots.txt 是网站根目录下的一个文本文件,用来声明哪些页面可以被爬取,哪些不可以。Scrapy 默认会遵守该协议。

启用方式:

ROBOTSTXT_OBEY = True

settings.py 中设置该参数后,Scrapy 会自动读取目标网站的 robots.txt 文件并遵守规则。

示例内容:

User-agent: *
Disallow: /admin/
Disallow: /private/

表示所有爬虫都不允许访问 /admin/ /private/ 路径。

6.3.2 合理设置请求频率与用户代理

避免对目标服务器造成压力,应设置合理的请求频率和随机 User-Agent。

DOWNLOAD_DELAY = 2  # 每个请求间隔 2 秒
RANDOMIZE_DOWNLOAD_DELAY = True  # 随机延迟(±50%)

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'

或使用 scrapy-fake-useragent 插件实现随机 UA:

pip install scrapy-fake-useragent

settings.py 中启用:

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
    'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400,
    'scrapy_fake_useragent.middleware.RetryUserAgentMiddleware': 401,
}

FAKEUSERAGENT_PROVIDERS = [
    'scrapy_fake_useragent.providers.FakeUserAgentProvider',
    'scrapy_fake_useragent.providers.FakerProvider',
    'scrapy_fake_useragent.providers.GooseProvider',
]

6.3.3 爬虫的法律责任与合规建议

网络爬虫虽是合法工具,但在实际使用中必须遵守以下法律与规范:

  • 遵守目标网站的 robots.txt 协议
  • 不得频繁请求导致服务器负载过高
  • 不得爬取隐私数据或受版权保护的内容
  • 不得绕过反爬机制进行非法抓取
  • 确保数据使用符合《网络安全法》、《数据安全法》、《个人信息保护法》等相关法规

建议在企业级项目中设立合规审查机制,确保爬虫行为在合法、道德和商业伦理范围内进行。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Scrapy 是一个高效的 Python 网络爬虫框架,适用于自动化抓取和处理网页数据。本文档基于 Python 3.7,通过一个简单爬虫项目,演示了如何使用 Scrapy 抓取网站 http://www.okhqb.com/ 的文章信息。内容涵盖 Scrapy 环境搭建、爬虫类定义、CSS 选择器提取数据、分页处理、数据结构定义、配置设置及爬虫运行等完整流程。本项目经过实操验证,适合初学者掌握 Scrapy 基础开发技巧,并为后续复杂项目打下基础。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

更多推荐