贝博恩创新科技网

Scrapy框架教程,Python爬虫如何快速上手?

目录

  1. Scrapy 简介
    • 什么是 Scrapy?
    • 为什么选择 Scrapy?
    • Scrapy 架构概览
  2. 环境准备
    • 安装 Python
    • 安装 Scrapy
    • 验证安装
  3. 你的第一个 Scrapy 项目
    • 创建项目
    • 生成一个 Spider
    • 定义要抓取的数据 (Items)
    • 编写 Spider 逻辑
    • 运行 Spider 并查看结果
  4. 深入 Scrapy 核心组件
    • Spider (蜘蛛):爬虫的大脑
    • Item (项目):数据容器
    • Item Pipeline (项目管道):数据处理与持久化
    • Downloader (下载器):负责下载网页
    • Scheduler (调度器):管理请求队列
    • Engine (引擎):指挥中心
    • Downloader Middlewares (下载中间件):处理请求和响应
    • Spider Middlewares (蜘蛛中间件):处理 Spider 的输入输出
  5. 实战项目:抓取知乎热榜
    • 项目目标
    • 创建项目
    • 分析目标网页
    • 编写 Spider
    • 定义 Item
    • 存储数据到 JSON 文件
    • 运行与调试
  6. 进阶技巧
    • 使用 Shell 调试 (scrapy shell)
    • 处理 JavaScript 渲染页面 (Scrapy-Splash)
    • 遵守 robots.txt 规则
    • 设置下载延迟和 User-Agent
    • 使用代理
  7. 总结与最佳实践

Scrapy 简介

什么是 Scrapy?

Scrapy 是一个用 Python 编写的高性能、异步的爬虫框架,它不仅仅是一个库,而是一个完整的“工具箱”,让你可以快速、高效地构建和部署爬虫,用于从网站中提取结构化数据。

Scrapy框架教程,Python爬虫如何快速上手?-图1
(图片来源网络,侵删)

为什么选择 Scrapy?

  • 高性能:基于 Twisted 异步网络库,能高效处理并发请求,速度远超同步请求的爬虫。
  • 架构清晰:采用“请求-响应”模型,各组件(Spider, Pipeline, Middleware)职责分明,易于维护和扩展。
  • 功能强大:内置了数据提取、处理、存储、错误处理、中间件等全套功能。
  • 可扩展性:通过中间件和管道,可以轻松实现自定义功能,如代理、登录、数据清洗等。
  • 成熟稳定:经过大量项目验证,社区活跃,文档丰富。

Scrapy 架构概览

理解 Scrapy 的工作流程是掌握它的关键。

  1. Engine (引擎):Scrapy 的核心,负责协调其他所有组件的工作。
  2. Scheduler (调度器):接收来自引擎的请求,并将其排入队列,等待引擎下载。
  3. Downloader (下载器):从调度器获取请求,下载网页内容,并将响应返回给引擎。
  4. Spider (蜘蛛):负责处理响应,解析数据,提取 Item,并生成新的 Request 交给引擎。
  5. Item Pipeline (项目管道):接收 Spider 提取的 Item,对其进行处理(如清洗、验证、存储到数据库等)。
  6. Downloader Middlewares (下载中间件):位于引擎和下载器之间,可以拦截和处理请求/响应,例如设置 User-Agent、代理等。
  7. Spider Middlewares (蜘蛛中间件):位于引擎和 Spider 之间,可以处理 Spider 的输入和输出,例如修改响应或丢弃某些请求。

数据流Engine -> Scheduler -> Downloader -> Engine -> Spider -> Engine -> Item Pipeline


环境准备

安装 Python

Scrapy 需要 Python 3.6 或更高版本,如果你还没有安装,请从 Python 官网 下载并安装。

安装 Scrapy

推荐使用 pip 进行安装,打开你的终端或命令提示符,运行:

Scrapy框架教程,Python爬虫如何快速上手?-图2
(图片来源网络,侵删)
pip install scrapy

如果遇到权限问题,可以在 macOS/Linux 上使用 sudo pip install scrapy,或者在 Windows 上以管理员身份运行命令提示符。

验证安装

安装完成后,运行以下命令,如果看到版本号,说明安装成功。

scrapy version

你的第一个 Scrapy 项目

我们将创建一个简单的爬虫,从 quotes.toscrape.com (一个专为爬虫练习设计的网站) 上抓取名言和作者。

创建项目

创建一个项目文件夹,然后在其中使用 startproject 命令生成 Scrapy 项目的骨架。

