Scrapy 从入门到实战,爬取网页数据全流程

Scrapy 是 Python 生态中最强大的分布式爬虫框架,专为高效爬取网页数据设计,支持异步请求、自动去重、断点续爬、数据持久化等核心功能,广泛应用于电商数据采集、舆情监控、行业数据分析等场景。

一、Scrapy 核心优势与适用场景

1.1 核心优势

  • 异步高效:基于 Twisted 异步框架,单线程可处理千级并发请求,爬取速度远超 Requests+BeautifulSoup 组合;

  • 功能完备:内置数据解析、请求调度、去重、限速、代理池等模块,无需重复造轮子;

  • 可扩展性强:支持自定义中间件、管道、扩展,轻松应对反爬、分布式爬取等复杂场景;

  • 数据处理便捷:内置 Item、Pipeline 机制,支持将数据直接存储到 MySQL、MongoDB、CSV 等格式;

  • 跨平台兼容:Windows、macOS、Linux 均可运行,支持 Docker 部署。

1.2 典型适用场景

应用场景 实际需求 举例说明
电商数据爬取 批量获取商品价格、评价、销量 爬取京东 / 淘宝商品列表、用户评论
资讯 / 舆情采集 实时抓取新闻、博客、社交媒体内容 爬取行业资讯、微博热点话题
招聘 / 房产数据采集 收集岗位信息、房源数据 爬取 BOSS 直聘岗位、链家二手房信息
数据挖掘预处理 为机器学习提供训练数据 爬取图片、文本、结构化数据

二、前置准备:环境搭建(Python 版)

Scrapy 依赖 Python 环境,推荐使用 Python 3.8~3.11(兼容性最佳),以下是 Windows/macOS/Linux 通用搭建步骤:

2.1 安装 Python 环境

  • 下载地址:https://www.python.org/downloads/

  • 安装注意:Windows 勾选 "Add Python to PATH",macOS/Linux 默认已配置环境变量;

  • 验证:CMD / 终端输入python --version(Windows)或python3 --version(macOS/Linux),显示版本号即成功。

2.2 安装 Scrapy 库

打开 CMD / 终端,执行 pip 命令安装(建议使用国内镜像源加速):

复制代码
\# 国内镜像源(豆瓣)加速安装,避免超时

pip install scrapy -i https://pypi.douban.com/simple
  • 验证:输入scrapy version,显示 Scrapy 版本号(如 2.11.0)即安装成功;

  • 常见问题:

三、核心流程:Scrapy 爬虫开发四步走

Scrapy 爬虫开发遵循 "创建项目→定义 Item→编写爬虫→配置 Pipeline" 的固定流程,以下以 "爬取豆瓣电影 Top250" 为例,详细拆解每一步操作。

3.1 第一步:创建 Scrapy 项目

打开 CMD / 终端,进入想要存放项目的目录,执行以下命令创建项目:

复制代码
\# scrapy startproject + 项目名

scrapy startproject douban\_movie

项目创建后,目录结构如下(核心文件已标注):

复制代码
douban\_movie/

├── douban\_movie/          # 项目核心目录

│   ├── \_\_init\_\_.py

│   ├── items.py           # 定义数据结构(核心)

│   ├── middlewares.py     # 中间件(反爬、代理等)

│   ├── pipelines.py       # 数据存储(核心)

│   ├── settings.py        # 项目配置(核心)

│   └── spiders/           # 爬虫脚本目录(核心)

│       └── \_\_init\_\_.py

└── scrapy.cfg             # 项目配置文件(无需修改)

3.2 第二步:定义 Item(数据结构)

Item 用于规范爬取的数据字段,避免数据混乱,修改douban_movie/``items.py

复制代码
import scrapy

class DoubanMovieItem(scrapy.Item):

    \# 电影名称

    title = scrapy.Field()

    \# 导演+主演

    director\_actor = scrapy.Field()

    \# 上映年份+国家+类型

    year\_country\_genre = scrapy.Field()

    \# 评分

    rating = scrapy.Field()

    \# 评价人数

    comment\_count = scrapy.Field()

    \# 电影简介

    intro = scrapy.Field()
  • 说明:每个字段用scrapy.Field()定义,后续爬虫爬取的数据将按此结构存储。

3.3 第三步:编写爬虫脚本(核心)

douban_movie/spiders目录下创建爬虫文件movie_spider.py,执行爬取逻辑:

复制代码
import scrapy

from douban\_movie.items import DoubanMovieItem

class MovieSpider(scrapy.Spider):

    \# 爬虫名称(唯一,用于启动爬虫)

    name = "douban\_top250"

    \# 允许爬取的域名(防止爬取到其他网站)

    allowed\_domains = \["douban.com"]

    \# 起始URL(爬取的入口页面)

    start\_urls = \["https://movie.douban.com/top250"]

    def parse(self, response):

        \# 1. 解析当前页面的电影数据

        movie\_list = response.xpath('//ol\[@class="grid\_view"]/li')

        for movie in movie\_list:

            item = DoubanMovieItem()

            \# 电影名称(提取text(),strip()去除空格)

            item\["title"] = movie.xpath('.//span\[@class="title"]\[1]/text()').extract\_first().strip()

            \# 导演+主演(split()分割字符串,去除多余空格)

            item\["director\_actor"] = movie.xpath('.//div\[@class="bd"]/p\[1]/text()').extract\_first().strip().split('\n')\[0].strip()

            \# 上映年份+国家+类型

            year\_country\_genre = movie.xpath('.//div\[@class="bd"]/p\[1]/text()').extract\_first().strip().split('\n')\[1].strip()

            item\["year\_country\_genre"] = year\_country\_genre

            \# 评分

            item\["rating"] = movie.xpath('.//span\[@class="rating\_num"]/text()').extract\_first().strip()

            \# 评价人数

            item\["comment\_count"] = movie.xpath('.//div\[@class="star"]/span\[4]/text()').extract\_first().strip()

            \# 电影简介(若不存在则返回空字符串)

            intro = movie.xpath('.//span\[@class="inq"]/text()').extract\_first()

            item\["intro"] = intro.strip() if intro else ""

            \# 提交Item到Pipeline(后续存储数据)

            yield item

        \# 2. 实现翻页爬取(找到下一页URL)

        next\_page = response.xpath('//span\[@class="next"]/a/@href')

        if next\_page:

            \# 拼接下一页完整URL(response.urljoin自动补全域名)

            next\_page\_url = response.urljoin(next\_page.extract\_first())

            \# 发起下一页请求,回调parse函数继续解析

            yield scrapy.Request(next\_page\_url, callback=self.parse)

关键代码解读

  1. 爬虫类继承 :必须继承scrapy.Spider,并重写name(爬虫唯一标识)、allowed_domains(域名限制)、start_urls(起始 URL);

  2. 解析函数 parse :Scrapy 自动发起start_urls的请求,响应结果传入 parse 函数,用于解析数据;

  3. XPath 解析:Scrapy 支持 XPath 和 CSS 选择器,XPath 更灵活(推荐),常用语法:

  • //:从根节点查找所有匹配元素;

  • .//:从当前节点查找子元素;

  • @属性名:提取元素属性(如 href、src);

  • extract_first():提取第一个匹配结果(避免列表);

  1. 翻页逻辑 :找到下一页链接,用scrapy.Request发起新请求,回调parse函数循环解析。

3.4 第四步:配置 Pipeline(数据存储)

Pipeline 用于处理爬虫爬取的 Item 数据,支持存储到 CSV、MySQL、MongoDB 等,以下以 "存储到 CSV 文件" 和 "存储到 MySQL" 为例:

3.4.1 存储到 CSV 文件(简单高效,适合快速导出)

无需修改pipelines.py,直接在启动爬虫时指定输出格式:

复制代码
scrapy crawl douban\_top250 -o douban\_top250.csv

3.4.2 存储到 MySQL(工业级场景,适合长期存储)

  1. 安装 MySQL 驱动:

    pip install pymysql

  2. 修改douban_movie/``pipelines.py,编写 MySQL 存储逻辑:

    import pymysql

    class DoubanMoviePipeline:

    # 爬虫启动时执行(连接数据库)

    def open_spider(self, spider):

    self.conn = pymysql.connect(

    host="localhost", # 数据库地址

    user="root", # 数据库用户名

    password="123456", # 数据库密码

    database="scrapy_db", # 数据库名(需提前创建)

    charset="utf8mb4" # 支持中文

    )

    self.cursor = self.conn.cursor()

    # 创建电影表(若不存在)

    self.cursor.execute('''

    CREATE TABLE IF NOT EXISTS douban_top250 (

    id INT AUTO_INCREMENT PRIMARY KEY,

    title VARCHAR(255) NOT NULL,

    director_actor VARCHAR(255),

    year_country_genre VARCHAR(255),

    rating FLOAT,

    comment_count VARCHAR(50),

    intro TEXT

    )

    ''')

    self.conn.commit()

    # 处理每一个Item(插入数据库)

    def process_item(self, item, spider):

    try:

    self.cursor.execute('''

    INSERT INTO douban_top250 (title, director_actor, year_country_genre, rating, comment_count, intro)

    VALUES (%s, %s, %s, %s, %s, %s)

    ''', (

    item["title"],

    item["director_actor"],

    item["year_country_genre"],

    item["rating"],

    item["comment_count"],

    item["intro"]

    ))

    self.conn.commit()

    except Exception as e:

    print(f"插入数据失败:{e}")

    self.conn.rollback()

    return item # 必须返回Item,否则后续Pipeline无法处理

    # 爬虫关闭时执行(关闭数据库连接)

    def close_spider(self, spider):

    self.cursor.close()

    self.conn.close()

  3. 启用 Pipeline:修改douban_movie/``settings.py,取消ITEM_PIPELINES的注释并配置:

    ITEM_PIPELINES = {

    "douban_movie.pipelines.DoubanMoviePipeline": 300, # 300为优先级(越小越先执行)

    }

