14.4 爬虫:Scrapy 框架¶
内容梗概¶
前面三节课演示的单文件爬虫只能"小打小闹",本课引出工程化方案:Python 最有名的爬虫框架 Scrapy。老师先分析单文件写法的四个短板(不会从结果中发现新链接、无多线程、无去重、无模块化),再对照官网架构图讲解 Scrapy 的引擎、调度器、下载器、Spider、Item Pipeline 与中间件各自的分工。随后实操:pipenv 安装 Scrapy、scrapy startproject 建项目、讲解各配置文件(scrapy.cfg、settings.py)的含义,并以知乎话题精华为例写好完整 Spider——用生成器 yield 返回 Item 和新 Request,处理分页,再通过回答→问题的跳转抓取每个热门问题的前 10 条回答,最后由 Item Pipeline 写入 CSV。结尾补充了未展开的高级话题(验证码、代理、模拟登录、cookie 池)并布置作业。
知识点详解¶
为什么需要框架(约 00:00 - 02:20)¶
单文件爬虫持续抓取大量数据的四个问题: 1. 不会从抓取结果中解析出新的待抓对象(如抓了知乎 data 后没有分析 paging 继续翻页); 2. 没有多线程/多进程,机器性能没利用起来; 3. 没有去重等机制; 4. 所有过程写在一个文件里,没有模块化(发送请求、解析 response 应拆成不同模块)。
Scrapy 架构(约 02:20 - 13:50)¶
- Scrapy 是 Python 里功能最全、配置最灵活的爬虫框架(另有 PySpider 等)。
- Spider(爬虫):针对每个网站单独写一个(B 站、优酷、爱奇艺各一个),因为各站结构不同;Spider 负责**解析** response(HTML 或 JSON),并提供种子 URL(入口链接)。
- 数据流:Spider 给出种子 request → 交给**引擎(Engine)** → 引擎转给**调度器(Scheduler)(排队、去重、决定起多少线程)→ 调度器经引擎交给**下载器(Downloader)(可多线程并发下载)→ 返回的 response 扔回 Spider 解析。
- 对应关系:Downloader ≈ requests/Selenium(发请求拿响应),Spider ≈ BeautifulSoup/json(解析)——本质逻辑和之前的简单写法一样,只是模块化了。
- Spider 解析的结果有两种(约 08:00):
- Item:提取到的一条数据,交给 Item Pipeline 处理;
- Request:从结果中发现的新请求(新链接、下一页),重新扔给引擎→调度器,循环抓取。
- Item Pipeline(约 10:10):对 Item 做进一步处理——字段清洗/预处理(如没抓到点赞数的补 0)、写入数据库等。
- 中间件(Middleware)(约 11:40):Downloader 和 Spider 的一进一出都有中间件,对经过的 request/response/item 做统一预处理,例如给 request 加 header、改 User-Agent、加代理。
安装与项目创建(约 13:50 - 23:00)¶
- 官方文档安装向导:
pipenv install scrapy(也可用 conda)。 - 坑 1:
pipenv install -i 镜像源的-i参数**不会**写入生成的 Pipfile,源没改过来会很慢——需手动把 Pipfile 里的源 URL 改成镜像,删掉Pipfile.lock让它重新生成,再 install(约 17:00)。 - 坑 2:
pipenv install 包在pipenv shell之前之后执行都可以,但**执行脚本前必须 shell 进虚拟环境**,安装的库才生效(约 16:00)。 - 安装后虚拟环境的 Scripts 目录下会多一个
scrapy.exe(就像 pip.exe、pipenv.exe),于是可用scrapy命令(约 20:50)。 - 坑 3:VSCode 多窗口时容易进错虚拟环境(老师误留在 music163 环境里导致
scrapy命令找不到)——注意终端提示的环境名(约 21:30)。 - 创建项目:
scrapy startproject 项目名(示例项目名"知乎"),自动生成目录结构。 - 新建 Spider 两种方式:在
spiders/文件夹手动新建 py 文件;或命令scrapy genspider 爬虫名 域名(名字和域名为必选参数)(约 25:20)。老师用 genspider 生成的文件放错了位置——官方文档建议手动创建,照做更稳妥。
项目文件解析(约 27:40 - 39:40)¶
scrapy.cfg:cfg = config。[settings] default = 知乎.settings用"点"是因为把"知乎"文件夹当作 Python 包(含空的__init__.py),settings 是包里的模块;[deploy]部分指定部署目标,本地运行注释掉即可。- 运行逻辑:在含 scrapy.cfg 的目录运行命令 → Scrapy 读 cfg → 找到 settings 模块 → 读入全部配置(项目名、spider 位置、UA 等)。
settings.py关键配置:USER_AGENT:伪装成浏览器的 UA;ROBOTSTXT_OBEY:是否遵守 robots.txt——演示设为 False,一般情况应遵守;- 并发请求数(默认 16,最大 32);
DOWNLOAD_DELAY:下载延迟,默认 0 表示疯狂抓取,"太不礼貌",演示设 3~5 秒(约 37:00);ITEM_PIPELINES = {'知乎.pipelines.AnswerPipeline': 300}:指定处理 Item 的类,后面的数字是**优先级**,数字越小越早处理,可配多个流水线依次处理(约 38:00)。
Spider 编写(约 39:40 - 58:00)¶
- 用框架后 Downloader 不用自己写——不用自己发请求拿 response,只需在 Spider 里写好拿到 response 后怎么处理。
start_urls列表:爬虫入口,可放多个 URL。示例放入知乎话题接口(只写 include 参数;不写 offset/limit 时知乎会默认自动加上)。parse(self, response):每个爬虫默认的解析函数——downloader 每抓到一个 response 就交给它处理,抓一个处理一个,循环往复。parse 的产出有 items 和 request 两种。- 示例 parse 逻辑:
json.loads(response.text)把 JSON 串转字典;- 处理
data(回答列表):for 循环逐条处理; - 处理
paging:is_end为 False 时取next链接生成新请求——用**生成器yield**(不是 return)返回scrapy.Request(url=next_url, callback=self.parse);callback 指定由谁处理该请求的 response,设为 parse 自身即可无限循环翻页(约 46:40 - 48:30)。 - 进阶跳转逻辑(约 48:30 - 58:00):示例不直接存话题下的回答,而是
answer.get("target")→.get("question")取出该回答所属问题的 id,用字符串format拼接"获取某问题下所有回答"的接口 URL(改 path 里的 question id 即切换问题,limit=10、offset=0 取前 10 条),发新请求;因该接口返回格式不同,需用**另一个解析函数**作为 callback 处理。 - 字典
.get(key)取不到时返回 None,可防 KeyError(示例处老师也少写了非空判断)。
Item 与 Item Pipeline(约 58:00 - 65:00)¶
items.py中自定义AnswerItem:只有属性没有方法的类,用scrapy.Field()定义六个字段(id、作者 id、作者名、作者性别、content、点赞数)。- 解析函数中对每个回答创建一个 item 填好字段,用
yield扔给 Item Pipeline。 - Pipeline 类(约 62:50):
__init__只在爬虫启动时由 scrapy 命令执行一次——示例用内置csv模块初始化answers.csv(utf-8、文本模式w而非二进制的wb),csv.DictWriter按 fieldnames 写好表头(writeheader)。 - 此后每 yield 一个 item,框架就调用一次
process_item,把 item 作为一行写入 CSV。 - CSV 是以逗号分隔的文本格式,类似表格的列分隔。
运行效果(约 65:00 - 67:00)¶
- 运行
scrapy crawl 爬虫名(crawl 后必须跟 spider 名字,官方文档"how to run our spider"有写)。 - 屏幕滚动 debug 信息,因设了 5 秒延迟爬得不快;
Ctrl+C中断后查看 answers.csv——抓到 100 多个回答的作者、内容、点赞数,可基于此做数据分析(如"美食话题点赞前十是谁")。
未展开的高级话题(约 67:00 - 73:40)¶
推荐崔庆才的爬虫书(GitBook 在线开源版,不用买),课堂内容只是冰山一角: - 验证码识别:模拟登录、爬取次数超限都会遇到,可能需要图像识别技术; - 代理:单机 IP 发 100 个请求会被封;用多台代理机器(B、C…Z)轮换发请求,服务器看到的是不同 IP,且每个 IP 间隔很久才访问一次,像人类正常速度; - 模拟登录; - Cookie 池:HTTP 是无状态协议,靠 cookie 识别登录用户;注册多个账号存下各自的 cookie 构成 cookie 池,配合代理使用——用户和 IP 都不一样,服务器无法封禁,否则正常服务也无法运行。这是对抗的逻辑。
作业(约 73:40 - 75:20)¶
- 找一个自己感兴趣的站点,用 Scrapy 抓数据保存到 CSV,可参考知乎示例的存 CSV 逻辑,但**不能照抄知乎那份代码**(课程有源代码比对查重工具,相似度高对学生不利)。
- 期限两周。
示例与演示¶
- 项目搭建演示:
mkdir webcrawler→pipenv install(踩镜像源没写进 Pipfile 的坑,手动改源重装)→pipenv shell→pipenv install scrapy(装了 25 个依赖包)→scrapy startproject 知乎→scrapy genspider 知乎 zhihu.com(生成位置放错,说明手动创建更稳)。 - 完整示例(课前写好,课程仓库 spiders/scrapy 目录下可签出):知乎话题精华爬虫——从话题接口出发翻页,对每条精华回答跳转抓取其所属热门问题的前 10 条回答,字段含回答 id、作者 id/名/性别、内容、点赞数,经 AnswerPipeline 写入 answers.csv。运行约一分钟抓到 100+ 条回答。
- 作业要求:自选站点 + Scrapy + 存 CSV,禁抄示例,两周完成。
重点与难点¶
- **理解框架数据流**比记 API 重要:Engine/Scheduler/Downloader/Spider/Pipeline 各管一段,parse 产出 item 或新 request。
- parse 函数用
yield(生成器)而非 return 返回结果;新请求通过scrapy.Request(url, callback=...)构造,callback 指定解析函数,不同格式的响应要用不同的解析函数。 - 配置三处易错:
scrapy crawl后必须跟 spider 名;pipenv -i参数不写入 Pipfile;运行脚本前必须pipenv shell。 - 礼貌爬取:
DOWNLOAD_DELAY设延迟、遵守 robots.txt(演示除外)、控制并发。 - Item Pipeline 优先级数字越小越先执行;
__init__只执行一次(适合打开文件),process_item每条执行一次。 - CSV 是文本文件,打开用
w不用wb。
关联内容¶
- 直接前置:14.1(requests+bs4 静态页)、14.2(Selenium 动态页)、14.3(知乎接口分析——本课 start_urls、接口参数、json 解析全部复用)。
- 基础课程关联:模块与包(
__init__.py、包.模块的点号语法)、生成器(yield)、字典 get、字符串 format、csv 模块、虚拟环境。 - 后续:数据分析章节会用到本课抓取的 CSV(如统计点赞前十);14.4-错误排查课专门解答
scrapy crawl目录错误和 pip 安装 Scrapy 编译报错两个高频问题。