Python Scrapy 入门教程:从零学会抓取和解析网页数据

Python Scrapy 入门教程:从零学会抓取和解析网页数据

很多 Python 初学者学完基础语法后,都会遇到一个很实际的问题:怎么把网页里的数据稳定地提取下来,变成自己能处理的结构化数据?

如果你只是偶尔抓一个页面,用 requests + BeautifulSoup 当然可以;但当你想抓取多个页面、自动跟进链接、统一清洗字段、导出成 JSON 或 CSV,甚至把数据存进数据库时,Scrapy 会明显更省事。

Scrapy 是 Python 生态里非常经典的爬虫框架。它不只是"下载网页源码",而是把发请求、收响应、解析数据、跟踪链接、导出结果、去重、限速、日志管理这一整套流程都组织好了。

这篇文章面向新手,目标很明确:

  • 让你理解 Scrapy 是什么
  • 带你从零创建一个 Scrapy 项目
  • 学会抓取网页、解析字段、翻页采集、导出数据
  • 告诉你 Scrapy 除了抓文章标题之外,还能做什么

学完后,你至少能独立写出一个基础可用的网页数据采集脚本。

1. Scrapy 是什么

Scrapy 是一个用于抓取网站并提取结构化数据的 Python 框架。

你可以把它理解成一个专门为"网页采集"准备好的工程化工具箱。相比只写一个简单脚本,Scrapy 的优势在于它已经帮你解决了很多重复问题,比如:

  • 请求调度
  • 并发抓取
  • 自动跟踪链接
  • 页面解析
  • 数据导出
  • 日志输出
  • 重试与超时
  • 去重和限速

也就是说,Scrapy 更适合做"持续抓取"和"多页面采集",而不只是一次性的实验代码。

2. Scrapy 适合哪些场景

对新手来说,最适合先用 Scrapy 解决的,通常是下面这些任务:

  • 抓取资讯站点的标题、链接、发布时间
  • 采集商品列表页中的名称、价格、销量、详情页地址
  • 批量抓取博客、论坛、文档站中的内容摘要
  • 采集招聘网站中的岗位名称、城市、薪资范围
  • 采集分页数据并导出成 JSON、CSV、Excel 可再处理的数据

如果你的目标是"从很多网页里提取重复结构的数据",Scrapy 基本就是合适的工具。

3. Scrapy 和 requests + BeautifulSoup 有什么区别

很多新手会问:我已经会 requestsBeautifulSoup,为什么还要学 Scrapy?

可以这样理解:

  • requests 负责发请求
  • BeautifulSoup 负责解析 HTML
  • Scrapy 则把请求、解析、调度、导出、翻页、日志这些能力打包成了一个框架

如果只是抓 1 个页面,requests + BeautifulSoup 更轻量。

如果要抓几十页、几百页,或者想要把项目组织得更清晰、可复用、可扩展,Scrapy 往往更合适。

4. 安装 Scrapy

先确保你已经安装了 Python。然后直接执行:

bash 复制代码
pip install scrapy

安装完成后,可以检查版本:

bash 复制代码
scrapy version

如果命令能正常输出版本信息,说明 Scrapy 已经安装成功。

5. 创建第一个 Scrapy 项目

Scrapy 通常不是单文件脚本的写法,而是按项目组织。

先创建项目:

bash 复制代码
scrapy startproject myspider

进入项目目录:

bash 复制代码
cd myspider

你会看到类似这样的结构:

text 复制代码
myspider/
├── scrapy.cfg
└── myspider/
    ├── __init__.py
    ├── items.py
    ├── middlewares.py
    ├── pipelines.py
    ├── settings.py
    └── spiders/

几个最常见的文件先记住:

  • spiders/:放爬虫代码
  • items.py:定义要采集的数据字段
  • pipelines.py:对采集结果做清洗、保存、去重等处理
  • settings.py:配置请求头、并发、延时、导出编码等

刚开始你不需要把每个文件都学透,先会写 spiders/ 里的爬虫就够了。

6. 先写一个最小可运行的爬虫

下面我们用一个经典练习站点 https://quotes.toscrape.com/ 来演示。这个站点专门给爬虫教程使用,页面结构简单,很适合新手练手。

spiders 目录下新建文件 quotes_spider.py

