Scrapy 爬虫框架从入门到实战——高效采集不再是难题

前面用 requests + BeautifulSoup 写了那么多爬虫,已经能解决大部分需求了。但如果遇到需要大规模采集、数据量上万页的场景,就该上 Scrapy 了。

Scrapy 是 Python 最强大的爬虫框架,内置了多线程调度、请求去重、数据管道、自动限速等功能,让你写更少的代码,爬更多的数据。

一、安装与创建项目

bash 复制代码
pip install scrapy

# 创建爬虫项目
scrapy startproject myspider

# 目录结构
myspider/
├── scrapy.cfg
└── myspider/
    ├── __init__.py
    ├── items.py          # 定义数据字段
    ├── middlewares.py    # 中间件(代理、UA等)
    ├── pipelines.py     # 数据管道(存数据库/文件)
    ├── settings.py      # 配置文件
    └── spiders/         # 爬虫代码目录
        └── __init__.py

# 创建爬虫
cd myspider
scrapy genspider quotes quotes.toscrape.com

二、核心组件速览

复制代码
URLs → 调度器(Scheduler) → 下载器(Downloader) → 爬虫(Spider) → 管道(Pipeline)
         ↑                          ↓
         去重过滤器(DupeFilter)  中间件(Middleware)

各组件作用:

组件 职责
Spider 解析响应、提取数据、生成新请求
Scheduler 管理待爬取的请求队列
Downloader 下载网页内容
Pipeline 清洗、验证、存储数据
Middleware 处理请求/响应的钩子(UA切换、代理等)

三、实战:爬取名言网站

quotes.toscrape.com 为例,爬取名言、作者和标签。

1. 定义数据模型

python 复制代码
# items.py
import scrapy

class QuoteItem(scrapy.Item):
    text = scrapy.Field()        # 名言内容
    author = scrapy.Field()      # 作者
    tags = scrapy.Field()        # 标签列表
    author_url = scrapy.Field()  # 作者详情页URL

2. 编写爬虫

python 复制代码
# spiders/quotes.py
import scrapy
from myspider.items import QuoteItem

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"):
            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()
            # 提取作者详情页URL
            item["author_url"] = quote.css("a::attr(href)").get()

            yield item

        # 翻页:提取下一页链接并继续爬取
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

3. 数据存储------Pipeline

python 复制代码
# pipelines.py
import json
from itemadapter import ItemAdapter

class JsonPipeline:
    """保存为JSON文件"""
    def open_spider(self, spider):
        self.file = open("quotes.json", "w", encoding="utf-8")
        self.file.write("[\n")
        self.first = True

    def close_spider(self, spider):
        self.file.write("\n]")
        self.file.close()

    def process_item(self, item, spider):
        line = json.dumps(dict(item), ensure_ascii=False)
        if not self.first:
            self.file.write(",\n")
        self.first = False
        self.file.write("  " + line)
        return item


class MongoPipeline:
    """保存到MongoDB"""
    def open_spider(self, spider):
        self.client = pymongo.MongoClient("localhost", 27017)
        self.db = self.client["quotes_db"]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db["quotes"].insert_one(dict(item))
        return item

4. 启用 Pipeline

python 复制代码
# settings.py
ITEM_PIPELINES = {
    "myspider.pipelines.JsonPipeline": 300,   # 数字越小优先级越高
    # "myspider.pipelines.MongoPipeline": 400,
}

5. 运行

bash 复制代码
scrapy crawl quotes -o quotes.json  # 内置方式,最简单
# 或者
scrapy crawl quotes                # 走自定义Pipeline

-o quotes.json 是 Scrapy 内置的导出方式,一行命令搞定。

四、中间件------随机User-Agent

python 复制代码
# middlewares.py
import random

class RandomUserAgentMiddleware:
    """随机切换 User-Agent"""
    user_agents = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0",
    ]

    def process_request(self, request, spider):
        request.headers["User-Agent"] = random.choice(self.user_agents)
        return None  # 返回 None 继续处理
python 复制代码
# settings.py 中启用
DOWNLOADER_MIDDLEWARES = {
    "myspider.middlewares.RandomUserAgentMiddleware": 543,
}

五、爬虫配置优化

python 复制代码
# settings.py ------ 常用配置项

# 遵守 robots.txt(开发时建议设为 False)
ROBOTSTXT_OBEY = False

# 下载延迟(秒),防止被封
DOWNLOAD_DELAY = 1.5   # 每两个请求间隔 1.5 秒

# 并发请求数
CONCURRENT_REQUESTS = 16        # 总并发
CONCURRENT_REQUESTS_PER_DOMAIN = 8  # 每个域名并发

# 请求超时
DOWNLOAD_TIMEOUT = 15

# 是否启用cookies
COOKIES_ENABLED = False

# 自动限流扩展(智能调节爬取速度)
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 5.0
AUTOTHROTTLE_MAX_DELAY = 60.0

# 重试配置
RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 403, 408]

# 日志级别
LOG_LEVEL = "INFO"

六、实战:爬取翻页详情页

很多网站是列表页 → 详情页的结构,两步完成:

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

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

    def parse(self, response):
        """第一步:提取列表页中的书籍链接"""
        for book in response.css("article.product_pod"):
            detail_url = book.css("h3 a::attr(href)").get()
            yield response.follow(detail_url, callback=self.parse_book)

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

    def parse_book(self, response):
        """第二步:解析详情页"""
        item = BookItem()
        item["title"] = response.css("h1::text").get()
        item["price"] = response.css("p.price_color::text").get()
        item["description"] = response.css(
            "#product_description ~ p::text"
        ).get()
        # 提取分类
        breadcrumb = response.css("ul.breadcrumb li a::text").getall()
        item["category"] = breadcrumb[2] if len(breadcrumb) > 2 else ""
        yield item

七、如何断点续爬

Scrapy 支持暂停和恢复:

bash 复制代码
# 启动时生成状态文件
scrapy crawl quotes -s JOBDIR=quotes_state

# 再次运行会自动恢复(Ctrl+C 中断后)
scrapy crawl quotes -s JOBDIR=quotes_state

适合大规模采集怕中断的场景。

八、Scrapy vs requests 怎么选?

场景 推荐方案 原因
爬几十页数据 requests + BS4 简单灵活,没必要上框架
爬几百页数据 Scrapy 多线程自动调度,速度快
爬上万页数据 Scrapy + 分布式 Scrapy-Redis 实现分布式
需要登录/复杂交互 Scrapy + Selenium 配合中间件处理动态内容

建议路线: requests 入门 → Scrapy 进阶 → Scrapy-Redis 分布式

总结

Scrapy 真正强的地方在于:你只管写解析逻辑,调度、并发、重试、限速、去重这些脏活累活框架全包了。对做数据采集来说,Scrapy 是值得投入时间学习的利器。


💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。