Python进阶教程:23_Scrapy 爬虫框架 零基础超详细教程

Scrapy 是 Python 生态中最成熟、最专业的异步爬虫框架,不是单一的功能库,而是一整套爬虫解决方案。它帮我们封装好了请求调度、并发下载、数据提取、去重、数据存储等重复工作,让我们只需要专注写「数据提取逻辑」,就能快速开发高性能、结构化的爬虫项目。

如果把 requests + BeautifulSoup 比作自己手搓的自行车,适合短距离、简单场景;那 Scrapy 就是全自动的汽车,适合大规模、批量爬取、结构化采集的专业场景。

本文从概念、安装、项目结构到完整实战,全程通俗讲解 + 可运行代码 + 逐行解释,零基础也能一步步跑通第一个 Scrapy 爬虫。

⚠️ 合规提示:本文仅用于技术学习,请遵守目标网站的 robots 协议与用户协议,控制请求频率,不要恶意爬取他人网站数据。


一、先搞懂:Scrapy 是什么?

1. 通俗理解

你可以把 Scrapy 理解成一个「爬虫工厂」:

  • 你只需要告诉它:要爬哪些网站、要提取什么数据、数据怎么处理
  • 工厂自动帮你完成:发请求、下载页面、调度任务、并发提速、去重、存数据
  • 不用自己写循环发请求、不用自己管理多线程、不用自己处理异常重试,框架都帮你做好了

2. 和 requests + BeautifulSoup 的区别

对比项 requests + BeautifulSoup Scrapy 框架
定位 轻量工具,灵活自由 专业框架,工程化
并发 需要自己写多线程 / 协程 原生异步高并发,自动调度
功能 只有请求 + 解析 自带去重、管道、中间件、导出等全套功能
学习成本 低,上手快 稍高,需要熟悉项目结构与规则
适用场景 少量页面、简单爬虫、临时需求 批量爬取、结构化采集、长期项目

3. 核心优势

  • 高性能异步:基于 Twisted 异步框架,并发能力强,爬取速度远快于同步手写代码
  • 结构化强:通过 Item 定义数据字段,规范整齐,不容易乱
  • 功能齐全:自带自动去重、重试、Cookie 管理、User-Agent 轮换等
  • 扩展性强:中间件、管道可以自由扩展,比如存数据库、分布式爬取
  • 导出方便:一行命令导出 JSON、CSV、XML 等格式

二、环境安装

Scrapy 是第三方库,通过 pip 安装即可。

安装命令

打开终端 / 命令行,执行:

复制代码
pip install scrapy

注意:Windows 安装如果报错,大概率是缺少 VC++ 依赖,可以先安装 pywin32,或者直接用 Anaconda 环境安装更省心。

验证安装

安装完成后,终端输入:

复制代码
scrapy version

输出版本号(比如 Scrapy 2.11.0)就说明安装成功。


三、核心组件与工作流程(新手必懂)

Scrapy 采用组件化设计,每个组件各司其职,配合完成整个爬取流程。新手不用深究底层,先搞懂每个组件是干嘛的、数据怎么流转的即可。

1. 五大核心组件(工厂类比)

表格

组件 中文名 通俗职责
Engine 引擎 工厂厂长,总管所有流程,协调各个组件工作
Scheduler 调度器 任务排队员,管理所有待爬取的网址,按顺序发给下载器
Downloader 下载器 采购员,负责发请求下载网页源码
Spider 爬虫 解析员,拿到网页后提取目标数据、发现新的待爬网址
Pipeline 管道 加工仓,对爬下来的数据做清洗、去重、存数据库等处理

2. 简单工作流程

  1. 你在 Spider 里写好起始网址,交给引擎
  2. 引擎把网址发给调度器,排进待爬队列
  3. 调度器把下一个网址发给下载器,下载网页源码
  4. 下载器把网页源码返回给 Spider,你写的解析代码提取数据
  5. Spider 提取出两类内容,交回引擎:
    • 数据 Item:交给 Pipeline 做后续处理 / 存储
    • 新网址:交给调度器,加入队列继续爬
  6. 循环往复,直到队列里没有网址,爬虫结束

💡 新手可以先记住:我们写代码主要改两个地方

  1. Spider 爬虫文件:写网址、写提取逻辑
  2. Pipeline 管道文件:写数据处理、存储逻辑 其他组件框架默认都做好了,新手不用改。

四、Scrapy 项目结构

Scrapy 是按项目管理的,不是单个 py 文件,所有代码都在统一的项目目录里。

1. 创建项目

终端进入你想放代码的文件夹,执行:

复制代码
scrapy startproject 项目名