python 复制代码
import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["https://quotes.toscrape.com/"]

    def parse(self, response):
        for quote in response.css("div.quote"):
            yield {
                "text": quote.css("span.text::text").get(),
                "author": quote.css("small.author::text").get(),
                "tags": quote.css("div.tags a.tag::text").getall(),
            }

这段代码先不要急着背,先理解它在做什么:

  • name 是爬虫名字,后面运行时要用
  • start_urls 是起始页面列表
  • parse() 是默认解析函数
  • response.css(...) 用 CSS 选择器定位网页元素
  • yield 把采集到的一条数据交给 Scrapy

7. 运行爬虫并查看结果

在项目根目录执行:

bash 复制代码
scrapy crawl quotes

如果你想把结果直接导出成 JSON:

bash 复制代码
scrapy crawl quotes -O quotes.json

导出成 CSV:

bash 复制代码
scrapy crawl quotes -O quotes.csv

这一步非常重要,因为很多新手第一次接触 Scrapy 时,会突然意识到:原来我不用自己手写文件保存逻辑,Scrapy 已经帮我做好了。

8. response 到底是什么

parse(self, response) 里,response 就是服务器返回的页面响应对象。

你经常会用到这些能力:

python 复制代码
response.url
response.status
response.text
response.css(...)
response.xpath(...)

比如:

python 复制代码
def parse(self, response):
    print(response.url)
    print(response.status)

你可以把它理解成:请求完成后,Scrapy 把网页内容和相关信息都封装进了 response,供你继续解析。

9. 用 CSS 选择器提取数据

Scrapy 对 CSS 选择器支持很好,新手建议优先学这个。

还是看刚才那段代码:

python 复制代码
quote.css("span.text::text").get()
quote.css("small.author::text").get()
quote.css("div.tags a.tag::text").getall()

这里有两个高频方法:

  • .get():取第一个匹配结果
  • .getall():取所有匹配结果并返回列表

常见写法:

python 复制代码
response.css("title::text").get()
response.css("a::attr(href)").getall()
response.css("img::attr(src)").getall()

如果页面里你想抓的是文本,就常用 ::text;如果你想抓链接或图片地址,就常用 ::attr(...)

10. 也可以用 XPath

Scrapy 同样支持 XPath:

python 复制代码
quote.xpath('.//span[@class="text"]/text()').get()
quote.xpath('.//small[@class="author"]/text()').get()
quote.xpath('.//div[@class="tags"]/a[@class="tag"]/text()').getall()

那到底该学 CSS 还是 XPath?

我的建议是:

  • 入门先用 CSS,语法更短
  • 遇到复杂层级、兄弟节点、条件匹配时,再补 XPath

只会其中一种,也已经能做很多事。

11. 学会翻页抓取

单页采集只是开始,真正实用的场景通常都要翻页。

quotes.toscrape.com 首页底部有"下一页"链接,我们可以继续跟进:

python 复制代码
import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["https://quotes.toscrape.com/"]

    def parse(self, response):
        for quote in response.css("div.quote"):
            yield {
                "text": quote.css("span.text::text").get(),
                "author": quote.css("small.author::text").get(),
                "tags": quote.css("div.tags a.tag::text").getall(),
            }

        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

这里最关键的一句是:

python 复制代码
yield response.follow(next_page, callback=self.parse)

它的意思是:继续请求下一页,请求完成后仍然交给 parse() 处理。

这就是 Scrapy 很舒服的地方之一。你不需要自己手工拼接完整链接,也不用自己写复杂的循环控制。

12. 定义 Item,让数据结构更清晰

直接 yield {} 当然可以,但项目稍微大一点之后,定义 Item 会更规范。

先修改 items.py

python 复制代码
import scrapy


class QuoteItem(scrapy.Item):
    text = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()

然后在爬虫中使用:

python 复制代码
import scrapy
from myspider.items import QuoteItem


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["https://quotes.toscrape.com/"]

    def parse(self, response):
        for quote in response.css("div.quote"):
            item = QuoteItem()
            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()
            yield item

Item 的好处是字段边界更清楚,后面写清洗逻辑、保存逻辑时也更统一。

13. 用 Pipeline 清洗数据

很多网页抓下来之后,并不是马上就能直接用。常见问题包括:

  • 文本前后有空格
  • 某些字段为空
  • 日期格式不统一
  • 同一条数据重复出现

