当前位置: 首页 > article >正文

从脚本到系统:设计一个支持插件、限流、重试与监控的 Python 异步爬虫框架

从脚本到系统设计一个支持插件、限流、重试与监控的 Python 异步爬虫框架很多人第一次写 Python 爬虫都是从几十行脚本开始的requests.get()、BeautifulSoup、for循环、保存 CSV。它很快也很有成就感。但真实项目往往不是“抓一个页面”这么简单而是目标站点很多、页面结构变化频繁、网络偶发失败、接口有访问频率限制、任务需要长期运行、线上出错要能追踪。这时问题就从“写一个爬虫”变成了“设计一个爬虫系统”。本文面向既想打牢 Python 编程基础、又希望进入 Python 实战与工程化阶段的读者。我们将设计一个支持插件、限流、重试、监控的异步爬虫系统用它回答一个高级 Python 工程师经常面对的问题如何把混乱的抓取需求组织成可扩展、可维护、可观测的工程体系。在技术选型上我们使用asyncio作为并发基础。Python 官方文档说明asyncio用async/await编写并发代码并且是多个异步框架的基础这非常适合网络 I/O 密集型场景。(Python documentation) HTTP 客户端可以选择aiohttp它的客户端请求支持异步上下文管理器适合构建资源安全的异步请求流程。(AIOHTTP)一、先定义边界一个工程化爬虫系统应该解决什么一个可上线的异步爬虫系统至少要回答六个问题1. URL 从哪里来 2. 请求如何并发执行 3. 不同网站如何解析 4. 失败如何重试 5. 访问频率如何控制 6. 系统状态如何监控如果把它画成流程图大致是这样Seed URLsSchedulerRate LimiterAsync FetcherRetry PolicyPlugin ParserPipeline StorageMetrics Monitor这个结构的核心思想是抓取、解析、存储、监控分离。不要把所有逻辑写进一个crawl.py否则三天后你自己都会害怕打开它。二、项目目录设计先让系统有秩序推荐目录如下async_crawler/ ├── crawler/ │ ├── core.py # 爬虫主流程 │ ├── fetcher.py # 异步请求 │ ├── limiter.py # 限流器 │ ├── retry.py # 重试策略 │ ├── plugin.py # 插件协议与加载 │ ├── pipeline.py # 数据处理与存储 │ └── metrics.py # 监控指标 ├── plugins/ │ ├── news.py │ └── product.py ├── tests/ │ └── test_parser.py └── main.py目录不是形式主义。好的目录结构会告诉后来者哪里放规则哪里放变化哪里是稳定核心。三、基础能力异步请求器 Fetcher爬虫的第一层能力是请求页面。同步爬虫一次只能等待一个网络响应而异步爬虫可以在等待 A 网站返回时同时调度 B、C、D 请求。# crawler/fetcher.pyimportaiohttpimportasynciofromdataclassesimportdataclassdataclassclassFetchResult:url:strstatus:inttext:strclassAsyncFetcher:def__init__(self,timeout:int10):self.timeoutaiohttp.ClientTimeout(totaltimeout)asyncdeffetch(self,session:aiohttp.ClientSession,url:str)-FetchResult:asyncwithsession.get(url,timeoutself.timeout)asresp:textawaitresp.text(errorsignore)returnFetchResult(urlurl,statusresp.status,texttext)这里用了async with它不只是语法优雅更重要的是能确保连接资源被正确释放。高级 Python 编程的一个重要习惯是资源生命周期要显式管理。四、限流设计别让并发变成攻击很多新手误以为异步爬虫越快越好这是危险的。成熟爬虫一定要有礼貌设置合理User-Agent、尊重目标站规则、遵守robots.txt、限制并发、避免对目标服务造成压力。一个简单的限流器可以基于asyncio.Semaphore实现# crawler/limiter.pyimportasynciofromcollectionsimportdefaultdictclassDomainRateLimiter:def__init__(self,per_domain_limit:int3,delay:float0.5):self._locksdefaultdict(lambda:asyncio.Semaphore(per_domain_limit))self._delaydelayasyncdefacquire(self,domain:str):semself._locks[domain]awaitsem.acquire()awaitasyncio.sleep(self._delay)returnsem使用时fromurllib.parseimporturlparseasyncdeflimited_fetch(fetcher,session,limiter,url):domainurlparse(url).netloc semawaitlimiter.acquire(domain)try:returnawaitfetcher.fetch(session,url)finally:sem.release()这段代码解决的是“每个域名最多同时几个请求”。真实项目中还可以加令牌桶、滑动窗口、分布式限流等方案。五、重试机制失败不可怕不可控才可怕网络请求天然不稳定。DNS 抖动、连接超时、服务端 502、503、504 都可能发生。高级工程师不会简单地except Exception: pass而是设计明确的重试策略。如果项目允许依赖第三方库可以使用 Tenacity。Tenacity 是一个通用 Python 重试库目标就是简化重试行为的添加。(Tenacity)当然我们也可以先实现一个轻量版本# crawler/retry.pyimportasyncioimportrandom RETRY_STATUS{429,500,502,503,504}classRetryPolicy:def__init__(self,max_attempts:int3,base_delay:float0.5):self.max_attemptsmax_attempts self.base_delaybase_delayasyncdefrun(self,coro_factory):last_errorNoneforattemptinrange(1,self.max_attempts1):try:resultawaitcoro_factory()ifresult.statusnotinRETRY_STATUS:returnresult last_errorRuntimeError(fretryable status:{result.status})exceptExceptionasexc:last_errorexc delayself.base_delay*(2**(attempt-1))jitterrandom.uniform(0,0.2)awaitasyncio.sleep(delayjitter)raiselast_error这里用了指数退避和随机抖动。它的价值在于失败后不要所有任务一起立刻重试否则可能造成“雪崩式重试”。六、插件系统让变化关在笼子里不同网站结构不同解析规则也不同。如果把所有解析逻辑写进主流程系统很快就会变成一锅粥。我们可以定义插件协议# crawler/plugin.pyfromtypingimportProtocol,IterablefromdataclassesimportdataclassdataclassclassItem:source:strtitle:strurl:strclassSpiderPlugin(Protocol):name:strdefmatch(self,url:str)-bool:...defparse(self,url:str,html:str)-Iterable[Item]:...新闻插件示例# plugins/news.pyfrombs4importBeautifulSoupfromcrawler.pluginimportItemclassNewsPlugin:namenewsdefmatch(self,url:str)-bool:returnnewsinurldefparse(self,url:str,html:str):soupBeautifulSoup(html,html.parser)forainsoup.select(a):titlea.get_text(stripTrue)hrefa.get(href)iftitleandhref:yieldItem(sourceself.name,titletitle,urlhref)插件管理器# crawler/plugin.pyclassPluginManager:def__init__(self,plugins):self.pluginspluginsdefselect(self,url:str):forplugininself.plugins:ifplugin.match(url):returnpluginreturnNone插件化的意义不是“看起来架构很高级”而是把变化隔离起来。目标站点结构变了只改对应插件不动核心调度器。七、数据管道不要边爬边乱存抓到数据后不建议直接在解析函数里写数据库。更好的方式是走统一 Pipeline# crawler/pipeline.pyimportjsonclassJsonLinePipeline:def__init__(self,path:str):self.pathpathasyncdefsave_many(self,items):withopen(self.path,a,encodingutf-8)asf:foriteminitems:f.write(json.dumps(item.__dict__,ensure_asciiFalse)\n)真实项目可以替换成 MySQL、PostgreSQL、MongoDB、Kafka 或对象存储。核心原则是解析只负责解析存储只负责存储。八、监控没有指标的系统等于盲飞异步爬虫跑起来之后你最关心的不是“它现在还活着吗”而是每分钟抓取多少页面 成功率是多少 失败最多的是哪些状态码 平均响应时间是多少 队列是否积压 插件解析失败率是多少Prometheus 是常见的监控方案它提供多维数据模型、PromQL 查询语言和告警能力。(prometheus.io) 在 Python 中可以设计如下指标层# crawler/metrics.pyfromcollectionsimportCounterimporttimeclassMetrics:def__init__(self):self.counterCounter()self.latencies[]definc(self,name:str):self.counter[name]1defobserve_latency(self,seconds:float):self.latencies.append(seconds)defreport(self):avgsum(self.latencies)/len(self.latencies)ifself.latencieselse0return{counts:dict(self.counter),avg_latency:round(avg,4),}请求时打点importtimeasyncdefmonitored_fetch(fetcher,session,retry_policy,metrics,url):starttime.perf_counter()try:resultawaitretry_policy.run(lambda:fetcher.fetch(session,url))metrics.inc(fstatus_{result.status})returnresultexceptException:metrics.inc(failed)raisefinally:metrics.observe_latency(time.perf_counter()-start)很多工程事故不是因为没人写代码而是系统出问题时没人知道问题在哪里。监控就是给系统装上仪表盘。九、主流程把组件组装成系统现在我们把 Fetcher、Limiter、Retry、Plugin、Pipeline、Metrics 组合起来# crawler/core.pyimportaiohttpimportasyncioclassAsyncCrawler:def__init__(self,urls,fetcher,limiter,retry_policy,plugins,pipeline,metrics):self.urlsurls self.fetcherfetcher self.limiterlimiter self.retry_policyretry_policy self.pluginsplugins self.pipelinepipeline self.metricsmetricsasyncdefcrawl_one(self,session,url):resultawaitlimited_fetch(self.fetcher,session,self.limiter,url)pluginself.plugins.select(url)ifnotplugin:self.metrics.inc(no_plugin)returnitemslist(plugin.parse(url,result.text))awaitself.pipeline.save_many(items)self.metrics.inc(page_done)self.metrics.inc(fitems_{len(items)})asyncdefrun(self):asyncwithaiohttp.ClientSession(headers{User-Agent:AsyncCrawlerBot/1.0})assession:tasks[self.crawl_one(session,url)forurlinself.urls]awaitasyncio.gather(*tasks,return_exceptionsTrue)print(self.metrics.report())入口文件# main.pyimportasynciofromcrawler.fetcherimportAsyncFetcherfromcrawler.limiterimportDomainRateLimiterfromcrawler.retryimportRetryPolicyfromcrawler.pluginimportPluginManagerfromcrawler.pipelineimportJsonLinePipelinefromcrawler.metricsimportMetricsfromcrawler.coreimportAsyncCrawlerfromplugins.newsimportNewsPlugin urls[https://example.com/news/1,https://example.com/news/2,]crawlerAsyncCrawler(urlsurls,fetcherAsyncFetcher(timeout10),limiterDomainRateLimiter(per_domain_limit2,delay1),retry_policyRetryPolicy(max_attempts3),pluginsPluginManager([NewsPlugin()]),pipelineJsonLinePipeline(items.jsonl),metricsMetrics(),)asyncio.run(crawler.run())这已经不是一个“脚本”而是一个具备工程骨架的小型系统。十、最佳实践清单让爬虫长期可维护1. 尊重规则与边界爬虫不是绕过限制的工具。正式项目应遵守目标网站服务条款、robots.txt、版权规则和访问频率要求。2. 限流优先于重试很多失败来自访问太猛。先降低压力再谈重试。3. 解析逻辑插件化每个站点一个插件每个插件有单元测试。页面结构变化时故障范围可控。4. 日志与指标分层日志回答“发生了什么”指标回答“整体是否健康”。5. 数据落盘使用 JSONLJSONL 一行一条记录适合追加写入、断点恢复和后续批处理。6. 给任务加唯一 IDURL、插件名、时间戳、重试次数都应该进入上下文便于排查问题。7. 测试解析器而不是测试网络单元测试中保存 HTML 样本测试parse()输出是否稳定。deftest_news_plugin_parse():htmla href/a标题 A/apluginNewsPlugin()itemslist(plugin.parse(https://example.com/news,html))assertlen(items)1assertitems[0].title标题 A十一、从入门到高级这套系统背后的 Python 能力这个项目几乎串起了 Python 教程中的核心知识能力在项目中的体现列表、字典、集合URL 队列、状态码统计、插件注册函数与异常请求封装、失败处理类与面向对象Fetcher、Limiter、Plugin、Pipeline装饰器与上下文管理器监控、资源释放生成器解析结果流式产出异步编程高并发网络 I/O模块化设计各组件职责分离单元测试保障插件稳定性这也是 Python 的魅力所在它既能让初学者快速写出第一个程序也能支撑复杂系统的工程化演进。十二、未来扩展从单机爬虫到平台化系统当业务增长后可以继续演进单机队列 - Redis/Kafka 分布式队列 JSONL 文件 - PostgreSQL/Elasticsearch/对象存储 简单 Metrics - Prometheus Grafana 手动运行 - Airflow/Prefect 定时调度 静态插件 - 动态插件加载与版本管理 本地部署 - Docker Kubernetes也可以引入 FastAPI 做任务管理接口或用 Streamlit 做内部监控面板。FastAPI 官方定位是基于标准 Python 类型提示构建现代、高性能 APIStreamlit 文档则强调可以用纯 Python 构建和分享数据应用非常适合内部数据工具与监控看板。(FastAPI)结语高级不是更快而是更稳一个初级爬虫解决“我能不能抓到”一个工程化爬虫解决“我能不能长期、稳定、可控、可追踪地抓到”。Python 高级工程师的价值不在于能不能写出最炫的异步代码而在于能不能把真实世界的不确定性收进系统设计里网络会失败所以有重试目标站有压力所以有限流规则会变化所以有插件线上会出问题所以有监控团队会协作所以有模块边界和测试。这就是 Python 实战最迷人的地方你写的不只是代码而是一套让混乱变得有秩序的能力。

相关文章:

从脚本到系统:设计一个支持插件、限流、重试与监控的 Python 异步爬虫框架

从脚本到系统:设计一个支持插件、限流、重试与监控的 Python 异步爬虫框架 很多人第一次写 Python 爬虫,都是从几十行脚本开始的:requests.get()、BeautifulSoup、for 循环、保存 CSV。它很快,也很有成就感。但真实项目往往不是“…...

ComfyUI-WD14-Tagger:AI智能图像标签提取的终极完整指南

ComfyUI-WD14-Tagger:AI智能图像标签提取的终极完整指南 【免费下载链接】ComfyUI-WD14-Tagger A ComfyUI extension allowing for the interrogation of booru tags from images. 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-WD14-Tagger 在AI图像…...

终极音乐解锁指南:3步让加密音乐在任何设备自由播放

终极音乐解锁指南:3步让加密音乐在任何设备自由播放 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地址: https:/…...

概率论:常见分布的期望与方差、中心极限定理、切比雪夫不等式

目录 一、0、1分布 二、二项分布 三、泊松分布 四、均匀分布​ 五、指数分布 六、正态分布 七、中心极限定理及其应用 (1)中心极限定理的定义 (2)使用示例 八、切比雪夫不等式 (1)切比雪夫不…...

3步掌握OpenSpeedy:免费开源游戏加速工具使用指南