3.5 启动爬虫

进入项目根目录(douban_movie目录),执行以下命令启动爬虫:

复制代码
\# scrapy crawl + 爬虫名称(name字段定义的名称)

scrapy crawl douban\_top250
  • 运行效果:Scrapy 会自动发起请求、解析数据、存储到 MySQL,终端会显示爬取进度(请求数、Item 数、耗时等)。

四、核心配置:settings.py 关键参数(优化爬取效果)

settings.py是 Scrapy 项目的核心配置文件,合理配置可提升爬取效率、规避反爬:

复制代码
\# 1. 爬虫名称(可选)

BOT\_NAME = "douban\_movie"

\# 2. 爬虫模块路径(无需修改)

SPIDER\_MODULES = \["douban\_movie.spiders"]

NEWSPIDER\_MODULE = "douban\_movie.spiders"

\# 3. 遵守robots.txt协议(默认True,反爬友好;爬取自己的网站可设为False)

ROBOTSTXT\_OBEY = True

\# 4. 并发请求数(默认16,根据目标网站抗压能力调整,避免被封IP)

CONCURRENT\_REQUESTS = 8

\# 5. 下载延迟(单位:秒,默认0,建议设置1\~3秒,模拟人工访问)

DOWNLOAD\_DELAY = 2

\# 6. 禁用Cookie(默认False,反爬场景可设为True)

\# COOKIES\_ENABLED = False

\# 7. 设置请求头(模拟浏览器,避免被识别为爬虫)

DEFAULT\_REQUEST\_HEADERS = {

    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,\*/\*;q=0.8",

    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",

    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",

}

\# 8. 启用Pipeline(已配置,见3.4.2)

ITEM\_PIPELINES = {

    "douban\_movie.pipelines.DoubanMoviePipeline": 300,

}

\# 9. 下载超时时间(单位:秒,默认180,避免长时间阻塞)

DOWNLOAD\_TIMEOUT = 30

\# 10. 重试次数(默认2,网络波动时自动重试)

RETRY\_TIMES = 3

五、进阶技巧:解决实际爬取问题

5.1 处理动态加载页面(JavaScript 渲染)

Scrapy 默认不支持解析 JS 动态加载的数据(如滑动加载、点击加载),解决方案:

  1. 分析接口:F12 打开开发者工具→Network→XHR,找到动态加载的 API 接口,直接爬取接口数据(推荐,效率最高);

  2. 集成 Selenium:通过 Scrapy 中间件将 Selenium 与 Scrapy 结合,模拟浏览器渲染:

    # 安装依赖

    pip install scrapy-selenium selenium webdriver-manager

修改settings.py添加中间件:

复制代码
DOWNLOADER\_MIDDLEWARES = {

    "scrapy\_selenium.SeleniumMiddleware": 800,

}

\# Selenium配置

SELENIUM\_DRIVER\_NAME = "chrome"

SELENIUM\_DRIVER\_EXECUTABLE\_PATH = None  # 自动下载驱动

SELENIUM\_DRIVER\_ARGUMENTS = \["--headless=new"]  # 无头模式

5.2 配置代理 IP(规避 IP 封禁)

反爬严格的网站会封禁频繁请求的 IP,需配置代理池,修改middlewares.py

复制代码
class ProxyMiddleware:

    def process\_request(self, request, spider):

        # 代理IP列表(可替换为自己的代理池)

        proxies = \[

            "http://123.45.67.89:8080",

            "http://98.76.54.32:8888",

        ]

        # 随机选择一个代理

        proxy = random.choice(proxies)

        request.meta\["proxy"] = proxy

启用中间件(settings.py):

复制代码
DOWNLOADER\_MIDDLEWARES = {

    "douban\_movie.middlewares.ProxyMiddleware": 750,

}