Scrapy框架教程,Python爬虫如何快速上手?-图3
(图片来源网络,侵删)
# 创建项目文件夹
mkdir my_scraper_project
cd my_scraper_project
# 创建 Scrapy 项目
scrapy startproject tutorial

这会生成如下目录结构:

tutorial/
├── scrapy.cfg          # 部署配置文件
└── tutorial/           # 项目 Python 模块
    ├── __init__.py
    ├── items.py        # 定义 Item 的地方
    ├── middlewares.py  # 中间件
    ├── pipelines.py    # 管道
    ├── settings.py     # 项目设置
    └── spiders/        # Spider 存放目录
        └── __init__.py

生成一个 Spider

tutorial 目录下,使用 genspider 命令快速创建一个 Spider 模板。

cd tutorial
scrapy genspider quotes quotes.toscrape.com

这会在 spiders 目录下创建一个名为 quotes.py 的文件,并包含一些基本代码。

定义要抓取的数据

打开 tutorial/items.py,这是定义我们想要抓取的数据结构的地方。

# tutorial/items.py
import scrapy
class QuoteItem(scrapy.Item):
    # 定义要抓取的字段
    text = scrapy.Field()  # 名言内容
    author = scrapy.Field() # 作者
    tags = scrapy.Field()  # 标签

编写 Spider 逻辑

打开 spiders/quotes.py,这是爬虫的核心,我们需要修改它,让它知道如何访问网站、解析页面并提取数据。

# tutorial/spiders/quotes.py
import scrapy
from tutorial.items import QuoteItem # 导入我们定义的 Item
class QuotesSpider(scrapy.Spider):
    # Spider 的唯一名称,运行时使用
    name = 'quotes'
    # 允许爬取的域名,用于限制爬虫范围
    allowed_domains = ['quotes.toscrape.com']
    # 初始请求的 URL 列表
    start_urls = ['http://quotes.toscrape.com/']
    # parse 方法是 Spider 的核心,处理响应
    def parse(self, response):
        # 使用 CSS 选择器提取所有名言块
        for quote in response.css('div.quote'):
            # 创建一个 Item 对象
            item = QuoteItem()
            # 填充 Item 数据
            item['text'] = quote.css('span.text::text').get()
            item['author'] = quote.css('small.author::text').get()
            item['tags'] = quote.css('div.tags a.tag::text').getall()
            # 返回 Item,Scrapy 会自动将其传递给 Pipeline
            yield item
        # 处理分页:提取下一页的链接,并生成新的请求
        next_page = response.css('li.next a::attr(href)').get()
        if next_page is not None:
            # 将相对 URL 转换为绝对 URL,并生成新的请求
            yield response.follow(next_page, callback=self.parse)

代码解释:

  • name: Spider 的名字,用 scrapy crawl quotes 来启动它。
  • start_urls: 爬虫开始抓取的初始 URL。
  • parse(response): 这是 Scrapy Spider 的默认回调方法,当下载器获取到 start_urls 的响应后,会自动调用这个方法。
  • response.css(): Scrapy 内置的 CSS 选择器,用于从 HTML 响应中提取数据。
    • div.quote: 选择所有 class="quote"div 元素。
    • span.text::text: 选择 span 元素,并获取其内部的文本内容 (:text 是 Scrapy 的伪元素)。
    • get(): 获取第一个匹配的结果。
    • getall(): 获取所有匹配的结果,返回一个列表。
  • yield item: 将提取的 Item 返回给 Scrapy 引擎,引擎会将其交给 Item Pipeline 处理。
  • response.next_page: 提取“下一页”按钮的 href 属性。
  • response.follow(): 一个便捷方法,用于根据相对 URL 生成新的 Request,并将其交给调度器。callback=self.parse 指定新请求的响应仍然由 parse 方法处理,从而实现递归抓取。

运行 Spider 并查看结果

tutorial 项目根目录下,运行以下命令:

scrapy crawl quotes

你会看到 Scrapy 开始抓取数据,并实时打印出提取的 Item,格式如下:

