scrapy 是一个纯 Python 编写的异步爬虫框架,具备以下特点:
| 优势 | 说明 | 
|---|---|
| 异步高效 | 基于 Twisted,非阻塞 IO | 
| 模块化 | 各部分可灵活配置/替换 | 
| 中间件机制 | 支持代理、UA、cookie 控制等 | 
| 强大的解析 | 内置 XPath、CSS 提取器 | 
| 自动去重 | Scheduler 内部维护请求 fingerprint | 
| 可扩展 | 支持 Redis/MongoDB/Selenium 等集成 | 
适合用于中大型项目,管理多个 Spider 和抓取流程。
一、Scrapy 架构图 & 核心原理
            
            
              bash
              
              
            
          
                  ┌────────────────────────────────────┐
        │           Scrapy Engine 引擎       │
        │   (调度、分发请求与响应的控制中枢)  │
        └────────────────────────────────────┘
                      ▲          ▲
                      │          │
       ┌──────────────┘          └────────────────────┐
       ▼                                               ▼
┌──────────────┐                             ┌─────────────────────┐
│ Scheduler    │                             │  Downloader          │
│ 调度器       │                             │ 下载器(发请求)     │
│ 维护请求队列 │                             └─────────────────────┘
└─────▲────────┘                                       ▲
      │                                                │
      │       ┌─────────────────────────────┐          │
      └──────►│ Downloader Middlewares 下载中间件│◄──────┘
              └─────────────────────────────┘
                          ▲
                          │
                    ┌─────┴───────┐
                    │ Request 请求│
                    └────────────┘
                          │
                          ▼
                    ┌─────────────┐
                    │ Response 响应│
                    └────┬────────┘
                         │
                         ▼
         ┌────────────────────────────────────┐
         │             Spider 爬虫类           │
         │    (处理响应,提取数据和生成新请求) │
         └────────────────┬───────────────────┘
                          │
                          ▼
                ┌────────────────────┐
                │  Item(提取数据)   │
                └───────┬────────────┘
                        ▼
            ┌──────────────────────────┐
            │  Item Pipeline 管道       │
            │(保存数据到文件、数据库等)│
            └──────────────────────────┘
        >工作流程步骤解释
- 
>1. Spider 生成初始 Request 请求
- 
如**
start_urls = ['https://example.com']** - 
被送到引擎(Engine)
 
 - 
 - 
>2. Engine 把请求交给 Scheduler 调度器
- 调度器负责排队请求,避免重复(有去重功能)
 
 - 
>3. Engine 从 Scheduler 取一个请求交给 Downloader 下载器
- Downloader 通过 HTTP 请求抓网页(可带中间件,如代理)
 
 - 
>4. Downloader 下载完后返回 Response 给 Engine
 - 
>5. Engine 把 Response 给 Spider 的 parse() 方法
- 写的**
parse()** 里会提取数据(Item)或继续发送新请求 
 - 写的**
 - 
>6. 提取的数据(Item)交给 Pipeline
- Pipeline 可以把它存储到:文件、MongoDB、MySQL、Elasticsearch 等
 
 - 
>7. Spider 产生的新请求(Request)再次送入 Scheduler → 重复上面过程
 
>模拟完整流程(豆瓣爬虫举例)
假设爬豆瓣 Top250 页:
- 
>1. Spider:创建第一个请求**
Request("https://movie.douban.com/top250")** - 
**>2.**Scheduler:收下请求排队
 - 
**>3.**Downloader:请求网站,返回 HTML
 - 
**>4.**Spider.parse():用 CSS/XPath 抽取电影名、评分
 - 
>5. 生成 Item:
{'title': '肖申克的救赎', 'score': '9.7'} - 
**>6.**Pipeline:保存为 JSON
 - 
**>7.**如果页面有"下一页",parse() 再 yield 一个 Request(下一页链接),流程继续
 
二、Scrapy 项目结构
创建项目:
            
            
              bash
              
              
            
          
          scrapy startproject myspider
        结构如下:
            
            
              bash
              
              
            
          
          myspider/                      ← Scrapy 项目根目录
├── scrapy.cfg                 ← Scrapy 配置文件(全局入口)
├── myspider/                 ← 项目 Python 包目录(真正的业务逻辑在这)
│   ├── __init__.py            ← 表明这是一个包
│   ├── items.py               ← 定义数据结构(Item 模型)
│   ├── middlewares.py         ← 下载中间件定义(如加代理)
│   ├── pipelines.py           ← 管道:保存数据(文件、数据库)
│   ├── settings.py            ← 项目配置文件(如 headers、限速、并发数)
│   └── spiders/               ← 存放所有 Spider 的目录
│       └── example.py         ← 一个 Spider 示例(爬虫脚本)
        2.1 scrapy.cfg(项目运行配置文件)