这时就可以把清洗逻辑放进 pipelines.py

例如:

python 复制代码
class MyspiderPipeline:
    def process_item(self, item, spider):
        if item.get("text"):
            item["text"] = item["text"].strip()

        if item.get("author"):
            item["author"] = item["author"].strip()

        item["tags"] = [tag.strip() for tag in item.get("tags", [])]
        return item

然后去 settings.py 启用它:

python 复制代码
ITEM_PIPELINES = {
    "myspider.pipelines.MyspiderPipeline": 300,
}

新手可以先记住一句话:Spider 负责抓,Pipeline 负责洗。

14. 常见配置项:新手最该先知道哪些

Scrapy 很强,但默认并不是"想怎么抓就怎么抓"。你至少应该知道下面几个配置项。

14.1 设置请求标识

有些网站会根据请求头判断请求来源。你可以在 settings.py 里设置:

python 复制代码
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Safari/537.36"

14.2 控制抓取速度

不要对目标网站发太快的请求。新手阶段建议至少加一点延时:

python 复制代码
DOWNLOAD_DELAY = 1

14.3 关闭 robots.txt 限制前先理解规则

很多教程会写:

python 复制代码
ROBOTSTXT_OBEY = False

但你需要先知道这意味着什么。robots.txt 是网站声明抓取规则的地方。是否遵守,要根据你的使用场景、目标站点规则和合规要求来判断。新手不要一上来就默认无视规则。

14.4 导出中文时避免乱码

如果你要导出中文 JSON,经常会加:

python 复制代码
FEED_EXPORT_ENCODING = "utf-8"

这能减少导出文件中文乱码的问题。

15. 一个更像真实项目的完整示例

下面给你一个稍完整一点的爬虫,把抓取、翻页和字段提取放在一起:

python 复制代码
import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
    start_urls = ["https://quotes.toscrape.com/"]

    def parse(self, response):
        for quote in response.css("div.quote"):
            yield {
                "text": quote.css("span.text::text").get(default="").strip(),
                "author": quote.css("small.author::text").get(default="").strip(),
                "tags": quote.css("div.tags a.tag::text").getall(),
                "detail_url": response.urljoin(
                    quote.css("span a::attr(href)").get(default="")
                ),
            }

        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

这个版本已经覆盖了初学者最常用的几个动作:

  • 进入起始页
  • 遍历列表块
  • 提取文本、作者、标签、详情链接
  • 继续翻页

16. 新手最容易踩的坑

刚学 Scrapy 时,下面这些问题非常常见:

16.1 选择器写对了,但取不到数据

原因可能包括:

  • 页面结构和你想的不一样
  • 内容在 JavaScript 运行后才出现
  • 你选中的节点层级不对
  • 属性名或类名写错

先做两件事:

  • 在浏览器开发者工具里重新检查元素
  • scrapy shell 网址 进入交互环境测试选择器

例如:

bash 复制代码
scrapy shell https://quotes.toscrape.com/

进入后可以直接测试:

python 复制代码
response.css("title::text").get()

这是定位问题最快的方法之一。

16.2 页面明明能打开,但 Scrapy 抓不到完整内容

这通常是因为目标页面的数据是前端 JavaScript 动态渲染出来的,而 Scrapy 默认抓到的是原始 HTML 响应。

这时你有几种思路:

  • 先在浏览器网络面板里找真实接口,直接抓接口数据
  • 配合 scrapy-playwright 处理需要渲染的页面
  • 对确实只依赖静态 HTML 的站点,优先直接用 Scrapy

对新手来说,先学会抓静态页面,再处理动态页面,学习曲线会平很多。

16.3 导出的数据重复

原因往往是:

  • 翻页逻辑写错
  • 同一个链接被多次跟进
  • 详情页和列表页重复产出同一条记录

解决思路通常是:

  • 检查 next_page 逻辑
  • 用唯一字段去重
  • 在 Pipeline 中做重复判断

17. Scrapy 除了抓网页标题,还能做什么

很多新手误以为 Scrapy 只能"抓文章标题"。其实它能做的事情远不止这些。

17.1 采集结构化业务数据

