欢迎光临
我们一直在努力

基于正则表达式集的数据格式合法性审计系统:从公开规整数据采集到脏数据入库拦截

㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~ ㊙️本期爬虫难度指数:⭐⭐⭐⭐⭐(专家级) 🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

    • 🌟 开篇语
    • 0️⃣ 前言(Preface)
    • 1️⃣ 摘要(Abstract)
    • 2️⃣ 背景与需求(Why)
      • 2.1 为什么要爬这些数据
      • 2.2 目标字段清单
      • 2.3 本文采用的实战目标
    • 3️⃣ 合规与注意事项
      • 3.1 关于 robots.txt
      • 3.2 控制访问频率
      • 3.3 不采集敏感信息
      • 3.4 正则审计不是事实校验
    • 4️⃣ 技术选型与整体流程(What/How)
      • 4.1 静态、动态、API 三种采集方式
      • 4.2 整体流程
      • 4.3 为什么选择 requests、BeautifulSoup、PyYAML、SQLite
    • 5️⃣ 环境准备与依赖安装
      • 5.1 Python 版本
      • 5.2 安装依赖
      • 5.3 推荐项目结构
    • 6️⃣ 核心实现:请求层(Fetcher)
      • 6.1 配置文件:config/crawler.yaml
      • 6.2 请求层代码:regex_audit/fetcher.py
    • 7️⃣ 核心实现:解析层(Parser)
      • 7.1 示例 HTML:examples/source_page.html
      • 7.2 数据模型:regex_audit/models.py
      • 7.3 清洗层:regex_audit/cleaner.py
      • 7.4 解析层代码:regex_audit/parser.py
    • 8️⃣ 数据存储与导出(Storage)
      • 8.1 字段映射表
        • raw_records
        • audit_records
        • quarantine_records
      • 8.2 去重策略
      • 8.3 存储层代码:regex_audit/storage.py
    • 9️⃣ 正则规则中心设计
      • 9.1 规则配置:config/regex_rules.yaml
      • 9.2 规则中心代码:regex_audit/rule_center.py
      • 9.3 动态下发规则的工程思路
    • 10️⃣ 审计器实现:字段级格式合法性判断
      • 10.1 审计器代码:regex_audit/auditor.py
      • 10.2 为什么使用 re.fullmatch
      • 10.3 格式审计的态度
    • 11️⃣ 工具函数与导出层
      • 11.1 配置读取:regex_audit/utils.py
      • 11.2 CSV 导出:regex_audit/exporter.py
    • 12️⃣ 命令行入口:main.py
    • 13️⃣ 运行方式与结果展示
      • 13.1 启动命令
      • 13.2 输出位置
      • 13.3 运行输出示例
      • 13.4 audit_result.csv 示例结果
      • 13.5 quarantine_result.csv 示例结果
    • 14️⃣ 完整 README 示例
    • 运行
    • 输出
    • 注意事项
      • 15.6 正则明明对,为什么匹配失败
      • 15.7 统一社会信用代码正则能否判断真实有效
    • 16️⃣ 进阶优化
      • 16.1 并发采集
      • 16.2 断点续跑
      • 16.3 日志与监控
      • 16.4 定时任务
      • 16.5 规则灰度
      • 16.6 多规则匹配
      • 16.7 校验位算法扩展
    • 17️⃣ 单元测试建议
      • 17.1 安装 pytest
      • 17.2 测试目录结构
      • 17.3 test_cleaner.py
      • 17.4 test_auditor.py
      • 17.5 test_parser.py
    • 18️⃣ 从 SQLite 迁移到 MySQL 的思路
      • 18.1 MySQL 表结构示例
      • 18.2 MySQL 版本存储层骨架
    • 19️⃣ 数据质量视角下的指标设计
      • 19.1 字段合规率
      • 19.2 记录合规率
      • 19.3 异常原因分布
      • 19.4 字段异常排行
      • 19.5 规则版本追溯
    • 20️⃣ 一个更接近生产的规则配置设计
    • 21️⃣ 关于“拦截脏数据入库”的边界
    • 22️⃣ 生产环境中的数据流设计
    • 23️⃣ 代码运行后的数据库检查
    • 24️⃣ 进一步封装为命令行工具
    • 25️⃣ Scrapy 版本迁移思路
    • 26️⃣ Playwright 适用场景
    • 27️⃣ 规则维护的现实经验
      • 27.1 不要把规则写死在代码里
    • 🌟 开篇语
  • 中文标点符号、全半角与空白字符全链路规范化过滤器:从公开文本采集到 ETL 清洗中间件落地
    • 0️⃣ 前言(Preface)
    • 1️⃣ 摘要(Abstract)
    • 2️⃣ 背景与需求(Why)
      • 2.1 为什么要做这类采集与清洗
      • 2.2 目标站点与目标字段
    • 3️⃣ 合规与注意事项(必写)
      • 3.1 robots.txt 基本说明
      • 3.2 频率控制,不做攻击式并发
      • 3.3 不采集敏感信息,不绕过付费或登录限制
    • 4️⃣ 技术选型与整体流程(What/How)
      • 4.1 本文属于哪种采集类型
      • 4.2 整体流程
      • 4.3 为什么选择 requests、bs4、lxml
    • 5️⃣ 环境准备与依赖安装(可复现)
      • 5.1 Python 版本
      • 5.2 依赖安装
      • 5.3 推荐项目结构
    • 6️⃣ 核心实现:请求层(Fetcher)
      • 6.1 config.py
      • 6.2 fetcher.py
    • 7️⃣ 核心实现:解析层(Parser)
      • 7.1 parser.py
      • 7.2 列表页如何拿详情链接
      • 7.3 详情页如何抽字段
      • 7.4 缺失字段怎么办
    • 8️⃣ 数据存储与导出(Storage)
      • 8.1 字段映射表
      • 8.2 去重策略
    • 9️⃣ 核心实现:清洗层(Normalizer)
      • 9.1 常见脏字符类型
        • 1. 零宽字符
        • 2. 全角字符
        • 3. 异常空白
        • 4. 繁简混用
      • 9.2 normalizer.py 完整实现
      • 9.3 关于 NFKC 与全半角转换
      • 9.4 关于中文标点规范化
      • 9.5 transform_rules_hit 的价值
    • 10️⃣ Storage 完整实现
      • 10.1 storage.py
    • 11️⃣ 主入口:串起采集、解析、清洗、存储
      • 11.1 main.py
    • 12️⃣ 本地演示:不依赖真实网站也能跑通
      • 12.1 demo_local.py
    • 13️⃣ 运行方式与结果展示(必写)
      • 13.1 如何启动
      • 13.2 输出在哪里
      • 13.3 示例结果展示
    • 🔟 常见问题与排错(强烈建议写)
      • 10.1 403 怎么办
      • 10.2 429 怎么办
      • 10.3 HTML 抓到空壳怎么办
      • 10.4 解析报错怎么办
      • 10.5 编码或乱码如何处理
      • 10.6 清洗后文本变奇怪怎么办
    • 1️⃣1️⃣ 进阶优化(可选但加分)
      • 11.1 并发优化
      • 11.2 asyncio 版本思路
      • 11.3 断点续跑
      • 11.4 日志与监控
      • 11.5 定时任务
      • 11.6 扩展为 Scrapy 中间件
      • 11.7 清洗规则配置化
      • 11.8 加入单元测试
    • 1️⃣2️⃣ 总结与延伸阅读
    • 🌟 文末
      • ✅ 专栏持续更新中|建议收藏 + 订阅
      • ✅ 互动征集
      • ✅ 免责声明
      • 27.2 不要迷信一个超长正则
    • 🌟 文末
      • ✅ 专栏持续更新中|建议收藏 + 订阅
      • ✅ 互动征集
      • ✅ 免责声明

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。 运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO 欢迎大家常来逛逛,一起学习,一起进步~🌟

  我长期专注 Python 爬虫工程化实战,主理专栏👉 《Python爬虫实战》:从采集策略到反爬对抗,从数据清洗到分布式调度,持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”,让数据价值真正做到——抓得到、洗得净、用得上。

  📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间:如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。    💕订阅后更新会优先推送,按目录学习更高效💯~