比如我们创建一个爬取名言的项目:

复制代码
scrapy startproject quotetutorial

执行后会自动生成一个 quotetutorial 文件夹,目录结构如下:

python 复制代码
quotetutorial/
    scrapy.cfg            # 项目配置文件(一般不用改)
    quotetutorial/        # 项目核心代码包
        __init__.py
        items.py          # 【重点】定义你要爬的数据结构(字段)
        middlewares.py    # 中间件(新手一般不动)
        pipelines.py      # 【重点】数据管道,清洗/存储数据
        settings.py       # 【重点】全局配置:请求头、并发、延迟、管道开关等
        spiders/          # 【重点】存放所有爬虫文件
            __init__.py

2. 核心文件说明

文件 作用 新手优先级
spiders/ 文件夹 放爬虫代码,每个爬虫对应一个文件,写网址和提取逻辑 ⭐⭐⭐⭐⭐
items.py 定义要爬取的字段,相当于数据模板,让数据更规范 ⭐⭐⭐⭐
pipelines.py 处理爬下来的数据,比如清洗、去重、存数据库 ⭐⭐⭐
settings.py 全局配置,改请求头、下载延迟、开启管道都在这里 ⭐⭐⭐⭐⭐
middlewares.py 中间件,比如代理、User-Agent 轮换,进阶再学 ⭐⭐

五、第一个完整实战:爬取名言网站

我们用专门的爬虫练习网站 https://quotes.toscrape.com/ 做实战,目标是爬取每页的名言、作者、标签。 这个网站无反爬、结构清晰,非常适合新手练手。

步骤 1:创建项目

终端执行:

python 复制代码
scrapy startproject quotetutorial
cd quotetutorial