3步掌握OpenSpeedy:免费开源游戏加速工具使用指南 【免费下载链接】OpenSpeedy 🎮 An open-source game speed modifier. 项目地址: https://gitcode.com/gh_mirrors/op/OpenSpeedy 你是否曾为游戏卡顿而烦恼?是否希望在单机游戏中加快…...

基于BLE模块的低功耗无线遥控器设计与实现

1. 项目概述:基于BLE模块的无线遥控器设计与实现几年前,我在捣鼓智能家居时,一直想找一个低功耗、响应快、又能自己完全掌控的无线遥控方案。市面上的成品要么协议封闭,要么功耗感人,要么延迟高得让人着急。后来&#…...

3步快速恢复加密压缩包密码:ArchivePasswordTestTool终极指南

3步快速恢复加密压缩包密码:ArchivePasswordTestTool终极指南 【免费下载链接】ArchivePasswordTestTool 利用7zip测试压缩包的功能 对加密压缩包进行自动化测试密码 项目地址: https://gitcode.com/gh_mirrors/ar/ArchivePasswordTestTool 面对遗忘的加密压…...

Diablo Edit2:3步掌握暗黑破坏神2存档修改的终极秘籍

Diablo Edit2:3步掌握暗黑破坏神2存档修改的终极秘籍 【免费下载链接】diablo_edit Diablo II Character editor. 项目地址: https://gitcode.com/gh_mirrors/di/diablo_edit 还在为暗黑破坏神2中刷装备的漫长过程感到疲惫吗?Diablo Edit2这款免费…...