0️⃣ 前言(Preface)

这篇文章要做的事情很具体:用 Python 采集公开页面中的统一社会信用代码、邮编、车牌号等规整字段,通过一个“配置化正则规则中心”对字段格式进行合法性审计,最后把合规数据、异常数据和审计结果分别存入 SQLite,形成一套可复现、可扩展、可动态下发规则的数据质量拦截方案。

读完这篇文章,你大概能获得三件东西:

第一,你会知道如何把爬虫采集、字段解析、正则审计、数据入库这几个步骤拆成清晰的工程模块。

第二,你会得到一套真实可运行的 Python 示例项目,包含请求层、解析层、规则中心、审计器、存储层和命令行入口。

第三,你会理解“格式合法”与“业务真实”之间的边界:正则表达式能拦截大量脏数据,但它不是万能的,更不能替代后续的业务校验、权威库校验和人工复核。


1️⃣ 摘要(Abstract)

本文基于 Python、requests、BeautifulSoup、PyYAML 和 SQLite,构建一个面向统一社会信用代码、邮编、车牌号等公开规整数据的数据格式合法性审计系统,最终产出结构化审计表、合规数据表和异常隔离表。

读完之后,你可以掌握:

  • 如何从静态页面或接口中采集规整字段,并抽取为标准记录。
  • 如何使用 YAML 配置正则规则,实现字段校验规则的集中管理和动态更新。
  • 如何在数据入库前进行格式拦截,避免明显不合规的数据污染业务库。
  • 这篇文章不是为了追求“爬得多快”,而是为了把一个常见但经常被忽略的问题讲透:数据进入数据库之前,至少应该先过一遍格式合法性审计。


    2️⃣ 背景与需求(Why)

    做数据采集久了,大家会慢慢遇到一个不太显眼但很烦的问题:页面上的数据看起来很规整,字段也都像那么回事,可真正入库以后才发现,有些统一社会信用代码少了一位,有些邮编混进了中文字符,有些车牌号多了空格或符号,还有些字段根本是测试数据。

    如果这些数据只是临时分析,问题可能还不严重。可一旦它们进入正式数据仓库、风控系统、客户画像系统、企业信息聚合系统,后续清洗成本就会越来越高。更麻烦的是,很多异常字段不会立刻报错,而是悄悄躺在数据库里,等到报表统计、接口联调、模型训练或下游同步时才暴露出来。

    所以,这篇文章要解决的核心问题不是“怎么写一个能跑的爬虫”,而是:

    如何在公开规整数据采集之后、正式入库之前,建立一层可配置、可动态更新、可审计追溯的数据格式合法性拦截系统。

    2.1 为什么要爬这些数据

    这类数据常见于企业信息展示页、物流网点公开页、车辆服务公开样例页、地址信息页面、开放数据目录页面等。它们往往具有以下特点:

  • 字段格式相对固定。
  • 页面结构通常比较规整。
  • 数据质量对后续分析或系统使用影响很大。
  • 很多字段可以通过正则表达式完成初步合法性校验。
  • 例如:

    统一社会信用代码一般是 18 位,由数字和大写字母组成,通常不包含 I、O、Z、S、V 等易混淆字符。

    邮编一般是 6 位数字,且第一位不为 0。

    车牌号根据不同规则会有普通燃油车、新能源车、警用车、使领馆车等多种格式。本文为了工程演示,会采用一个适合常规场景的简化正则,不追求覆盖所有特殊车牌。

    2.2 目标字段清单

    本文设计的数据字段如下。

    字段名
    含义
    示例
    record_id 原始记录编号 R0001
    field_name 字段名称 unified_social_credit_code
    value 字段值 91350100M000100Y43
    regex_pattern 命中的正则规则 1{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$
    is_compliant 是否合规 1
    source_url 来源页面 https://example.com/public/list
    reason 审计说明 matched
    created_at 审计时间 2026-07-06 10:30:00

    为了便于演示,最终会落到 SQLite 中的三张表:

  • raw_records:保存采集后的原始记录。
  • audit_records:保存每个字段的格式审计结果。
  • quarantine_records:保存不合规字段,供后续人工复核或规则修正。
  • 2.3 本文采用的实战目标

    为了保证代码可以安全复现,本文不会绑定某一个真实商业平台,也不会演示绕过登录、绕过限制、批量刷取接口等行为。项目支持两种数据来源:

    第一种是本地示例 HTML 文件,用于完整跑通采集、解析、审计、入库流程。

    第二种是配置 URL 的公开静态页面,读者可以在遵守目标站点规则的前提下替换为自己的公开数据页面。

    我平时写这类小项目时,比较偏爱先做“本地可跑通版本”。原因很简单:爬虫项目出问题时,网络、站点结构、反爬策略、编码、解析选择器、字段规则都可能是变量。如果一上来就连真实站点,排错会很乱。先用本地 HTML 固定输入,确认流程和数据模型没问题,再替换数据源,效率会高很多。


    3️⃣ 合规与注意事项

    爬虫技术本身是中性的,但使用方式必须谨慎。尤其是涉及企业标识、地址、邮编、车辆号牌等字段时,更应该明确边界。本文的目标是技术学习和数据质量审计,不涉及敏感信息采集,不演示绕过登录、绕过付费、绕过访问限制的做法。

    3.1 关于 robots.txt

    robots.txt 是网站用来声明爬虫访问规则的文本文件。它通常位于站点根目录,例如:

    https://example.com/robots.txt

    在正式采集前,建议先查看目标站点的 robots.txt,确认相关路径是否允许抓取。虽然 robots.txt 不是访问控制系统,但它体现了站点对自动化访问的基本态度。作为技术人员,至少应该尊重这些规则。

    如果目标路径被明确禁止抓取,就不要继续抓取该路径。很多时候,换成站点提供的开放数据接口、公开下载文件、官方数据集或手动授权方式,会比硬爬页面更稳定,也更省心。

    3.2 控制访问频率

    本文示例代码会在请求层加入 timeout、重试和退避机制,但这不代表可以无限并发。一个负责任的爬虫应该做到:

  • 请求频率可控。
  • 失败后不要立刻疯狂重试。
  • 不要对同一页面进行无意义重复抓取。
  • 不要使用攻击式并发压测目标站点。
  • 对 429、503 等响应保持克制。
  • 对于公开页面的学习型采集,通常不需要高并发。很多时候,每秒 1 次甚至每几秒 1 次都足够了。真正的工程价值不在于“刷得快”,而在于“流程稳、字段准、入库干净、异常可追溯”。

    3.3 不采集敏感信息

    本文只讨论公开规整字段的格式合法性审计,不采集个人隐私信息,不抓取需要登录后才能访问的数据,不绕过权限限制,也不演示验证码破解、接口签名破解、付费内容绕过等行为。

    如果业务确实需要采集某些数据,应优先考虑以下方式:

  • 使用官方开放接口。
  • 使用经过授权的数据导出。
  • 使用企业内部数据同步。
  • 使用公开可下载数据集。
  • 与数据提供方达成明确的数据使用协议。
  • 3.4 正则审计不是事实校验

    这点很重要。正则表达式只能判断一个字段“长得像不像合法格式”,不能证明它一定真实存在。

    例如:

    91350100M000100Y43

    这个字符串可能符合统一社会信用代码的基础格式,但它是否真实存在,还需要权威企业信息库、工商数据源或业务系统进一步验证。

    同样,邮编 100000 符合 6 位数字格式,但它是否对应某个具体地址,也不是正则能解决的问题。

    所以本文系统的定位是:

    在数据入库前做第一道格式质量门禁,拦截明显不合规数据,降低后续清洗成本。


    4️⃣ 技术选型与整体流程(What/How)

    4.1 静态、动态、API 三种采集方式

    常见页面采集可以粗略分为三类。

    第一类是静态页面。页面 HTML 中已经包含目标字段,直接用 requests 获取页面,再用 BeautifulSoup、lxml 或 XPath 解析即可。

    第二类是动态页面。浏览器打开页面后,数据由 JavaScript 再次请求接口渲染出来。requests 抓到的 HTML 可能只是一个空壳。这种情况可以分析接口,也可以用 Playwright 这类浏览器自动化工具渲染后再解析。

    第三类是 API。数据直接以 JSON 形式返回,解析成本低,结构稳定性通常也更好。如果站点提供公开 API,并且允许调用,优先考虑 API。

    本文采用的是:

    静态 HTML 页面采集 + BeautifulSoup 解析 + YAML 正则规则中心 + SQLite 入库。

    为什么不一上来用 Scrapy 或 Playwright?

    因为本文重点不是复杂反爬,也不是大规模调度,而是“采集之后如何做格式审计和脏数据拦截”。requests + BeautifulSoup 足够清楚、依赖少、可读性好。等后续需要并发、队列、断点续跑、分布式调度时,再迁移到 Scrapy 或异步框架也不迟。

    4.2 整体流程

    系统流程可以概括为:

    #mermaid-svg-1ccuUBF4pHie15Bn{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1ccuUBF4pHie15Bn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1ccuUBF4pHie15Bn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1ccuUBF4pHie15Bn .error-icon{fill:#552222;}#mermaid-svg-1ccuUBF4pHie15Bn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1ccuUBF4pHie15Bn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1ccuUBF4pHie15Bn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1ccuUBF4pHie15Bn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1ccuUBF4pHie15Bn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1ccuUBF4pHie15Bn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1ccuUBF4pHie15Bn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1ccuUBF4pHie15Bn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1ccuUBF4pHie15Bn .marker.cross{stroke:#333333;}#mermaid-svg-1ccuUBF4pHie15Bn svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1ccuUBF4pHie15Bn p{margin:0;}#mermaid-svg-1ccuUBF4pHie15Bn .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-1ccuUBF4pHie15Bn .cluster-label text{fill:#333;}#mermaid-svg-1ccuUBF4pHie15Bn .cluster-label span{color:#333;}#mermaid-svg-1ccuUBF4pHie15Bn .cluster-label span p{background-color:transparent;}#mermaid-svg-1ccuUBF4pHie15Bn .label text,#mermaid-svg-1ccuUBF4pHie15Bn span{fill:#333;color:#333;}#mermaid-svg-1ccuUBF4pHie15Bn .node rect,#mermaid-svg-1ccuUBF4pHie15Bn .node circle,#mermaid-svg-1ccuUBF4pHie15Bn .node ellipse,#mermaid-svg-1ccuUBF4pHie15Bn .node polygon,#mermaid-svg-1ccuUBF4pHie15Bn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1ccuUBF4pHie15Bn .rough-node .label text,#mermaid-svg-1ccuUBF4pHie15Bn .node .label text,#mermaid-svg-1ccuUBF4pHie15Bn .image-shape .label,#mermaid-svg-1ccuUBF4pHie15Bn .icon-shape .label{text-anchor:middle;}#mermaid-svg-1ccuUBF4pHie15Bn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1ccuUBF4pHie15Bn .rough-node .label,#mermaid-svg-1ccuUBF4pHie15Bn .node .label,#mermaid-svg-1ccuUBF4pHie15Bn .image-shape .label,#mermaid-svg-1ccuUBF4pHie15Bn .icon-shape .label{text-align:center;}#mermaid-svg-1ccuUBF4pHie15Bn .node.clickable{cursor:pointer;}#mermaid-svg-1ccuUBF4pHie15Bn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1ccuUBF4pHie15Bn .arrowheadPath{fill:#333333;}#mermaid-svg-1ccuUBF4pHie15Bn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1ccuUBF4pHie15Bn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1ccuUBF4pHie15Bn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1ccuUBF4pHie15Bn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1ccuUBF4pHie15Bn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1ccuUBF4pHie15Bn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1ccuUBF4pHie15Bn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1ccuUBF4pHie15Bn .cluster text{fill:#333;}#mermaid-svg-1ccuUBF4pHie15Bn .cluster span{color:#333;}#mermaid-svg-1ccuUBF4pHie15Bn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1ccuUBF4pHie15Bn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1ccuUBF4pHie15Bn rect.text{fill:none;stroke-width:0;}#mermaid-svg-1ccuUBF4pHie15Bn .icon-shape,#mermaid-svg-1ccuUBF4pHie15Bn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1ccuUBF4pHie15Bn .icon-shape p,#mermaid-svg-1ccuUBF4pHie15Bn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1ccuUBF4pHie15Bn .icon-shape .label rect,#mermaid-svg-1ccuUBF4pHie15Bn .image-shape .label

    赞(0)
    未经允许不得转载:171主机测评 » 基于正则表达式集的数据格式合法性审计系统:从公开规整数据采集到脏数据入库拦截
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址