Python后端爬虫专题18:进程退出码是0,职位却少了一半——中间件、AutoThrottle与统计排障
Python后端爬虫专题18进程退出码是0职位却少了一半——中间件、AutoThrottle与统计排障上一篇练习完整答案实际输出校验脚本可写成importjsonfrompathlibimportPath required{external_id,source_url,title,company,city,description,skills,salary_min,salary_max,salary_months,published_at,}rows[json.loads(line)forlineinPath(data/scrapy-jobs.jsonl).read_text(encodingutf-8).splitlines()]assertlen(rows)3assertall(set(row)requiredforrowinrows)assertall(isinstance(row[published_at],str)forrowinrows)assertlen({row[source_url]forrowinrows})3-o会追加到已有 Feed重复执行可能重复三行-O先覆盖适合生成代表当前运行结果的全量文件。持续事件日志可以追加但还需要 offset、事件 id 和消费者幂等。坏详情缺少h1时parse_detail抛PageParseError消息标明title让空标题继续会污染指纹、搜索和统计且以后难以区分“真的无标题”与“选择器失效”。值班现场为什么 finished 不是成功某次 Spider 日志最后写着Spider closed (finished)运维只看到退出码 0第二天产品发现职位从 3000 变成 1400。查看 Statsresponse_status_count/429820、retry/max_reached400、item_scraped_count1400。进程只是正常结束了调度生命周期业务目标并未达成。这类问题要沿中间件链排查。Downloader Middleware 在请求发出前和响应回来后工作适合代理之外的通用请求头、重试、缓存和统计Spider Middleware 位于 Engine 与 Spider 回调之间处理输入响应和回调输出。不要把字段解析塞进下载中间件否则页面业务规则会与传输机制耦合。JobSpider 的六项运行护栏ROBOTSTXT_OBEYTrue让 Scrapy 在访问前读取规则AUTOTHROTTLE_ENABLEDTrue根据延迟动态调整间隔CONCURRENT_REQUESTS_PER_DOMAIN2给同域设置并发上限DOWNLOAD_TIMEOUT10限制单请求占用RETRY_HTTP_CODES只包含可恢复的 429/5xxFEED_EXPORT_ENCODINGutf-8保证中文交付。运行独立检查点cd project.\.venv\Scripts\python.exe-m pytest tests\test_framework_adapters.py::test_scrapy_spider_enforces_polite_domain_runtime_settings-q测试的意义不是证明 Scrapy 支持这些键而是防止项目重构时误删自己的访问承诺。真正运行时还应查看 settings 优先级命令行-s可能覆盖 Spidercustom_settings部署配置要有审查。AutoThrottle 不是“自动越快越好”它根据响应延迟与目标并发调整下载延迟服务器变慢时放缓稳定时逐步恢复。它不会无视CONCURRENT_REQUESTS_PER_DOMAIN也不替你决定合法频率。Target concurrency 是目标而非硬保证非 200 响应不会促使提速但仍应对 429 遵从 Retry-After 和业务停止线。本地 TargetLab 很快AutoThrottle 可能迅速缩短延迟这不代表真实站点也能承受。课程可用AUTOTHROTTLE_DEBUGTrue观察每个 slot 的 latency、delay 和 concurrency但生产长期打开会产生很多日志。一张够用的运行核对表先看finish_reasonfinished、shutdown、closespider_* 含义不同再比较请求、响应与 item 数看 HTTP 状态分布特别是 301/302、401/403、429、5xx看 retry 次数与 max_reached看 downloader exceptions 中 timeout、DNS、TLS最后看 elapsed_time_seconds 和每分钟 item 吞吐。业务侧还要记录发现详情数、解析成功数、入库 created/updated/unchanged 和错误样本 URL。Scrapy 原生 Stats 不理解“职位更新”所以适配层或 Pipeline 应补业务计数。技术指标正常而业务发现数突然归零往往是选择器或登录页面问题。为什么不对所有状态都重试400 表示请求不符合合同重发相同内容仍错401/403 需要凭据或授权处理404 多数是资源已经删除429 与 5xx 才可能在退避后恢复。无限重试会拖住 slot、放大对方故障并让任务永不结束。重试耗尽必须进入报告不能被最后的 finished 掩盖。RetryMiddleware 与 AutoThrottle 也不是一回事前者决定失败响应是否再次调度后者控制请求节奏。若服务返回 Retry-After需要确认当前 Scrapy 版本/自定义中间件是否按预期处理不能只因为启用了 AutoThrottle 就假定严格等待该秒数。事故处理动作看到 429 激增先降低并发/频率并暂停扩容不换代理绕过看到大量 200 但 item 降低抽取快照比较 DOM看到 302 增加检查是否跳到登录或同意页面看到 403核对授权而非伪装浏览器看到单个 5xx有限重试并记录。处理后用固定 Fixture 回归再小流量恢复。本篇完整 Spider 配置完整模块的代码量不大但每个 setting 都对应上面的运行决策。实际团队可把跨 Spider 默认值放 settings.py把来源特例保留在 custom_settings并记录为什么特例存在。让 Scrapy 的调度能力复用 JobRadar 已验证的解析合同。fromcollections.abcimportIterablefromurllib.parseimporturlsplitimportscrapyfromscrapy.httpimportResponsefromjobradar.parsingimportparse_detail,parse_listingclassJobSpider(scrapy.Spider):采集 TargetLab 列表与详情item 字段与 JobItem 完全一致。namejobradar_jobscustom_settings{ROBOTSTXT_OBEY:True,AUTOTHROTTLE_ENABLED:True,CONCURRENT_REQUESTS_PER_DOMAIN:2,DOWNLOAD_TIMEOUT:10,RETRY_HTTP_CODES:[429,500,502,503,504],FEED_EXPORT_ENCODING:utf-8,}def__init__(self,seed_url:str,*args:object,**kwargs:object)-None:super().__init__(*args,**kwargs)partsurlsplit(seed_url)ifparts.schemenotin{http,https}ornotparts.hostname:raiseValueError(seed_url must be an absolute HTTP(S) URL)self.start_urls[seed_url]self.allowed_domains[parts.hostname]defparse(self,response:Response,**kwargs:object)-Iterable[scrapy.Request]:listingparse_listing(response.text,response.url)fordetail_urlinlisting.detail_urls:yieldscrapy.Request(detail_url,callbackself.parse_job)iflisting.next_url:yieldscrapy.Request(listing.next_url,callbackself.parse)defparse_job(self,response:Response)-Iterable[dict[str,object]]:itemparse_detail(response.text,response.url)yielditem.model_dump(modejson)本篇课后练习给出429300、retry/max_reached120、item_scraped800、finish_reasonfinished的完整故障判断与处置顺序。启动 TargetLab 后运行 Spider保存末尾 Stats找出请求数、200 数、item 数、耗时和 finish_reason。说明 Downloader Middleware 与 Item Pipeline 分别适合做什么并举一个放错层会导致的问题。下一篇会处理普通 HTTP 响应中根本没有职位 DOM 的动态页面。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →