Scrapy 爬虫入门:用一个列表页项目讲清请求、解析与翻页

编程狮(w3cschool.cn) 2026-09-08 14:47:37 浏览数 (21)
反馈

Scrapy 爬虫的最小闭环只有五步:创建项目、发送请求、解析字段、跟随下一页、导出结果。初学时先用公开练习页面跑通这个闭环,再考虑并发、代理或分布式,学习效率会更高。

Scrapy 列表页请求解析与翻页封面图

你会用 requests 抓到一页 HTML,却不知道怎样把脚本变成可维护项目;或者写出了 Spider,但翻页后数据重复、字段为空。本文基于 Python 3 与 Scrapy 的常见项目结构,用一个公开练习列表页讲清请求、CSS 选择器、Item、翻页和 JSON 导出。今天这篇文章,编程狮带你完成第一个能运行、能检查、也知道边界的 Scrapy 爬虫。

一、创建 Scrapy 爬虫项目与运行环境

Scrapy 是一个面向网络抓取的 Python 框架。它把请求调度、响应解析、数据管道和并发控制拆成不同组件,适合从一次性脚本升级到可维护项目。

先创建独立虚拟环境并安装 Scrapy:

python -m venv .venv

Windows 激活命令:

.venv\Scripts\activate
python -m pip install scrapy

macOS 与 Linux 激活命令:

source .venv/bin/activate
python -m pip install scrapy

然后创建项目:

scrapy startproject w3cschool_demo
cd w3cschool_demo
scrapy genspider books books.toscrape.com

项目中的 spiders 放抓取规则,items.py 定义数据结构,pipelines.py 处理清洗和保存,settings.py 管并发、延迟与请求头。Python 基础不扎实时,先补一遍 Python3 基础教程,会更容易理解类、生成器和字典。

二、发送请求并确认响应是否正确

Spider 的 start_urls 会生成初始请求,parse 方法接收响应。先只打印状态码和页面标题,确认域名、编码与响应内容正确,再开始写复杂选择器。

import scrapy


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

    def parse(self, response):
        self.logger.info("status=%s url=%s", response.status, response.url)
        title = response.css("title::text").get(default="").strip()
        self.logger.info("page title=%s", title)

运行命令如下:

scrapy crawl books

如果状态码不是 200,先检查 robots.txt、访问频率、域名和网络,不要立即加入伪装请求头或代理。Scrapy 爬虫应遵守目标网站规则,只抓取允许公开访问的数据,并设置合理延迟。

HTTP 请求机制不熟悉时,Python requests 教程 能帮助你先理解请求、响应、状态码和请求头,再回来看 Scrapy 的调度层。

三、用 CSS 选择器解析列表字段

练习页面中,每本书位于 article.product_pod 元素。先用浏览器开发者工具找到重复的列表容器,再逐条提取标题、价格和详情地址。

import scrapy


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

    def parse(self, response):
        for card in response.css("article.product_pod"):
            yield {
                "title": card.css("h3 a::attr(title)").get(default="").strip(),
                "price": card.css("p.price_color::text").get(default="").strip(),
                "url": response.urljoin(card.css("h3 a::attr(href)").get()),
            }

get 取第一个结果,getall 返回全部结果。default 可以避免选择器未命中时立刻对 None 调用 strip。response.urljoin 会基于当前页面补全相对地址,比手工拼接域名可靠。

💡 小提示:选择器先在 scrapy shell URL 中测试。每次只确认一个字段,命中后再写进 Spider,排错会比反复运行整个项目更快。

四、跟随下一页并避免重复抓取

列表页通常通过“下一页”链接翻页。Scrapy 会自动对请求去重,因此最简单的做法是提取 next 链接并 yield response.follow。

    def parse(self, response):
        for card in response.css("article.product_pod"):
            yield {
                "title": card.css("h3 a::attr(title)").get(default="").strip(),
                "price": card.css("p.price_color::text").get(default="").strip(),
                "url": response.urljoin(card.css("h3 a::attr(href)").get()),
            }

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

callback 指定下一页仍由 parse 处理。不要使用固定页数死循环,也不要把页码无限加一;以页面实际存在的下一页链接为停止条件更稳。

