用 Scrapy Pipeline 做字段级过滤最稳妥,因其专为清洗、验证、去敏设计,运行于 Item 生成后、存储前,不干扰解析逻辑与中间件机制,且支持多 Pipeline 协作与精准正则匹配。
用 Scrapy Pipeline 做字段级过滤最稳妥
Scrapy 的
是专为清洗、验证、去敏设计的环节,它天然在
生成之后、存入数据库或文件之前运行,不会污染原始解析逻辑,也避免把过滤逻辑硬塞进
导致职责混乱。
常见错误是直接在
里用
跳过 yield,这会导致后续中间件、统计、重试等机制失效;更糟的是,如果多个 Pipeline 需要协作(比如先去敏再翻译),顺序错乱就全乱套。
必须在
中启用:
,数字越小优先级越高
方法里别直接修改
字段值(如
),而应抛出
或返回新
实例,否则可能引发不可见的引用污染
敏感词匹配前务必统一转小写 + 去首尾空格,否则
和
就会漏判
正则替换比简单 replace 更安全
用
处理敏感词,看似简单,但极易误伤:比如过滤
,结果把
、
全干掉了;而正则加词边界(
)能精准锚定独立单词。
实际场景中,招标信息
爬虫
常需过滤“中标”“废标”“围标”等词,但又不能影响“标书”“标准”“标识”——这时候不用
就是埋雷。
立即学习
“
Python免费学习笔记(深入)
”;
用
,
防止敏感词含正则元字符(如
、
)导致编译失败
不要用
简单处理大小写,而应在预处理时统一转小写,否则中文混英文场景下大小写逻辑会失控
性能上,100 个以内敏感词用正则没问题;超过 500 个建议换
,否则每次
编译开销明显
日志输出必须单独加 Filter,不能靠 Pipeline 拦截
很多人以为 Pipeline 过滤了
,日志里就不会出现敏感内容——错了。Scrapy 默认会在
级别把整个
dump 到日志,哪怕你 Drop 掉了,dump 动作早已发生。
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
下载
真实踩坑案例:某政务爬虫把身份证号字段进了 Item,Pipeline 正确 Drop,但日志文件里仍明文记录了带身份证的原始
字典,审计时直接暴雷。
必须自定义
子类,在
方法里检查
和
,对含
、
、
的日志条目返回
这个 Filter 要显式添加到每个
(
和
都得加),缺一个就漏一个
别试图用
然后指望 Filter 拦住——
在格式化前就已进入
,Filter 必须扫描
元组
BeautifulSoup 解析时用 CSS 伪类提前排除干扰节点
敏感信息常藏在特定 DOM 区域里,比如“联系方式”模块、“备注”字段、“用户评论”区块。与其等全部数据进 Pipeline 再筛,不如在
阶段就用 CSS 选择器把高危区域直接剔除。
典型例子:抓医生执业信息时,“线上问诊”“视频咨询”这类条目常和实体诊所共享
类名,若不前置过滤,后面清洗成本翻倍。
用
直接跳过评论区和联系方式区,减少无效解析
结合
伪类,如
,可快速排除含关键词的描述块
注意:
匹配的是元素自身文本(不含子元素),若敏感词在子标签里(如
),得改用
回溯父节点
真正难的不是写过滤逻辑,而是判断哪一层该过滤——是网络层(请求头伪装)、解析层(CSS 排除)、管道层(字段校验),还是日志层(消息拦截)。漏掉任何一层,都可能让敏感信息从缝隙里漏出去。
PipelineItemparse()parse()if 'politics' in descriptionsettings.pyITEM_PIPELINES = {'dirbot.pipelines.FilterWordsPipeline': 300}process_itemitemitem['description'] = item['description'].replace(...)DropItemItem'Politics ''politics'str.replace()'id''identity''child'r'\bid\b'\bre.sub(r'|'.join(map(re.escape, words)), '*', text)re.escape'c++''C#'flags=re.Iahocorasickre.subItemDEBUGItemItemlogging.Filterfilter()record.msgrecord.args'password''id_card''phone'FalseHandlerStreamHandlerFileHandlerlogger.debug("user: %s, pwd: %s", user, pwd)pwdrecord.argsargsparse().location-itemsoup.select('.content:not(.comment):not(.contact)'):-soup-contains().desc:not(:-soup-contains('身份证')):-soup-contains()密码find_all(text=re.compile(...))