目录
- Scrapy 简介
- 什么是 Scrapy?
- 为什么选择 Scrapy?
- Scrapy 架构概览
- 环境准备
- 安装 Python
- 安装 Scrapy
- 验证安装
- 你的第一个 Scrapy 项目
- 创建项目
- 生成一个 Spider
- 定义要抓取的数据 (Items)
- 编写 Spider 逻辑
- 运行 Spider 并查看结果
- 深入 Scrapy 核心组件
- Spider (蜘蛛):爬虫的大脑
- Item (项目):数据容器
- Item Pipeline (项目管道):数据处理与持久化
- Downloader (下载器):负责下载网页
- Scheduler (调度器):管理请求队列
- Engine (引擎):指挥中心
- Downloader Middlewares (下载中间件):处理请求和响应
- Spider Middlewares (蜘蛛中间件):处理 Spider 的输入输出
- 实战项目:抓取知乎热榜
- 项目目标
- 创建项目
- 分析目标网页
- 编写 Spider
- 定义 Item
- 存储数据到 JSON 文件
- 运行与调试
- 进阶技巧
- 使用 Shell 调试 (
scrapy shell) - 处理 JavaScript 渲染页面 (Scrapy-Splash)
- 遵守
robots.txt规则 - 设置下载延迟和 User-Agent
- 使用代理
- 使用 Shell 调试 (
- 总结与最佳实践
Scrapy 简介
什么是 Scrapy?
Scrapy 是一个用 Python 编写的高性能、异步的爬虫框架,它不仅仅是一个库,而是一个完整的“工具箱”,让你可以快速、高效地构建和部署爬虫,用于从网站中提取结构化数据。

为什么选择 Scrapy?
- 高性能:基于 Twisted 异步网络库,能高效处理并发请求,速度远超同步请求的爬虫。
- 架构清晰:采用“请求-响应”模型,各组件(Spider, Pipeline, Middleware)职责分明,易于维护和扩展。
- 功能强大:内置了数据提取、处理、存储、错误处理、中间件等全套功能。
- 可扩展性:通过中间件和管道,可以轻松实现自定义功能,如代理、登录、数据清洗等。
- 成熟稳定:经过大量项目验证,社区活跃,文档丰富。
Scrapy 架构概览
理解 Scrapy 的工作流程是掌握它的关键。
- Engine (引擎):Scrapy 的核心,负责协调其他所有组件的工作。
- Scheduler (调度器):接收来自引擎的请求,并将其排入队列,等待引擎下载。
- Downloader (下载器):从调度器获取请求,下载网页内容,并将响应返回给引擎。
- Spider (蜘蛛):负责处理响应,解析数据,提取
Item,并生成新的Request交给引擎。 - Item Pipeline (项目管道):接收 Spider 提取的
Item,对其进行处理(如清洗、验证、存储到数据库等)。 - Downloader Middlewares (下载中间件):位于引擎和下载器之间,可以拦截和处理请求/响应,例如设置 User-Agent、代理等。
- Spider Middlewares (蜘蛛中间件):位于引擎和 Spider 之间,可以处理 Spider 的输入和输出,例如修改响应或丢弃某些请求。
数据流:
Engine -> Scheduler -> Downloader -> Engine -> Spider -> Engine -> Item Pipeline
环境准备
安装 Python
Scrapy 需要 Python 3.6 或更高版本,如果你还没有安装,请从 Python 官网 下载并安装。
安装 Scrapy
推荐使用 pip 进行安装,打开你的终端或命令提示符,运行:

pip install scrapy
如果遇到权限问题,可以在 macOS/Linux 上使用 sudo pip install scrapy,或者在 Windows 上以管理员身份运行命令提示符。
验证安装
安装完成后,运行以下命令,如果看到版本号,说明安装成功。
scrapy version
你的第一个 Scrapy 项目
我们将创建一个简单的爬虫,从 quotes.toscrape.com (一个专为爬虫练习设计的网站) 上抓取名言和作者。
创建项目
创建一个项目文件夹,然后在其中使用 startproject 命令生成 Scrapy 项目的骨架。

# 创建项目文件夹 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_AGENT和DOWNLOAD_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 中间件。
- 安装 Splash 服务 (通常使用 Docker)。
- 安装
scrapy-splash:pip install scrapy-splash。 - 在
settings.py中配置中间件。 - 在 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 是一个非常强大的工具,掌握它需要一定的练习,希望这份教程能帮助你入门,祝你爬虫愉快!
