公司动态
Python爬虫工程化选型指南:从requests到Playwright与Scrapy
做技术选型的时候最怕的不是“没有工具”而是“工具太多不知道怎么选”。Python 爬虫圈子尤其如此GitHub 上每天都有新的爬虫项目出现有人开源了一个“一键采集某平台数据”的工具有人实现了新的反反爬策略。但真到自己动手写爬虫时很多人却卡住了——收藏夹里存了几十个项目不知道哪个能解决自己的问题requests 能抓到静态页面遇到动态渲染的页面就束手无策数据量一旦上来单机脚本跑不动又不知道该怎么扩展。这篇文章不打算吹捧某个具体的“爆火项目”而是想聊一个更值得关注的变化Python 爬虫工具链正在从“requests BeautifulSoup 的单点脚本”走向“浏览器自动化 任务调度 数据治理”的工程化方案。这个变化决定了你应该选什么工具、怎么搭项目结构、怎么避免把时间浪费在重复造轮子上。读完你会得到一份能直接落地的工具选型清单、一套完整的示例代码以及一份常见问题排查清单。1. 爬虫圈最近在关注什么从“写请求”到“工程化”很多初学者对爬虫的理解是“发出 HTTP 请求拿到 HTML用正则或解析库提取数据”。这个理解本身没有错它对应的是最基础的爬虫模型。但如果你在 GitHub 上多翻几个高 star 的爬虫项目会发现大家讨论的重心早就变了如何管理 Cookie、Token 和登录态如何处理动态渲染的页面而不是只抓静态 HTML如何让爬虫按频率、按优先级、按增量去抓取如何把抓到的数据清洗、去重、落库如何监控爬虫的健康状态失败了怎么重试如何在不影响目标网站的前提下合法合规地抓取数据。这些问题加起来就是“爬虫工程化”。从材料来看无论是 GitHub 上的开源框架还是社区里的热门讨论都在往这个方向走。单点工具依然有价值但生产级的爬虫系统靠一个 requests 循环是不够的。所以你会看到近年热门的 Python 爬虫工具不再只有 requests 和 BeautifulSoup。浏览器自动化工具、任务调度框架、布隆过滤器、代理池、日志系统都被整合到了爬虫项目里。对开发者来说这既是好事也是挑战好处是有成熟方案可以借力坏处是学习曲线变陡了。这篇文章的判断是2025 年以后的爬虫选型优先考虑“能工程化”的工具而不是单个功能最强的工具。下面我按这个思路从基础工具到完整框架做一个梳理。2. 核心概念先把爬虫工具的分类搞明白在写代码之前建议先建立一张“工具地图”。很多新手学爬虫今天看一个 requests 教程明天看一个 Scrapy 教程最后混在一起不知道哪个环节该用哪个工具。其实爬虫工具可以按功能分成四层2.1 请求层负责发起 HTTP 请求、处理响应、管理会话。代表工具是 requests、httpx、aiohttp。requests 适合快速上手httpx 支持 HTTP/2 和异步aiohttp 适合高并发场景。2.2 解析层负责把响应内容转成结构化数据。代表工具有 BeautifulSoup、lxml、parsel、json。BeautifulSoup API 友好lxml 性能好parsel 是 Scrapy 内部的解析器语法兼容 CSS 和 XPath。2.3 浏览器自动化层负责模拟真实浏览器解决动态渲染、JavaScript 加密、复杂交互等问题。代表工具有 Selenium、Playwright、DrissionPage。这一层是近年变化最大的部分后面单独讲。2.4 框架与调度层负责把请求、解析、存储、去重、重试、调度整合成一个完整系统。代表工具有 Scrapy、feapder、crawlab。如果你的爬虫只需要跑一次、抓几十页数据用框架反而笨重但如果要长期运行这一层几乎必须引入。2.5 按任务类型分类从任务形态上看爬虫还可以分成三类这有助于你做架构设计类型特点典型使用方式批量型爬虫一次性抓完存量数据单机脚本或小集群重解析不重调度增量型爬虫只抓新增或变化的数据需要去重、时间戳或 ID 记录通常配数据库垂直型爬虫针对某类站点深耕需要针对单个站点定制解析和反爬策略明白了分类再回头看具体工具就不会迷路。接下来我们从环境准备开始逐步跑通一个最小爬虫再到动态页面最后说框架选型。3. 环境准备Python 与虚拟环境的基础配置无论你选哪个爬虫框架第一步都是把 Python 环境收拾干净。这里最容易被忽略的是不要用系统全局 Python 直接装包尤其是 Ubuntu 或 macOS 自带的环境装到一半权限报错、依赖冲突都很常见。3.1 安装 Python建议使用 Python 3.9 以上版本。macOS/Linux 可以优先看系统是否自带Windows 用户从官网下载安装包。如果你需要多个 Python 版本切换可以考虑使用 pyenv 或 conda 管理。版本细节以你实际安装的为准本文不指定死版本。安装完成后在终端验证python3 --version pip3 --version如果你的系统里同时有多个 Python建议使用python3 -m pip而不是直接pip避免装错环境。3.2 创建虚拟环境虚拟环境的作用是隔离项目依赖。一个爬虫项目用 requests 1.0另一个用 requests 2.0互不干扰。mkdir python-spider-demo cd python-spider-demo python3 -m venv venv source venv/bin/activateWindows 下激活命令是venv\Scripts\activate激活成功后终端会显示(venv)前缀。接下来安装本阶段需要的包pip install requests beautifulsoup4 lxml国内网络环境下如果 pip 下载慢可以临时使用国内镜像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests beautifulsoup4 lxml注意这里只是给你一个通用的镜像配置方式。实际使用哪个源取决于你所在网络环境的可用性请以能访问、可信任的源为准。3.3 关于 GitHub 开源项目的下载问题GitHub 本身就是爬虫开源项目的最大聚集地但很多开发者会遇到仓库下载慢、release 附件下载不动的情况。这里可以推荐两种通用思路一是使用 GitHub 镜像站二是使用仓库下载加速服务常见的有 ghproxy 这类转发服务。这些服务的可用性和安全性随时可能变化使用前注意核对代码来源生产环境不要使用你不信任的第三方打包。4. 入门级组合requests BeautifulSoup 快速抓取静态页面先写一个最小可运行的示例。示例目标站使用一个公开的爬虫练习站点quotes.toscrape.com它专门用来教学不涉及敏感数据。新建文件demo_quotes.py# 文件路径python-spider-demo/demo_quotes.py import requests from bs4 import BeautifulSoup def fetch_page(page: int): url fhttps://quotes.toscrape.com/page/{page}/ try: resp requests.get(url, timeout10) resp.raise_for_status() except requests.RequestException as e: print(f请求失败: {e}) return [] soup BeautifulSoup(resp.text, html.parser) quotes [] for item in soup.select(.quote): text item.select_one(.text).get_text(stripTrue) author item.select_one(.author).get_text(stripTrue) quotes.append({text: text, author: author}) return quotes if __name__ __main__: for page in range(1, 4): data fetch_page(page) print(f第 {page} 页获取到 {len(data)} 条数据) for quote in data[:2]: print(quote[author], quote[text][:30])代码逻辑说明requests.get负责发起 HTTP 请求timeout10是必加的否则遇到慢接口会一直挂住resp.raise_for_status()会在状态码非 200 时抛异常避免拿到错误页面还继续解析soup.select(.quote)使用 CSS 选择器定位所有名言节点get_text(stripTrue)去掉首尾空白避免解析出大量换行符。运行方式python demo_quotes.py预期输出类似第 1 页获取到 10 条数据 Albert Einstein “The world as we have created it...” J.K. Rowling “It is our choices, Harry, that show...” 第 2 页获取到 10 条数据 ...如果这里能跑通说明你的 requests、BeautifulSoup、Python 环境都没有问题。这个组合适合目标页面是服务端渲染、不需要登录、反爬策略不强的场景。它也是后续学习框架的基础。5. 动态页面与反爬场景DrissionPage 和 Playwright 如何选很多真实站点的数据不是写在静态 HTML 里的而是通过 JavaScript 请求接口后再渲染。用 requests 直接抓得到的 HTML 里没有数据。这时候需要浏览器自动化工具。老牌方案是 Selenium。但 Selenium 的问题也很明显需要下载对应版本的 webdriver配置繁琐启动浏览器时还要处理各种环境问题。近两年更常用的方案是 Playwright 和 DrissionPage。工具核心特点适用场景Selenium历史最久文档多老项目维护、兼容旧浏览器测试Playwright微软开源内置浏览器下载支持多种语言跨浏览器测试、复杂交互、截图DrissionPage国产开源把 requests 和浏览器控制封装到一起配置简单快速做反爬绕过、混合模式采集从社区反馈和工具热度来看DrissionPage 近年增长很快。它的一个重要设计是同一个代码里既能用浏览器自动化模式去解决登录和动态渲染也能用类似 requests 的方式直接请求接口。这对于爬虫场景非常方便因为不需要在 Selenium 和 requests 之间来回切换。下面是一个 DrissionPage 抓取动态页面示例。注意不同版本的 API 可能略有差异请以官方文档为准。# 文件路径python-spider-demo/demo_dynamic.py from DrissionPage import ChromiumPage page ChromiumPage() page.get(https://quotes.toscrape.com/js/) page.wait.load_start() for item in page.eles(.quote): text item.ele(.text).text author item.ele(.author).text print(f{author}: {text[:40]}) page.quit()这段代码的逻辑是启动一个 Chromium 浏览器实例访问动态渲染页面等页面加载完成后用选择器提取名言和作者。相比 Selenium不需要手动下载和配置 driver对新手更友好。如果你更倾向 Playwright官方提供了很强的调试工具执行下面命令可以看到操作和页面状态pip install playwright playwright install chromium python -m playwright codegen https://quotes.toscrape.com/js/codegen会打开录制窗口你手动操作页面它自动生成代码是一个快速学习选择器的高效办法。6. 完整实战示例从列表页到详情页的数据采集流程前面两个例子分别处理了静态页面和动态页面。实际项目里通常还需要把数据从列表页带到详情页再加去重、更新逻辑。这里给一个更完整的工程项目结构示例。假设我们要采集某个公开书籍站点流程是先抓列表页得到每本书的详情页地址再访问详情页拿价格和库存。这个示例站点也是公开练习用的。推荐的项目结构python-spider-demo/ ├── main.py # 入口 ├── config.py # 配置项 ├── parse.py # 解析逻辑 ├── storage.py # 存储逻辑 └── requirements.txt # 依赖清单config.py示例# 文件路径python-spider-demo/config.py BASE_URL https://books.toscrape.com/ REQUEST_INTERVAL 1 # 每次请求间隔秒数避免对目标站点造成压力 OUTPUT_FILE books.csvparse.py示例# 文件路径python-spider-demo/parse.py from bs4 import BeautifulSoup def parse_book_list(html: str) - list[dict]: soup BeautifulSoup(html, html.parser) books [] for article in soup.select(article.product_pod): title article.select_one(h3 a).get(title) price article.select_one(.price_color).get_text(stripTrue) detail_url article.select_one(h3 a).get(href) books.append({ title: title, price: price, detail_url: detail_url, }) return booksmain.py示例# 文件路径python-spider-demo/main.py import time import csv import requests import config import parse def fetch_html(url: str) - str: resp requests.get(url, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.text def main(): books [] for page in range(1, 3): url f{config.BASE_URL}catalogue/page-{page}.html html fetch_html(url) books.extend(parse.parse_book_list(html)) print(f第 {page} 页累计 {len(books)} 条) time.sleep(config.REQUEST_INTERVAL) with open(config.OUTPUT_FILE, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[title, price, detail_url]) writer.writeheader() writer.writerows(books) print(f已完成写入 {config.OUTPUT_FILE}) if __name__ __main__: main()这个结构虽然简单但已经开始体现“工程化”思维配置独立出来改请求间隔不用改业务代码解析逻辑单独成文件后续增强选择器只改一个地方存储端先用 CSV后续换成数据库只需要改storage.py。运行pip install requests beautifulsoup4 python main.py跑完以后可以用浏览器或文本编辑器打开books.csv查看是否有正确数据。一个小技巧是先手动用浏览器打开目标页面F12 看一下 HTML 结构和这段代码的 CSS 选择器是否匹配。很多时候抓不到数据不是代码写错而是页面结构和你以为的不一致。7. 运行验证与失败排查先看日志再谈加功能很多人写完爬虫第一次运行发现没有输出第一反应是去改选择器。但更稳妥的顺序是先确认请求是否成功再确认解析是否命中最后才怀疑选择器。建议按这个顺序排查确认请求状态。打印resp.status_code如果不是 200先解决请求层问题。可能原因包括 User-Agent 被拦截、需要登录、目标站封了 IP。确认页面内容。打印resp.text[:500]看返回的是不是预期 HTML。有些站点会返回验证码页、安全页或 JSON 数据说明你的请求模拟成度不够。确认选择器命中。在 Python 里先解析并打印len(soup.select(...))如果为 0说明 CSS 选择器没有命中。这时候应该回到浏览器 F12 重新复制选择器。确认编码。如果输出中文乱码用resp.encoding resp.apparent_encoding重新指定编码或者在响应头里找 charset。确认频率限制。如果前几页正常后面开始超时或 403大概率是请求频率太高需要增加time.sleep。在第 5 节和第 6 节的代码里我已经用time.sleep(config.REQUEST_INTERVAL)做了基础限速。这里想强调爬虫的稳定性往往不是靠技巧而是靠对请求频率的控制。真正稳定运行的爬虫看起来都“不太快”。8. 常见问题与排查思路下面整理一份爬虫开发过程中最常见的 6 类问题按“现象 - 可能原因 - 排查方式 - 解决方案”给出。这份清单建议收藏后续遇坑可以对照。问题现象可能原因排查方式解决方案状态码 403服务端识别出非浏览器请求打印请求头服务端是否要求 UA设置合理 User-Agent或换浏览器自动化返回空数据数据由 JavaScript 动态加载查看返回 HTML 是否包含字段使用 Playwright / DrissionPage中文乱码编码判断错误查看响应头 charset设置resp.encoding resp.apparent_encoding爬取到一半超时请求频率过高被限流查看错误日志中的状态码增加 sleep 间隔使用代理池GitHub 项目下载慢网络环境问题查看下载速度使用镜像站或下载加速服务注意核验来源依赖版本冲突requests / lxml 等版本不兼容查看 pip 依赖树创建虚拟环境用 requirements.txt 锁定版本这里特别提醒一下“代理池”。很多爬虫教程会说“被封 IP 就上代理”但代理服务和代理池的搭建涉及额外的基础设施成本对新手来说并不是第一优先级。如果你的爬虫只是学习或小规模使用控制频率、添加随机等待、做好重试比盲目上代理更有效。9. 最佳实践与学习路线爬虫项目的工程化建议学爬虫的人很多但能把爬虫写好的人不多。差距往往不在“会不会用某个库”而在“有没有工程思维”。下面这几条经验来自长时间和爬虫项目打交道的通用实践适合所有想从“能跑”走到“能长期跑”的开发者。9.1 从最小项目开始再引入框架不建议一上来就搭 Scrapy 项目。先用 requests BeautifulSoup 跑通一个完整流程理解 HTTP 请求、解析、存储三个环节。然后当你发现需要处理去重、重试、并发、断点续爬时再引入 Scrapy 或 feapder。9.2 数据存储优先考虑 CSV 和 SQLite前期不要急着上 MySQL 或 PostgreSQL。CSV 适合快速查看和调试SQLite 适合单机、结构化查询。等你真正遇到数据量瓶颈、并发写库问题再升级数据库。这个顺序能让你把精力放在爬虫本身的逻辑上。9.3 日志是最好的调试工具不要把print当成唯一输出。做一个简单的日志文件记录每次请求的时间、URL、状态码、耗时。遇到问题先看日志而不是重新跑一遍。推荐用 Python 自带的logging模块不要额外引库。9.4 学会用浏览器 DevTools 看接口很多数据并不需要“爬 HTML”而是直接找到页面背后的 JSON 接口。打开浏览器 F12切换到 Network 面板刷新页面找到 XHR 请求往往能看到接口返回的干净 JSON。用 requests 直接请求这个接口比解析 HTML 高效得多。9.5 注意合法合规边界爬虫本身是中性技术但使用必须克制。建议始终遵守遵守目标网站的 robots.txt 和使用条款不采集非公开、需要登录才能访问且明确禁止的数据控制请求频率避免影响对方服务学习用途的数据不要用于商业贩卖或公开传播涉及个人信息的采集严格遵循最小必要原则。9.6 学习资源清单如果你想深入建议优先看这些官方文档它们是信息增量最大的来源requests 官方文档理解 HTTP 会话、重试、代理BeautifulSoup 官方文档掌握 CSS 选择器和遍历Scrapy 官方教程理解框架的 Pipeline、Middleware、SchedulerDrissionPage 官方文档掌握浏览器自动化与请求模式的混合使用Playwright 官方文档适合复杂交互和调试。练习站点可以使用quotes.toscrape.com、books.toscrape.com、httpbin.org这些公开站点专门用于学习比直接抓取商业站点更稳妥。9.7 增量采集的思路如果你的爬虫需要长期运行不要每次都全量跑。最简单的增量方案是记录上一次抓取的最大 ID 或时间戳下一次抓取只抓比它新的数据。如果做不到那就先抓全量再用数据库唯一约束去重。再进阶一点可以用布隆过滤器或 Redis Set 做 URL 去重但那是后来的事。9.8 关于 GitHub 开源项目的阅读方法选开源项目时不要只看 star 数。重点看这几个指标最近一次 commit 时间三年未更新的项目遇到新问题风险高Issue 活跃度有人维护、有人回复才值得深入文档完整度一份好的 README 包含安装、示例、常见问题License明确开源协议的项目更安全代码风格项目里的代码是否规范、是否容易二次开发。这也是为什么建议你“按需求找项目而不是按热度找项目”。GitHub 上的爆火项目往往营销做得好但不一定适合你的业务场景。爬虫这条路走到后面真正拉开差距的不是你会多少反爬技巧而是你能不能把采集任务拆解成可靠的、可维护的、合法合规的系统。先跑通最小示例再逐步加去重、日志、增量、调度。收藏了那么多开源项目下一步就是挑一个自己熟悉的场景动手写起来。