比如:

  • 商品名称、价格、库存、评分
  • 招聘岗位、地点、经验要求、薪资区间
  • 博客标题、作者、发布时间、标签
  • 课程名称、章节、讲师、简介

17.2 批量跟进详情页

很多网站首页只给列表摘要,真正的详细信息在详情页里。Scrapy 很适合这种"列表页拿链接,再进入详情页补字段"的流程。

17.3 构建数据清洗流水线

抓取不是终点。你可以在 Scrapy 里顺手做:

  • 文本清洗
  • 字段标准化
  • 去重
  • 时间格式转换
  • 金额单位统一

17.4 导出到多种格式

Scrapy 原生就支持把数据导出为:

  • JSON
  • CSV
  • JSON Lines
  • XML

如果你自己再扩展 Pipeline,还可以保存到:

  • MySQL
  • PostgreSQL
  • MongoDB
  • Redis
  • Elasticsearch

17.5 做定时采集任务

Scrapy 项目很适合接入定时任务,比如每天抓一次新闻、每小时抓一次价格、每周抓一次岗位变化。它不只是"写完跑一次"的脚本,也可以发展成长期运行的数据采集工程。

18. 学 Scrapy 时要有的正确认知

Scrapy 很强,但它不是万能的。

你要早点建立这几个认知:

  • 它擅长静态页面和结构化采集
  • 它不负责替你绕过网站风控
  • 它不保证所有 JavaScript 页面都能直接抓
  • 它是工程框架,不只是几行演示代码

也就是说,学习 Scrapy 的重点不是死记 API,而是建立一个完整思路:

  1. 找页面结构
  2. 写选择器
  3. 提取字段
  4. 跟进分页或详情页
  5. 清洗和保存结果

只要这个流程打通,后面你抓不同网站时变化的主要只是选择器和字段,而不是整个方法论。

19. 给新手的学习路线

如果你刚开始学 Scrapy,我建议按下面顺序练习:

  1. 先抓单页标题、链接、文本
  2. 再练列表页循环提取
  3. 再加翻页
  4. 再进入详情页补充字段
  5. 再学习 Item、Pipeline、Settings
  6. 最后再接触动态渲染页面和数据库存储

不要一上来就挑战特别复杂的网站。先把简单站点做通,成就感会高很多,理解也更扎实。

20. 总结

如果你想系统学习 Python 爬虫,Scrapy 是非常值得投入时间的框架。

它的价值不只是"能抓网页",而是把网页采集这件事从零散脚本提升成了一个更规范、更稳定、可扩展的工程流程。对新手来说,一旦你学会了:

  • 创建项目
  • 编写 Spider
  • 用 CSS 或 XPath 解析字段
  • 实现翻页抓取
  • 用 Pipeline 清洗数据
  • 导出结果

你就已经具备了独立完成基础网页数据采集任务的能力。

最后提醒一句:在实际抓取网站数据时,一定要关注目标网站的使用规则、访问频率和合规要求。技术能力越强,越要有边界意识。

如果你刚准备开始动手,最推荐的练习方式就是自己创建一个 Scrapy 项目,抓取 quotes.toscrape.com 这样的练习站点,把本文里的例子完整跑一遍。只要你亲手跑通一次,Scrapy 就不会再显得抽象。

相关推荐
qq_349317482 小时前
Python GUI界面如何实现主题美化_引入ttk模块实现原生外观风格
jvm·数据库·python
Bat U2 小时前
JavaEE|多线程(五)
java·开发语言·jvm
疋瓞2 小时前
pringBoot + 若依框架开发与部署流程
java
forEverPlume2 小时前
c++如何通过解析二进制包头信息解决Socket传输过程中的粘包问题【详解】
jvm·数据库·python
豆豆2 小时前
高校网站用什么CMS?站群管理+国产化适配方案
java·大数据·cms·建站系统·信创国产化·高校网站·站群cms
玉小格2 小时前
对py作业的一个复盘
开发语言·python
BU摆烂会噶2 小时前
【LangGraph 持久化】让 AI Agent 拥有“记忆”
数据库·人工智能·python·langchain
captain3762 小时前
JDBC(Java Data Base Connectivity)
java·开发语言
longxibo2 小时前
【flowable 7.2.0 二开之三:基于 Flowable 7.2 的审批流系统解压即用】
java·tensorflow·jar