步骤 2:定义数据结构(items.py

Item 就是你要爬的数据的「模板」,提前定义好要哪些字段,避免爬的时候漏字段、字段名写错,数据更规范。

打开 quotetutorial/items.py,修改成如下代码:

python 复制代码
import scrapy

class QuoteItem(scrapy.Item):
    # 定义三个字段:名言内容、作者、标签
    text = scrapy.Field()    # 名言内容
    author = scrapy.Field()  # 作者
    tags = scrapy.Field()    # 标签(列表)
代码解释
  • 继承 scrapy.Item 就是一个标准的数据类
  • scrapy.Field() 用来声明字段,类似字典的 key
  • 后面爬虫里提取完数据,就把数据塞进这个 Item 里,交给管道处理

步骤 3:编写爬虫文件(核心)

spiders 文件夹里新建一个文件 quote_spider.py,这就是我们的爬虫文件,所有网址和提取逻辑都写在这里。

完整代码
python 复制代码
import scrapy
from quotetutorial.items import QuoteItem  # 导入我们定义好的数据类

class QuoteSpider(scrapy.Spider):
    # 爬虫的名字:运行爬虫时用这个名字,一个项目可以有多个爬虫,名字不能重复
    name = "quote"
    
    # 允许爬取的域名:防止爬虫爬到别的网站去,安全限制
    allowed_domains = ["quotes.toscrape.com"]
    
    # 起始网址:爬虫启动后最先请求的网址,可以写多个
    start_urls = ["https://quotes.toscrape.com/"]

    # 解析函数:下载器拿到网页后,自动调用这个方法解析
    # response 就是下载好的响应对象,包含网页源码和各种方法
    def parse(self, response):
        # 第一步:提取当前页所有名言条目
        # response.css() 用CSS选择器提取元素,返回选择器列表
        quote_items = response.css("div.quote")
        
        # 遍历每个条目,提取数据
        for quote in quote_items:
            # 创建Item对象,装数据
            item = QuoteItem()
            
            # ::text 是Scrapy CSS选择器的语法:提取标签内的文本
            # get() 提取第一个结果的字符串
            item["text"] = quote.css("span.text::text").get().strip("""")
            item["author"] = quote.css("small.author::text").get()
            # getall() 提取所有结果,返回列表
            item["tags"] = quote.css("a.tag::text").getall()
            
            # 【关键】yield 交出数据,交给框架传给管道
            yield item
逐行核心解释
  1. name = "quote" 爬虫的唯一标识,运行爬虫的时候用 scrapy crawl quote 就是通过这个名字找到对应爬虫。

  2. start_urls 起始网址列表,框架会自动给这些网址发请求,下载完页面自动调用 parse 方法解析。

  3. parse(self, response) 解析回调函数,是爬虫的核心:

    • 参数 response 是下载好的响应对象,包含网页全部内容
    • 所有数据提取、新网址发现都在这个函数里写
    • 函数必须用 yield 返回数据或新请求,不能用 return
  4. response.css() 提取数据 Scrapy 内置了 CSS 选择器和 XPath 两种提取方式,新手选自己熟悉的即可:

    • css("标签名::text"):提取标签内的文本
    • css("标签名::attr(href)"):提取标签的属性值,比如 href、src
    • .get():拿到第一个匹配结果的字符串
    • .getall():拿到所有匹配结果,返回列表

    补充:也可以用 XPath 写法,比如 response.xpath('//span[@class="text"]/text()').get(),效果完全一样。

  5. yield item 这是 Scrapy 最核心的语法。你可以把 yield 理解成「上交」:

    • 把整理好的 Item 上交,框架会自动传给管道处理
    • 不用自己存、不用自己管理列表,框架统一调度
    • 这是生成器的用法,也是 Scrapy 异步的基础

步骤 4:修改基础配置(settings.py

打开 quotetutorial/settings.py,修改几个新手必改的配置,避免被网站拦截、出现乱码等问题。

找到对应位置,修改 / 添加:

python 复制代码
# 1. 关闭 robots 协议遵守(练习用,真实项目建议遵守)
ROBOTSTXT_OBEY = False

# 2. 设置 User-Agent,模拟浏览器,不然会被拦截
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"

# 3. 设置下载延迟,每秒发一次请求,文明爬虫,避免封IP
DOWNLOAD_DELAY = 1

# 4. 导出JSON时中文正常显示,不用Unicode
FEED_EXPORT_ENCODING = "utf-8"

步骤 5:运行爬虫

终端进入项目根目录(有 scrapy.cfg 的那一层),执行:

复制代码
scrapy crawl quote

运行后终端会打印日志,你会看到爬虫自动请求页面、提取数据。

步骤 6:导出数据到文件

如果想把爬下来的数据直接保存成 JSON 或 CSV 文件,不用写管道,加个参数就行:

python 复制代码
# 导出为 JSON 文件
scrapy crawl quote -o quotes.json

# 导出为 CSV 文件
scrapy crawl quote -o quotes.csv

运行后项目根目录会生成对应的文件,打开就能看到结构化的数据。


六、进阶 1:实现自动翻页

上面的代码只能爬第一页,我们可以让爬虫自动识别「下一页」链接,继续爬,直到所有页爬完。

修改 quote_spider.py 的 parse 方法

在函数末尾加上翻页逻辑:

python 复制代码
def parse(self, response):
    # ===== 上面的提取数据代码不变 =====
    quote_items = response.css("div.quote")
    for quote in quote_items:
        item = QuoteItem()
        item["text"] = quote.css("span.text::text").get().strip("""")
        item["author"] = quote.css("small.author::text").get()
        item["tags"] = quote.css("a.tag::text").getall()
        yield item
    
    # ===== 新增:翻页逻辑 =====
    # 提取下一页的链接
    next_url = response.css("li.next a::attr(href)").get()
    
    # 如果有下一页
    if next_url:
        # 构造完整网址(相对路径转绝对路径)
        # response.urljoin() 自动拼接域名,不用自己拼
        full_url = response.urljoin(next_url)
        
        # 【关键】yield 一个新的Request,交给调度器继续爬
        # callback=self.parse 表示下载完还用当前这个parse函数解析
        yield scrapy.Request(url=full_url, callback=self.parse)

代码解释

  1. 先找到下一页按钮的 href 属性,拿到相对链接
  2. response.urljoin() 自动拼接成完整的绝对网址
  3. yield scrapy.Request(...) 把新网址上交,框架会自动去下载,下载完再调用 parse 解析
  4. 循环往复,直到某一页没有下一页链接,爬虫自动结束

再次运行 scrapy crawl quote -o all_quotes.json,就能爬完网站所有 10 页数据。


七、进阶 2:数据管道 Pipeline

当数据需要清洗、去重、存数据库时,就可以写在 Pipeline 里。管道可以有多个,按优先级顺序执行。

示例:做一个简单的数据清洗管道

打开 pipelines.py,我们写一个管道:去除空数据、打印保存提示。

python 复制代码
class QuoteCleanPipeline:
    # 爬虫启动时执行一次
    def open_spider(self, spider):
        print("爬虫启动,开始处理数据...")

    # 每个Item都会经过这个方法处理
    def process_item(self, item, spider):
        # 清洗:如果名言内容为空,就丢弃
        if not item.get("text"):
            raise DropItem("空数据,丢弃")
        
        # 给作者名字去掉首尾空格
        item["author"] = item["author"].strip()
        
        # 必须return item,把数据交给下一个管道
        return item

    # 爬虫结束时执行一次
    def close_spider(self, spider):
        print("爬虫结束,数据处理完成")

关键:在 settings.py 中开启管道

管道写完默认是不生效的,必须在 settings.py 里开启并设置优先级:

python 复制代码
ITEM_PIPELINES = {
    # 格式:"管道类路径": 优先级数字
    # 数字越小,优先级越高,越先执行
    "quotetutorial.pipelines.QuoteCleanPipeline": 300,
}

开启后,每个爬取到的 Item 都会自动经过这个管道处理。

💡 拓展:你还可以写更多管道,比如保存到 MySQL、MongoDB、去重管道等,分别设置不同的优先级即可。


八、常用提取方法汇总

Scrapy 的 response 对象提供了丰富的提取方法,新手掌握 CSS 和 XPath 两种即可。

1. CSS 选择器(简单易上手)

写法 作用
response.css("div.title") 选择 class 为 title 的 div
response.css("#header") 选择 id 为 header 的元素
css("a::text").get() 提取 a 标签的文本
css("a::attr(href)").get() 提取 a 标签的 href 属性
.get() 取第一个匹配结果,字符串
.getall() 取所有匹配结果,列表

2. XPath 选择器(功能更强)

和 Selenium 的 XPath 语法完全一致:

python 复制代码
# 提取名言文本
text = response.xpath('//span[@class="text"]/text()').get()
# 提取下一页链接
next_url = response.xpath('//li[@class="next"]/a/@href').get()

3. 小技巧

  • 不知道怎么写选择器?浏览器 F12 找到元素,右键 → 复制 → 复制选择器 / 复制 XPath,直接粘贴使用
  • 调试时可以用 scrapy shell 网址 进入交互模式,实时测试选择器是否正确,非常适合新手调试

九、新手高频易错点总结

1. 运行爬虫找不到项目

必须在项目根目录 (有 scrapy.cfg 的文件夹)下执行 scrapy crawl 命令,不然会报错。

2. parse 方法用 return 不用 yield

Scrapy 的解析函数必须用 yield 返回 Item 或 Request,用 return 数据不会被框架接收。 可以简单理解:yield 是 "逐个上交",符合框架的异步调度逻辑。

3. 管道写了不生效

必须在 settings.pyITEM_PIPELINES 里配置开启,不然管道永远不会执行。

4. 中文导出乱码

settings.py 里添加 FEED_EXPORT_ENCODING = "utf-8",导出 JSON/CSV 就会正常显示中文。

5. 相对链接不拼接域名

拿到的 href 是相对路径(比如 /page/2/),不能直接请求,必须用 response.urljoin(next_url) 拼接成完整网址。

6. 爬不到数据,先看源码

先打印 response.text,确认拿到的网页源码和浏览器里的是不是一样:

  • 源码里有内容 → 是选择器写错了
  • 源码里没有内容 → 是 JS 动态渲染的页面,Scrapy 直接爬不了,需要配合 Selenium 或者抓接口

7. 请求太快被封 IP

一定要加 DOWNLOAD_DELAY 设置延迟,控制请求频率,文明爬取。

相关推荐
此冬歌咏1 小时前
自动化服务器运维监控系统(python+shell)
linux·运维·服务器·开发语言·python·自动化
爱读源码的大都督1 小时前
DeepSeek面试官问:生产RAG系统回答不准确,该如何定位和优化?这样回答,能让面试官当场给你Offer!
java·后端·python
“AI国潮设计-小江”2 小时前
【Python实战】SDXL精准控制“普宁英歌舞×星空蛋糕”IP落地,附核心Prompt与商用授权思路
开发语言·人工智能·python·prompt·aigc
k4m7v2pz2 小时前
从 Python 搬到 Rust:pyglet MIDI DAW 变成 egui 鬼畜采样器的迁移复盘
开发语言·python·rust
tryCbest2 小时前
FastAPI中passlib包的作用
python·fastapi·passlib
心中有你02142 小时前
Python Tkinter 情侣恋爱日记桌面小程序(本地文件存储,无数据库)
开发语言·python
PFFstronger2 小时前
从 0 到 1 搭建接口自动化测试框架:分层架构 + 数据驱动 + 接口依赖编排
python·架构·自动化
Andya_net2 小时前
Spring Boot | 条件注解完全指南:从 @Conditional 到 @ConditionalOnExpression 的原理、实践与避坑
spring boot·后端·python
BYSJMG2 小时前
计算机毕设选题推荐:基于大数据的公共交通运营数据分析与可视化,Spark+K-Means聚类城市运营模式
大数据·hadoop·信息可视化·数据分析·spark·kmeans·课程设计