数据重复可能来自两个层面:请求 URL 重复,或不同 URL 返回同一业务记录。前者由 Scrapy 默认指纹去重处理,后者需要使用书籍 ID、规范 URL 或其他稳定键在管道中判断。

五、导出 JSON 并做好字段清洗

完成解析后,可以直接导出 JSON Lines:

scrapy crawl books -O books.jsonl

-O 会覆盖旧文件,-o 会追加。反复测试时优先使用 -O,避免误以为爬虫抓到了重复数据。导出前还应把价格转换为数值,并处理空标题、异常编码和重复业务键。

from itemadapter import ItemAdapter


class CleanBookPipeline:
    def process_item(self, item, spider):
        adapter = ItemAdapter(item)
        price_text = adapter.get("price", "").replace("£", "")
        adapter["price"] = float(price_text)
        adapter["title"] = adapter.get("title", "").strip()
        return item

启用管道时,在 settings.py 的 ITEM_PIPELINES 中配置路径和优先级。字段处理规则应集中放在管道中,Spider 只负责定位页面数据。常用 Python 容器和字符串操作可以通过 Python 速查手册 随时核对。

5.1 限速、重试与合规边界

真实网站不是练习靶场。开始抓取前先看 robots.txt 和服务条款,确认数据是否允许自动访问;不要抓取登录后个人数据、绕过验证码或突破访问控制。本文示例使用公开练习站点,不代表任何页面都适合照搬。

可以在 settings.py 设置下载延迟、并发和自动限速:

ROBOTSTXT_OBEY = True
DOWNLOAD_DELAY = 2.0
CONCURRENT_REQUESTS_PER_DOMAIN = 2
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 2.0
AUTOTHROTTLE_MAX_DELAY = 20.0
RETRY_TIMES = 2

这些值是学习项目的保守起点,不是所有网站的统一答案。遇到 429 或 503 时,应减速并等待,而不是提高并发。还要把 User-Agent、日志、失败 URL 和运行时间记录清楚,方便站点管理员联系和自己排错。

正式运行前还可以增加一个小规模验收:只抓取两页,核对记录数、必填字段、重复率和停止条件。确认输出符合预期后,再逐步增加页数。这样既能降低对站点的压力,也能在选择器变化、编码异常或翻页循环时尽早停下。Scrapy 爬虫入门阶段养成“小批验证再扩量”的习惯,比后期清理一大批错误数据更省时间。

Scrapy 列表页爬虫的最小执行流程

总结

Scrapy 爬虫入门的正确顺序是先跑通最小闭环:创建项目、确认响应、解析列表、跟随下一页、导出数据。选择器放在 shell 里逐个验证,字段清洗放进管道,停止条件跟随页面真实链接。

你还要记住三点:请求去重不等于业务数据去重;导出时区分覆盖和追加;抓取速度必须服从站点规则。下一步可以为这个练习项目增加详情页请求,并为标题、价格和 URL 编写三条自动测试。

延伸学习

想把 Scrapy 爬虫继续做完整,可以按这个顺序:

  1. 先跟着 Python 爬虫入门实践课程 系统完成项目结构和数据处理;
  2. 再读 Scrapy 框架基础笔记,复习框架组件的职责;
  3. 最后使用 Python 代码格式化工具 统一练习项目的代码风格。

常见问题

Q:Scrapy 和 requests 应该先学哪个?

先理解 requests 更容易。requests 帮你看清一次请求与响应,Scrapy 则在其上组织调度、解析、管道和并发。简单一次性任务用 requests,持续抓取项目更适合 Scrapy。

Q:选择器在浏览器里有效,Scrapy 为什么为空?

常见原因是页面内容由 JavaScript 后续渲染,而 Scrapy 收到的原始 HTML 没有这些节点。先查看 response.text,确认数据是否实际存在,再决定寻找接口或使用允许的渲染方案。

Q:怎样限制只抓前几页?

可以读取响应中的页码,在达到上限时不再 yield 下一页请求;也可以在 Spider 中维护明确计数。不要依赖关闭程序,因为那会留下不完整输出和难以复现的状态。

Q:429 状态码应该怎么办?

立即降低频率并等待。开启 AutoThrottle、增加 DOWNLOAD_DELAY、减少域名并发,并确认网站是否允许抓取。不要用代理轮换绕过站点的访问限制。

0 人点赞