位置 :根目录
作用:告诉 Scrapy 你要运行哪个项目的设置文件。
内容示例:
            
            
              bash
              
              
            
          
          [settings]
default = myspider.settings   ← 指定 settings.py 的位置
[deploy]
# 用于部署到 scrapyd 时用,不影响本地运行
        当运行 **scrapy crawl xxx**命令时,它会先从这个文件找到项目配置。
2.2 myspider/(项目主模块目录)
这是 Scrapy 真正执行的业务模块,其中所有核心逻辑都写在这里。
1)init.py
让 Python 把 myspider/ 识别为模块包,没别的逻辑。
2)items.py:定义数据结构(类似数据表字段)
Scrapy 的数据提取不是直接用字典,而是专门定义一个**Item** 类。
示例:
            
            
              python
              
              
            
          
          import scrapy
class MyspiderItem(scrapy.Item):
    title = scrapy.Field()
    author = scrapy.Field()
    date = scrapy.Field()
        在 Spider 中提取数据时用:
            
            
              python
              
              
            
          
          item = MyspiderItem()
item['title'] = ...
item['author'] = ...
yield item
        3)middlewares.py:下载中间件(拦截请求与响应)
Scrapy 支持在请求发出前、响应回来后做额外处理,例如:
- 
修改请求头(如 User-Agent)
 - 
设置代理
 - 
自动重试
 - 
伪装成浏览器
 
示例:
            
            
              python
              
              
            
          
          class RandomUserAgentMiddleware:
    def process_request(self, request, spider):
        request.headers['User-Agent'] = 'YourUserAgent'
        在 **settings.py**中启用:
            
            
              python
              
              
            
          
          DOWNLOADER_MIDDLEWARES = {
    'myspider.middlewares.RandomUserAgentMiddleware': 543,
}
        4)pipelines.py:数据管道(保存提取到的数据)
Scrapy 提取的数据通过**Item** 传入管道中做进一步处理,比如:
- 
保存到 JSON、CSV
 - 
存入数据库(MongoDB、MySQL)
 - 
图片下载
 
示例:
            
            
              python
              
              
            
          
          class JsonWriterPipeline:
    def open_spider(self, spider):
        self.file = open('items.json', 'w', encoding='utf-8')
    def process_item(self, item, spider):
        self.file.write(str(item) + "\n")
        return item
    def close_spider(self, spider):
        self.file.close()
        在**settings.py** 启用:
            
            
              python
              
              
            
          
          ITEM_PIPELINES = {
    'myspider.pipelines.JsonWriterPipeline': 300,
}
        5)settings.py:Scrapy 项目的配置中心
可以在这里设置:
| 配置项 | 作用 | 
|---|---|
ROBOTSTXT_OBEY | 
是否遵守 robots 协议(开发建议设为 False) | 
DOWNLOAD_DELAY | 
下载延迟(防止被封 IP) | 
CONCURRENT_REQUESTS | 
最大并发请求数 | 
DEFAULT_REQUEST_HEADERS | 
请求头设置 | 
ITEM_PIPELINES | 
设置哪些 pipeline 被启用 | 
DOWNLOADER_MIDDLEWARES | 
设置中间件 | 
示例片段:
            
            
              python
              
              
            
          
          BOT_NAME = 'myspider'
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 1
CONCURRENT_REQUESTS = 16
DEFAULT_REQUEST_HEADERS = {
   'User-Agent': 'Mozilla/5.0',
}
ITEM_PIPELINES = {
   'myspider.pipelines.JsonWriterPipeline': 300,
}
        6)spiders/:爬虫文件目录(每个爬虫类放一个 .py)
一个 Spider 类 = 一个网站的爬虫逻辑。
示例:
            
            
              python
              
              
            
          
          import scrapy
from myspider.items import MyspiderItem
class BookSpider(scrapy.Spider):
    name = "books"
    start_urls = ['https://books.example.com']
    def parse(self, response):
        for book in response.css('div.book'):
            item = MyspiderItem()
            item['title'] = book.css('h2::text').get()
            item['author'] = book.css('.author::text').get()
            yield item
        运行爬虫:
            
            
              bash
              
              
            
          
          scrapy crawl books
        2.3 各模块作用表
| 位置 | 作用 | 是否需要改 | 
|---|---|---|
scrapy.cfg | 
项目入口配置 | 一般不改 | 
myspider/__init__.py | 
标识模块 | 不改 | 
items.py | 
定义数据字段 | 必改 | 
middlewares.py | 
拦截请求/响应 | 可选改 | 
pipelines.py | 
存储 Item | 可选改 | 
settings.py | 
设置并发、延迟等 | 常用配置项要改 | 
spiders/ | 
放爬虫脚本 | 主战场,必须写 | 
三、项目举例
实战目标
- 
抓取 某勾职位接口。
 - 
提取字段:
- 职位名、公司名、城市、薪资、学历、经验、公司规模
 
 - 
