楼层: 首页/ 软件技术/ Python 爬虫技术/ Scrapy 框架:爬虫界的工程队
8

Scrapy 框架:爬虫界的工程队

Scrapy: The Industrial Framework

写脚本爬几个页面用 requests 够了。但要爬整站、几千个页面、管队列、管去重、存数据库——手写脚本就乱了。Scrapy 就是爬虫界的工程队,异步高性能,帮你把"下载、解析、存储"流水线搭好。

安装与项目创建

pip install scrapy scrapy startproject myspider cd myspider scrapy genspider blog example.com # 自动生成项目结构: # myspider/ # ├── scrapy.cfg # └── myspider/ # ├── items.py 定义数据结构 # ├── pipelines.py 处理爬下来的数据(存数据库) # ├── settings.py 配置 # └── spiders/ 爬虫代码放这

核心组件

组件职责
Spider你写的爬虫,定义从哪开始、怎么解析
Engine引擎,调度一切
Scheduler调度器,URL 队列(去重、排队)
Downloader下载器,发请求
Pipeline管道,数据清洗、存数据库
Middleware中间件,改请求/响应(加 UA、换代理)

写一个 Spider

# spiders/blog_spider.py import scrapy class BlogSpider(scrapy.Spider): name = "blog" start_urls = ["https://example.com/blog?page=1"] def parse(self, response): # response 已经是 lxml 对象,直接 .css()/.xpath() for post in response.css(".post"): yield { "title": post.css("h2::text").get(), "url": post.css("a::attr(href)").get(), } # 翻页:yield 一个 Request,Scrapy 自动排队继续爬 next_page = response.css(".next a::attr(href)").get() if next_page: yield response.follow(next_page, callback=self.parse) # 运行:scrapy crawl blog -o posts.csv

Item 与 ItemLoader

# items.py —— 定义爬的数据长什么样 import scrapy class PostItem(scrapy.Item): title = scrapy.Field() url = scrapy.Field() author = scrapy.Field() created = scrapy.Field() # Spider 里用 ItemLoader 自动清洗 from scrapy.loader import ItemLoader def parse(self, response): loader = ItemLoader(item=PostItem(), response=response) loader.add_css("title", "h2::text") loader.add_css("url", "a::attr(href)") yield loader.load_item()

settings.py 关键配置详解

配置干嘛用
ROBOTSTXT_OBEY遵守 robots.txt(生产建议 True)
USER_AGENT默认 UA
CONCURRENT_REQUESTS并发数(默认 16)
DOWNLOAD_DELAY请求间隔秒数
ITEM_PIPELINES启用哪些 Pipeline(数字=优先级)
DOWNLOADER_MIDDLEWARES启用哪些中间件
RETRY_ENABLED失败重试(默认 True)
LOG_LEVEL日志级别(DEBUG/INFO/WARNING)

ItemLoader 内置处理器

处理器干嘛用
TakeFirst()取第一个非空值
Join()把列表拼成字符串
MapCompose()对每个值依次调用函数
Identity()原样返回

中间件:UA 池与代理

# middlewares.py —— 每个请求前加随机 UA import random USER_AGENTS = ["Mozilla/5.0 ...", "Mozilla/5.0 ..."] class RandomUA: def process_request(self, request, spider): request.headers["User-Agent"] = random.choice(USER_AGENTS) return None # None = 继续处理;return Response = 短路

meta 传参:翻页时带上上下文

# 从列表页进详情页,把列表页信息带过去 def parse_list(self, response): for link in response.css(".post a::attr(href)").getall(): yield response.follow( link, callback=self.parse_detail, meta={"list_page": response.url} # 带过去 ) def parse_detail(self, response): list_url = response.meta["list_page"] # 拿回来 yield {"title": response.css("h1::text").get(), "from_list": list_url}

Pipeline:存数据库

# pipelines.py import pymysql class MysqlPipeline: def open_spider(self, spider): self.conn = pymysql.connect(host="localhost", user="root", password="123", database="spider") self.cur = self.conn.cursor() def process_item(self, item, spider): sql = "INSERT IGNORE INTO posts (title, url) VALUES (%s, %s)" self.cur.execute(sql, (item["title"], item["url"])) self.conn.commit() return item def close_spider(self, spider): self.cur.close() self.conn.close()

settings.py 关键配置

ROBOTSTXT_OBEY = False # 遵守 robots.txt(生产建议 True) USER_AGENT = "Mozilla/5.0 ..." CONCURRENT_REQUESTS = 16 # 并发数 DOWNLOAD_DELAY = 1 # 每次请求间隔1秒 ITEM_PIPELINES = { "myspider.pipelines.MysqlPipeline": 300, } # DOWNLOADER_MIDDLEWARES 加 UA 池/代理池

Scrapy 常用命令

命令干嘛用
scrapy startproject mybot创建项目
scrapy genspider blog example.com生成爬虫文件
scrapy crawl blog运行爬虫
scrapy crawl blog -o out.json导出 JSON
scrapy shell <url>交互式调试(REPL 里试选择器)
scrapy list列出所有爬虫名

CrawlSpider:自动爬整站

from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class SiteSpider(CrawlSpider): name = "site" allowed_domains = ["example.com"] start_urls = ["https://example.com/"] rules = ( # 自动提取所有 /post/ 开头的链接,交给 parse_post 解析 Rule(LinkExtractor(allow=r"/post/\d+"), callback="parse_post", follow=True), ) def parse_post(self, response): yield {"title": response.css("h1::text").get()}

Scrapy 工作流程

论Scrapy 内部怎么转

① Spider 给 start_urls → ② Engine 交给 Scheduler 排队 → ③ Downloader 下载(中间件加UA/代理) → ④ 响应回 Engine → ⑤ 交给 Spider.parse 解析 → ⑥ yield item 给 Pipeline 存 → ⑦ yield Request 给 Scheduler 继续爬。整个流程自动跑,你只管写 parse。

运行 Scrapy

# 运行爬虫,结果存 CSV scrapy crawl blog -o posts.csv # 2026-09-16 10:00:00 [scrapy] INFO: Spider opened # 2026-09-16 10:00:01 [scrapy] DEBUG: Crawled (200) <GET https://example.com/1> # 2026-09-16 10:00:02 [scrapy] DEBUG: Crawled (200) <GET https://example.com/2> # ... # 2026-09-16 10:00:10 [scrapy] INFO: Closing spider (finished) # 2026-09-16 10:00:10 [scrapy] INFO: Stored 50 items # 交互式调试:scrapy shell <url>,在 REPL 里试 response.css()
本章面试题 · Scrapy

1.(概念题)Scrapy 和手写 requests 脚本比,强在哪?

查看答案

答案:① 异步并发,几千个请求自动调度;② 内置去重、重试、User-Agent 轮换;③ 流水线(Pipeline)分离存储逻辑;④ 中间件可插拔。手写脚本管队列和去重太痛苦。

2.(概念题)Scrapy 的 yield 是干嘛的?为什么不是 return?

查看答案

答案:parse() 里 yield item 是"把这个数据交给 Pipeline",yield Request 是"把这个 URL 交给调度器继续爬"。yield 是生成器,Scrapy 逐批取数据,不会一次占满内存。

3.(概念题)CrawlSpider 和普通 Spider 区别?

查看答案

答案:普通 Spider 手动 yield Request 翻页;CrawlSpider 用 Rule+LinkExtractor 自动按规则发现链接,适合爬整站。