5.3 断点续爬(避免重复爬取)

爬取大量数据时中断后,可通过JOBDIR参数实现断点续爬:

复制代码
scrapy crawl douban\_top250 -s JOBDIR=./job\_info
  • 说明:./job_info为存储爬取状态的目录,再次执行该命令会从上次中断的位置继续爬取。

5.4 数据去重(避免重复存储)

通过 Pipeline 实现数据去重(以 MySQL 为例),修改process_item方法:

复制代码
def process\_item(self, item, spider):

    # 先查询数据库是否已存在该电影

    self.cursor.execute('SELECT id FROM douban\_top250 WHERE title = %s', (item\["title"],))

    if self.cursor.fetchone():

        print(f"电影《{item\['title']}》已存在,跳过")

        return item

    # 不存在则插入

    try:

        self.cursor.execute('''INSERT INTO ...''', (...))

        self.conn.commit()

    except Exception as e:

        self.conn.rollback()

    return item

六、常见坑与避坑技巧

6.1 爬取速度过慢

  • 原因 1:DOWNLOAD_DELAY设置过大 → 适当减小(如 1 秒);

  • 原因 2:CONCURRENT_REQUESTS设置过小 → 调整为 16~32(根据网站抗压能力);

  • 原因 3:未禁用图片加载 → 配置禁用图片:

    DOWNLOADER_MIDDLEWARES = {

    "scrapy.downloadermiddlewares.images.ImagesPipeline": None,

    }

6.2 被目标网站封禁 IP

  • 解决方案 1:设置DOWNLOAD_DELAY(1~3 秒),模拟人工访问;

  • 解决方案 2:配置代理 IP 池(推荐使用付费代理,稳定性更高);

  • 解决方案 3:更换User-Agent,避免单一请求头;

  • 解决方案 4:启用 Cookie 池,模拟登录状态爬取。

6.3 数据解析失败(XPath/CSS 选择器错误)

  • 排查步骤 1:在终端用scrapy shell调试,验证选择器是否正确:

    scrapy shell "https://movie.douban.com/top250"

    # 进入交互环境后,执行XPath验证

    response.xpath('//ol[@class="grid_view"]/li')

  • 排查步骤 2:检查网页源码(F12),确认元素 class/id 是否动态变化(如带随机字符串);

  • 解决方案:改用更稳定的父元素定位,或使用 contains 匹配部分属性值:

    # 匹配class包含"grid_view"的ol元素

    response.xpath('//ol[contains(@class, "grid_view")]/li')

6.4 数据库插入失败

  • 原因 1:中文编码问题 → 数据库 charset 设为utf8mb4

  • 原因 2:字段长度不足 → 调整 VARCHAR 长度(如 title 设为 255);

  • 原因 3:SQL 语法错误 → 用print(sql)输出 SQL 语句,手动验证语法。

七、进阶方向:Scrapy 分布式爬取

当需要爬取千万级数据时,单台机器效率过低,可通过scrapy-redis实现分布式爬取:

  1. 安装依赖:

    pip install scrapy-redis

  2. 修改settings.py配置 Redis:

    # 启用Redis调度器(分布式去重、任务调度)

    SCHEDULER = "scrapy_redis.scheduler.Scheduler"

    # 启用Redis去重

    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

    # Redis连接配置

    REDIS_URL = "redis://localhost:6379/0"

  3. 启动 Redis 服务,多台机器运行同一爬虫,即可实现任务分发和数据共享。

相关推荐
深蓝电商API5 小时前
Scrapy爬虫部署到Scrapyd服务端详解
爬虫·python·scrapy
深蓝电商API5 小时前
Scrapy Feed Exports 进阶:多种格式导出配置
爬虫·python·scrapy
深蓝电商API1 天前
Scrapy信号机制:监控爬虫全生命周期
爬虫·python·scrapy
深蓝电商API1 天前
Scrapy与MongoDB管道集成:异步存储方案
数据库·scrapy·mongodb
深蓝电商API2 天前
Scrapy 爬虫异常处理与重试机制优化
爬虫·python·scrapy
深蓝电商API2 天前
Scrapy CrawlSpider规则提取器深度实战
爬虫·python·scrapy
写代码的【黑咖啡】2 天前
深入了解 Python 中的 Scrapy:强大的网络爬虫框架
爬虫·python·scrapy
深蓝电商API3 天前
Scrapy ImagesPipeline和FilesPipeline自定义使用
爬虫·python·scrapy
深蓝电商API3 天前
Scrapy与Splash结合爬取JavaScript渲染页面
javascript·爬虫·python·scrapy