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/數據庫配合前端頁面展示 |