{'author': 'Albert Einstein', 'tags': ['change', 'deep-thoughts', 'thinking', 'world'], 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'}
{'author': 'J.K. Rowling', 'tags': ['abilities', 'choices'], 'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”'}
...

输出到文件: 通常我们不希望结果直接打印在终端,而是保存到文件,Scrapy 提供了 -o (output) 选项。

scrapy crawl quotes -o quotes.json

这会将结果保存为 quotes.json 文件,Scrapy 会自动处理 JSON 格式,确保每行都是一个完整的 JSON 对象。


深入 Scrapy 核心组件

Item Pipeline (项目管道)

当 Spider 产生一个 Item 后,它会自动进入 Item Pipeline,我们可以在 pipelines.py 中定义多个处理步骤,按顺序执行。

一个常见的用途是将数据存入数据库。 打开 tutorial/pipelines.py,我们可以这样写:

# tutorial/pipelines.py
import json
class JsonWriterPipeline:
    def open_spider(self, spider):
        # 当 Spider 启动时,打开一个文件用于写入
        self.file = open('items.jsonl', 'w', encoding='utf-8')
    def close_spider(self, spider):
        # 当 Spider 关闭时,关闭文件
        self.file.close()
    def process_item(self, item, spider):
        # 处理每个 Item,将其转换为 JSON 字符串并写入文件
        line = json.dumps(dict(item), ensure_ascii=False) + "\n"
        self.file.write(line)
        return item

启用 Pipeline: 要让这个 Pipeline 生效,必须在 settings.py 中启用它。

# tutorial/settings.py
ITEM_PIPELINES = {
   # 300 是优先级,数字越小,优先级越高
   'tutorial.pipelines.JsonWriterPipeline': 300,
}

现在再运行 scrapy crawl quotes,你会发现项目根目录下生成了 items.jsonl 文件,其中包含了所有抓取的数据。

Downloader Middlewares (下载中间件)

中间件是修改 Scrapy 请求和响应的钩子,一个非常常见的用途是设置请求头,User-Agent

设置 User-Agent: 在 settings.py 中,Scrapy 已经提供了一个默认的 USER_AGENT,我们可以修改它,或者自定义一个列表。

# tutorial/settings.py
# 将默认的 User-Agent 替换为 Chrome 的
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'
# 或者使用一个列表,Scrapy 会随机选择
# 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'

自定义中间件: 如果你想实现更复杂的逻辑,比如随机代理,可以在 middlewares.py 中编写。

# tutorial/middlewares.py
import random
class RandomUserAgentMiddleware:
    def __init__(self, user_agents):
        self.user_agents = user_agents
    @classmethod
    def from_crawler(cls, crawler):
        # 从 settings 中获取 user_agents 列表
        user_agents = crawler.settings.get('USER_AGENTS', [])
        return cls(user_agents)
    def process_request(self, request, spider):
        # 为每个请求随机选择一个 User-Agent
        ua = random.choice(self.user_agents)
        request.headers.setdefault('User-Agent', ua)

然后在 settings.py 中启用它:

# tutorial/settings.py
DOWNLOADER_MIDDLEWARES = {
   'tutorial.middlewares.RandomUserAgentMiddleware': 543,
}

注意:中间件的优先级数字越小,越靠近引擎,越早执行。


实战项目:抓取知乎热榜

让我们用学到的知识,来抓取知乎热榜的标题、链接、热度指数和排名。

项目目标

抓取 知乎热榜 页面,获取前 50 条热榜信息,并保存为 CSV 文件。

创建项目

scrapy startproject zhihu_hot
cd zhihu_hot
scrapy genspider hot_list www.zhihu.com

分析目标网页

打开浏览器,访问知乎热榜,按 F12 打开开发者工具,切换到“网络”标签,刷新页面,找到 hot-lists 的请求,分析其 HTML 结构,你会发现每条热榜都在一个 HotItem 组件下,包含标题、链接和热度数据。

定义 Item

打开 zhihu_hot/items.py

# zhihu_hot/items.py
import scrapy
class ZhihuHotItem(scrapy.Item):
    rank = scrapy.Field()  # 排名= scrapy.Field() # 标题
    link = scrapy.Field()  # 链接
    heat = scrapy.Field()  # 热度

编写 Spider

打开 spiders/hot_list.py

# zhihu_hot/spiders/hot_list.py
import scrapy
from zhihu_hot.items import ZhihuHotItem
class HotListSpider(scrapy.Spider):
    name = 'hot_list'
    allowed_domains = ['www.zhihu.com']
    # 注意:知乎热榜的实际 URL 是 /hot
    start_urls = ['https://www.zhihu.com/hot']
    def parse(self, response):
        # 使用 XPath 或 CSS 选择器定位每个热榜条目
        # 通过分析页面,每个条目都在一个 class 为 HotItem 的 div 下
        for item in response.css('div.HotItem'):
            rank = item.css('div.HotItem-rank::text').get()
            title = item.css('div.HotItem-title a::text').get()
            link = item.css('div.HotItem-title a::attr(href)').get()
            # 热度可能包含单位,需要处理一下
            heat_str = item.css('div.HotItem-metrics span::text').get()
            zhihu_item = ZhihuHotItem()
            zhihu_item['rank'] = rank
            zhihu_item['title'] = title
            zhihu_item['link'] = response.urljoin(link) # 处理相对链接
            zhihu_item['heat'] = heat_str
            yield zhihu_item

注意

  • response.urljoin(link):用于将相对路径(如 /question/123456)转换为完整的 URL(如 https://www.zhihu.com/question/123456)。
  • 知乎有反爬机制,直接运行可能会被屏蔽,我们需要在 settings.py 中设置 USER_AGENTDOWNLOAD_DELAY

设置 settings.py

# zhihu_hot/settings.py
# 模拟浏览器
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'
# 设置下载延迟,避免请求过于频繁被屏蔽
DOWNLOAD_DELAY = 1
# 禁用 cookies (可选)
COOKIES_ENABLED = False
# Obey robots.txt rules
ROBOTSTXT_OBEY = True

运行与存储

运行爬虫,并将结果保存为 CSV 文件,CSV 格式非常适合表格数据。

scrapy crawl hot_list -o hot_list.csv

你会得到一个 hot_list.csv 文件,内容类似:

1,如何评价苹果公司发布的 Vision Pro?,/question/580602599,999999+
2,有哪些让你相见恨晚的 PPT 制作技巧?,/question/20618853,888888+
...

进阶技巧

使用 Shell 调试 (scrapy shell)

在编写复杂的 CSS/XPath 选择器时,scrapy shell 是一个强大的调试工具,它可以在不运行完整 Spider 的情况下,快速测试你的选择器。

scrapy shell "https://quotes.toscrape.com"

进入交互式环境后,你可以直接使用 response 对象来测试选择器:

>>> response.css('div.quote')
[<Selector xpath="descendant-or-self::div[@class and contains(concat(' ', normalize-space(@class), ' '), ' quote ')]" data='<div class="quote" ...'>]
>>> quote = response.css('div.quote')[0]
>>> quote.css('span.text::text').get()
'“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'

处理 JavaScript 渲染页面

Scrapy 本身不执行 JavaScript,如果目标网站是动态加载的(比如使用 React, Vue, Angular),你需要借助其他工具。 最常用的方案是 Scrapy-Splash,它是一个基于 Splash (一个 JavaScript 渲染服务) 的 Scrapy 中间件。

  1. 安装 Splash 服务 (通常使用 Docker)。
  2. 安装 scrapy-splashpip install scrapy-splash
  3. settings.py 中配置中间件。
  4. 在 Spider 中使用 SplashRequest 代替 Request

遵守 robots.txt

Scrapy 默认遵守 robots.txt 规则,你可以在 settings.py 中通过 ROBOTSTXT_OBEY = False 来禁用它,但强烈不建议这样做,这可能导致你的 IP 被网站封禁。

设置下载延迟和 User-Agent

这两个是反爬最基本、最重要的设置,如前面实战项目所示,请务必在 settings.py 中配置好。

使用代理

当需要大规模抓取时,使用代理是避免 IP 被封的必要手段,可以通过下载中间件来实现。

# middlewares.py
import random
class ProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.get('PROXY_LIST')
        return cls(proxy_list)
    def process_request(self, request, spider):
        proxy = random.choice(self.proxy_list)
        request.meta['proxy'] = proxy

然后在 settings.py 中配置:

PROXY_LIST = [
    'http://user1:pass1@host1:port1',
    'http://user2:pass2@host2:port2',
]
DOWNLOADER_MIDDLEWARES = {
   'myproject.middlewares.ProxyMiddleware': 400,
}

总结与最佳实践

  • 从简单开始:先写一个能跑通的基础版本,再逐步添加功能(如分页、反爬、存储等)。
  • 尊重网站:遵守 robots.txt,设置合理的 DOWNLOAD_DELAY,不要对服务器造成过大压力。
  • 使用 Item Pipeline:将数据提取逻辑和数据处理逻辑分离,使代码更清晰。
  • 善用中间件:对于通用功能(如设置请求头、代理),使用中间件,而不是在每个 Spider 中重复编写。
  • 监控和日志:Scrapy 有强大的日志系统,合理配置日志级别,可以帮助你快速定位问题。
  • 法律和道德:在抓取任何网站之前,请确保你了解并遵守其服务条款以及当地的法律法规,不要抓取和传播敏感或受版权保护的数据。

Scrapy 是一个非常强大的工具,掌握它需要一定的练习,希望这份教程能帮助你入门,祝你爬虫愉快!

分享:
扫描分享到社交APP
上一篇
下一篇