关于 scrapy框架 详解
scrapy 是一个纯 Python 编写的异步爬虫框架,具备以下特点:
优势 | 说明 |
---|---|
异步高效 | 基于 Twisted,非阻塞 IO |
模块化 | 各部分可灵活配置/替换 |
中间件机制 | 支持代理、UA、cookie 控制等 |
强大的解析 | 内置 XPath、CSS 提取器 |
自动去重 | Scheduler 内部维护请求 fingerprint |
可扩展 | 支持 Redis/MongoDB/Selenium 等集成 |
适合用于中大型项目,管理多个 Spider 和抓取流程。
一、Scrapy 架构图 & 核心原理
┌────────────────────────────────────┐│ 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 项目结构
创建项目:
scrapy startproject myspider
结构如下:
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 你要运行哪个项目的设置文件。
内容示例:
[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
类。
示例:
import scrapyclass MyspiderItem(scrapy.Item):title = scrapy.Field()author = scrapy.Field()date = scrapy.Field()
在 Spider 中提取数据时用:
item = MyspiderItem()
item['title'] = ...
item['author'] = ...
yield item
3)middlewares.py
:下载中间件(拦截请求与响应)
Scrapy 支持在请求发出前、响应回来后做额外处理,例如:
修改请求头(如 User-Agent)
设置代理
自动重试
伪装成浏览器
示例:
class RandomUserAgentMiddleware:def process_request(self, request, spider):request.headers['User-Agent'] = 'YourUserAgent'
在 settings.py
中启用:
DOWNLOADER_MIDDLEWARES = {'myspider.middlewares.RandomUserAgentMiddleware': 543,
}
4)pipelines.py
:数据管道(保存提取到的数据)
Scrapy 提取的数据通过 Item
传入管道中做进一步处理,比如:
保存到 JSON、CSV
存入数据库(MongoDB、MySQL)
图片下载
示例:
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 itemdef close_spider(self, spider):self.file.close()
在 settings.py
启用:
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 | 设置中间件 |
示例片段:
BOT_NAME = 'myspider'ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 1
CONCURRENT_REQUESTS = 16DEFAULT_REQUEST_HEADERS = {'User-Agent': 'Mozilla/5.0',
}ITEM_PIPELINES = {'myspider.pipelines.JsonWriterPipeline': 300,
}
6)spiders/
:爬虫文件目录(每个爬虫类放一个 .py)
一个 Spider 类 = 一个网站的爬虫逻辑。
示例:
import scrapy
from myspider.items import MyspiderItemclass 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
运行爬虫:
scrapy crawl books
2.3 各模块作用表
位置 | 作用 | 是否需要改 |
---|---|---|
scrapy.cfg | 项目入口配置 | 一般不改 |
myspider/__init__.py | 标识模块 | 不改 |
items.py | 定义数据字段 | 必改 |
middlewares.py | 拦截请求/响应 | 可选改 |
pipelines.py | 存储 Item | 可选改 |
settings.py | 设置并发、延迟等 | 常用配置项要改 |
spiders/ | 放爬虫脚本 | 主战场,必须写 |
三、项目举例
实战目标
抓取 某勾职位接口。
提取字段:
职位名、公司名、城市、薪资、学历、经验、公司规模
自动翻页(1~5页)
保存为 CSV 文件
第一步:创建项目
打开终端:
scrapy startproject lagou_spider
cd lagou_spider
第二步:定义字段(items.py)
编辑 lagou_spider/lagou_spider/items.py
:
import scrapyclass 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()
第三步:创建爬虫文件
cd lagou_spider/lagou_spider/spiders
touch lagou.py # Windows 用户用编辑器创建 lagou.py 文件
编辑 lagou.py
:
import scrapy
import json
from lagou_spider.items import LagouSpiderItemclass 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
:
import csvclass 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 itemdef close_spider(self, spider):self.file.close()
第五步:修改配置 settings.py
编辑 lagou_spider/lagou_spider/settings.py
,添加或修改:
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 1.5 # 降低请求频率,防止封 IP
DEFAULT_REQUEST_HEADERS = {'User-Agent': 'Mozilla/5.0'
}
ITEM_PIPELINES = {'lagou_spider.pipelines.CsvPipeline': 300,
}
第六步:运行爬虫
在终端中运行:
scrapy crawl lagou
第七步:查看结果(lagou_jobs.csv)
输出示例(CSV 文件):
职位 | 公司 | 薪资 | 城市 | 经验 | 学历 | 公司规模 |
---|---|---|---|---|---|---|
Python开发工程师 | 字节跳动 | 15k-30k | 北京 | 3-5年 | 本科 | 10000人以上 |
后端Python工程师 | 腾讯 | 20k-40k | 深圳 | 5-10年 | 本科 | 5000-10000人 |
项目结构参考
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/数据库配合前端页面展示 |