地理空间机器学习库全解析:从TorchGeo到Raster Vision的实战指南

1. 项目概述:为什么我们需要专门的地理空间机器学习库?如果你尝试过用标准的PyTorch或TensorFlow去处理一张卫星影像,大概率会在第一步就卡住。不是模型写不出来,而是数据根本读不进去,或者读进去了却对不上位置。一张…...

为什么你的DeepSeek总漏检重构后代码?4步反混淆预处理法(附LLM辅助去装饰器Python脚本)

更多请点击: https://codechina.net 第一章:DeepSeek代码重复检测 DeepSeek-R1 模型在训练过程中引入了严格的代码去重机制,其核心目标是消除训练语料中语义等价或高度相似的代码片段,从而提升模型对真实编程模式的学习能力与泛化…...

内存申请和使用的场景分析(以AP->kernal->ISP为例)

在 ISP(Image Signal Processor)系统中,AP 与 ISP 之间的内存交互本质上是一个**“AP 申请可 DMA 访问的共享内存 → 内核建立映射 → 硬件寻址读写 → 同步与回收”**的过程。下面按数据流分层详细拆解。一、ISP 内存需求的特殊性 与普通应用…...

告别手动标注!用SAM(Segment Anything)和Python脚本,5分钟批量生成你的分割数据集