自动翻页(1~5页)
 - 
保存为 CSV 文件
 
第一步:创建项目
打开终端:
            
            
              bash
              
              
            
          
          scrapy startproject lagou_spider
cd lagou_spider
        第二步:定义字段(items.py)
编辑 lagou_spider/lagou_spider/items.py:
            
            
              python
              
              
            
          
          import scrapy
class LagouSpiderItem(scrapy.Item):
    position = scrapy.Field()
    company = scrapy.Field()
    salary = scrapy.Field()
    city = scrapy.Field()
    exp = scrapy.Field()
    edu = scrapy.Field()
    company_size = scrapy.Field()
        第三步:创建爬虫文件
            
            
              bash
              
              
            
          
          cd lagou_spider/lagou_spider/spiders
touch lagou.py  # Windows 用户用编辑器创建 lagou.py 文件
        编辑 lagou.py:
            
            
              python
              
              
            
          
          import scrapy
import json
from lagou_spider.items import LagouSpiderItem
class LagouSpider(scrapy.Spider):
    name = 'lagou'
    allowed_domains = ['lagou.com']
    start_urls = ['https://www.lagou.com/jobs/list_python']
    def start_requests(self):
        for page in range(1, 6):  # 抓取前 5 页
            url = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'
            headers = {
                'Referer': 'https://www.lagou.com/jobs/list_python',
                'User-Agent': 'Mozilla/5.0',
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
            }
            data = {
                'first': 'true' if page == 1 else 'false',
                'pn': str(page),
                'kd': 'python'
            }
            yield scrapy.FormRequest(
                url=url,
                formdata=data,
                headers=headers,
                callback=self.parse
            )
    def parse(self, response):
        data = json.loads(response.text)
        jobs = data['content']['positionResult']['result']
        for job in jobs:
            item = LagouSpiderItem()
            item['position'] = job['positionName']
            item['company'] = job['companyFullName']
            item['salary'] = job['salary']
            item['city'] = job['city']
            item['exp'] = job['workYear']
            item['edu'] = job['education']
            item['company_size'] = job['companySize']
            yield item
        第四步:配置 pipeline 保存 CSV
编辑**lagou_spider/lagou_spider/pipelines.py:**
            
            
              python
              
              
            
          
          import csv
class CsvPipeline:
    def open_spider(self, spider):
        self.file = open('lagou_jobs.csv', 'w', newline='', encoding='utf-8-sig')
        self.writer = csv.writer(self.file)
        self.writer.writerow(['职位', '公司', '薪资', '城市', '经验', '学历', '公司规模'])
    def process_item(self, item, spider):
        self.writer.writerow([
            item['position'],
            item['company'],
            item['salary'],
            item['city'],
            item['exp'],
            item['edu'],
            item['company_size']
        ])
        return item
    def close_spider(self, spider):
        self.file.close()
        第五步:修改配置 settings.py
编辑 lagou_spider/lagou_spider/settings.py,添加或修改:
            
            
              python
              
              
            
          
          ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 1.5  # 降低请求频率,防止封 IP
DEFAULT_REQUEST_HEADERS = {
    'User-Agent': 'Mozilla/5.0'
}
ITEM_PIPELINES = {
    'lagou_spider.pipelines.CsvPipeline': 300,
}
        第六步:运行爬虫
在终端中运行:
            
            
              bash
              
              
            
          
          scrapy crawl lagou
        第七步:查看结果(lagou_jobs.csv)
输出示例(CSV 文件):
| 职位 | 公司 | 薪资 | 城市 | 经验 | 学历 | 公司规模 | 
|---|---|---|---|---|---|---|
| Python开发工程师 | 字节跳动 | 15k-30k | 北京 | 3-5年 | 本科 | 10000人以上 | 
| 后端Python工程师 | 腾讯 | 20k-40k | 深圳 | 5-10年 | 本科 | 5000-10000人 | 
项目结构参考
            
            
              bash
              
              
            
          
          lagou_spider/
├── scrapy.cfg
├── lagou_spider/
│   ├── __init__.py
│   ├── items.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders/
│       └── lagou.py
        注意事项
| 问题 | 说明 | 
|---|---|
| 某勾有反爬 | 添加 DOWNLOAD_DELAY,5 页以内一般不封 | 
| 接口变化 | 抓的是 JSON 接口,稳定性比页面 HTML 好 | 
| 抓太多 | IP 会被封,建议加代理池后使用 | 
| 数据为空 | 设置 Referer + UA + Content-Type 后就正常了 | 
后续可拓展功能
| 功能 | 方法 | 
|---|---|
| 添加代理池 | 中间件 process_request() 设置 | 
| 分布式 | 用 scrapy-redis 实现 | 
| 存 MongoDB | 改 pipeline 用 pymongo 写入 | 
| 接入前端展示 | 输出 JSON/数据库配合前端页面展示 |