5分钟批量生成分割数据集:SAM自动化标注全流程实战 在计算机视觉领域,数据标注一直是制约模型开发效率的瓶颈。传统手工标注不仅耗时费力,还容易引入人为误差。Meta开源的Segment Anything Model(SAM)彻底改变了这一局…...

【国家级攻防演练级建议】:DeepSeek私有化部署中4类隐蔽后门植入路径与实时检测方案

更多请点击: https://kaifayun.com 第一章:DeepSeek私有化部署中隐蔽后门植入的攻防对抗本质 在私有化场景下,DeepSeek模型的部署链路常跨越镜像构建、权重加载、推理服务启动及API网关接入等多个环节。攻击者可利用构建上下文污染、依赖包劫…...

DeepSeek代码审查能力白皮书(2024企业级实测报告)

更多请点击: https://kaifayun.com 第一章:DeepSeek代码审查能力白皮书(2024企业级实测报告)概述 本报告基于2024年Q1至Q3期间,面向金融、电信与云原生三大垂直行业的17家头部企业客户开展的深度实测,覆盖…...

Lindy多步骤任务自动化落地全图谱(企业级架构师压箱底实践)

更多请点击: https://codechina.net 第一章:Lindy多步骤任务自动化落地全图谱(企业级架构师压箱底实践) Lindy效应在自动化系统设计中揭示了一个关键洞察:越久经考验的实践,其未来预期寿命越长。Lindy多步…...

Midjourney V6锐化失控?3步诊断+5组--sref/--stylize协同参数公式,立竿见影修复模糊与锯齿

更多请点击: https://intelliparadigm.com 第一章:Midjourney V6锐化失控的本质归因 Midjourney V6 引入的全新扩散架构与隐式细节增强机制,导致图像生成过程中高频纹理被过度强化,其根本原因并非参数误配,而是模型在…...

SpringBoot WebClient 介绍

目录一、什么是 WebClient?二、 WebClient 能解决什么问题?三、WebClient 和 RestTemplate 的区别四、WebClient 的核心优势1. 非阻塞(Non-Blocking)2. 支持异步3. 链式 API 更现代五、WebClient 的核心对象六、Mono 和 Flux 是什…...

仅限首批200位架构师获取:DeepSeek-DDD联合建模工作坊实录(含领域事件风暴原始会议录像+决策日志)

更多请点击: https://kaifayun.com 第一章:DeepSeek领域驱动设计的范式演进与本质洞察 DeepSeek作为面向大规模智能体协同与复杂业务语义建模的新一代AI原生架构,其领域驱动设计(DDD)实践已突破传统分层单体范式&…...

CI/CD流水线中的幽灵依赖——DeepSeek项目92%存在未声明的transitive risk,你中招了吗?

更多请点击: https://intelliparadigm.com 第一章:CI/CD流水线中的幽灵依赖——DeepSeek项目92%存在未声明的transitive risk,你中招了吗? 在现代CI/CD实践中,开发者常误以为 package.json 或 requirements.txt 中显式…...

如何在原神中解放双手:自动钓鱼、拾取与对话跳过的终极指南

如何在原神中解放双手:自动钓鱼、拾取与对话跳过的终极指南 【免费下载链接】genshin-impact-script 原神脚本,包含自动钓鱼、自动拾取、自动跳过对话等多项实用功能。A Genshin Impact script includes many useful features such as automatic fishing…...

NBT数据可视化编辑解决方案:NBTExplorer技术解析与应用指南

NBT数据可视化编辑解决方案:NBTExplorer技术解析与应用指南 【免费下载链接】NBTExplorer A graphical NBT editor for all Minecraft NBT data sources 项目地址: https://gitcode.com/gh_mirrors/nb/NBTExplorer NBTExplorer是一款面向Minecraft数据管理的…...

PrediPrune:机器学习驱动的编译器超级优化候选剪枝策略

1. 项目概述与核心挑战在编译器优化的世界里,我们总在追求极致的性能。传统的编译器优化器,比如LLVM的Pass,依赖于一系列预定义的、经过验证的转换规则。它们很高效,但想象力也受限于这些规则。超级优化器(Superoptimi…...

终极指南:5步掌握Cursor AI Pro完整功能免费解锁技巧

终极指南:5步掌握Cursor AI Pro完整功能免费解锁技巧 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached your tria…...

3分钟终极指南:用ncmdump轻松解密网易云音乐NCM格式文件

3分钟终极指南:用ncmdump轻松解密网易云音乐NCM格式文件 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump正是解决这个问题的神器&…...

Python到Android的魔法之旅:5步将你的代码变成移动应用

Python到Android的魔法之旅:5步将你的代码变成移动应用 【免费下载链接】python-for-android Turn your Python application into an Android APK 项目地址: https://gitcode.com/gh_mirrors/py/python-for-android 想象一下,你花了几个月时间精心…...

Safe Exam Browser虚拟机绕过实战:深度解析与安全研究指南

Safe Exam Browser虚拟机绕过实战:深度解析与安全研究指南 【免费下载链接】safe-exam-browser-bypass A VM and display detection bypass for SEB. 项目地址: https://gitcode.com/gh_mirrors/sa/safe-exam-browser-bypass 在数字化教育快速发展的今天&…...

免费解锁AMD Ryzen隐藏性能:SMUDebugTool终极指南

免费解锁AMD Ryzen隐藏性能:SMUDebugTool终极指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gitcod…...

从SIM800到BK A7670E:4G Cat.1模块硬件平替转接板设计全解析

1. 项目概述:从2G到4G的硬件平替升级 手头有个老项目,用的还是SIM800这种经典的2G模块,现在网络环境变了,2G退网是大势所趋,信号覆盖越来越差,项目得活下去,升级到4G成了刚需。但问题来了&#…...

5分钟掌握AutoClicker:Windows鼠标点击自动化的终极指南

5分钟掌握AutoClicker:Windows鼠标点击自动化的终极指南 【免费下载链接】AutoClicker AutoClicker is a useful simple tool for automating mouse clicks. 项目地址: https://gitcode.com/gh_mirrors/au/AutoClicker AutoClicker是一款专为Windows设计的鼠…...

如何用OpenHRMS打造企业级人力资源管理系统:30+模块完全指南

如何用OpenHRMS打造企业级人力资源管理系统:30模块完全指南 【免费下载链接】OpenHRMS 项目地址: https://gitcode.com/gh_mirrors/op/OpenHRMS 还在为繁琐的人力资源管理头疼吗?🤔 面对员工考勤、薪酬计算、绩效评估等复杂流程&…...