数据验证

简而言之:数据验证会根据预期的类型、格式、范围和完整性规则检查采集到的记录,从而在损坏的数据进入任何依赖它的系统之前将其发现。

数据验证是数据采集与使用之间的质量关卡。在网页数据管道中,它比在大多数系统中更加重要,因为数据源不受你控制,并且可能在毫无预警的情况下发生变化。网站重新设计不会产生错误,而是会生成一些结构看似正常、实际上却不包含任何有用信息的记录。

验证层级:

  1. 字段级:每个值的类型、格式和范围是否正确?价格应为正数,货币应使用有效的 ISO 代码。
  2. 记录级:各字段之间是否一致?高于标价的促销价单独来看有效,但组合起来却是错误的。
  3. 批次级:本次运行结果是否与之前的运行结果相似?记录数量突然下降或空值激增都表明存在问题,无论单条记录是否通过验证。
  4. 跨数据源:不同的独立数据源是否一致?如果某件商品在四个网站上的价格都是 40,而在第五个网站上是 4,这更可能是解析错误,而不是捡到了便宜货。

常见验证规则:

字段 规则 通常可发现的故障
price 正数,且低于合理上限 货币符号被解析为数字的一部分
currency 三个字母,且为有效的 ISO 4217 代码 区域设置检测失败
url 绝对 URL,使用 HTTP 或 HTTPS 协议 相对链接未解析
title 非空,长度在限制范围内 选择器匹配到了空容器
rating 介于 0 和评分上限之间 抓取到了百分比而不是星级评分
scraped_at 时间较近,且不能是未来时间 时区处理错误

实施字段验证:

from datetime import datetime, timezone

VALID_CURRENCIES = {"USD", "EUR", "GBP", "JPY", "ILS"}

def validate(record):
    errors = []

    title = record.get("title")
    if not title or not title.strip():
        errors.append("title_empty")

    price = record.get("price")
    if price is None:
        errors.append("price_missing")
    elif not isinstance(price, (int, float)):
        errors.append("price_not_numeric")
    elif price <= 0 or price > 1000000:
        errors.append("price_out_of_range")

    currency = record.get("currency")
    if currency not in VALID_CURRENCIES:
        errors.append("currency_invalid")

    url = record.get("url", "")
    if not url.startswith(("http://", "https://")):
        errors.append("url_not_absolute")

    ts = record.get("scraped_at")
    if ts and ts > datetime.now(timezone.utc):
        errors.append("timestamp_future")

    return errors

收集所有错误,而不是在遇到第一个错误时就返回,这一点非常重要。知道一条记录违反了四项规则,比只知道它违反了一项规则更有助于诊断,而且还能避免“修复一个、重新运行、再发现下一个”的循环。

批次验证可以发现记录验证遗漏的问题:

破坏性最大的故障,是那些每条记录单独看都有效的故障。

如果某个选择器失效,并在每个页面上都为价格返回 null,那么允许价格为 null 的 schema 会让每条记录都通过验证。系统不会抛出任何错误,只是数据集中最重要的那一列变成了空列,而问题要到几天后生成报告时才会暴露。

批次检查通过将本次运行与历史运行进行比较,而不是仅与规则进行比较,来发现这类问题。有效的信号包括与上一次运行相比的记录数量、每个字段的空值率、值的分布,以及发生变化的记录比例。某个字段的空值率从 2% 上升到 97%,显然意味着存在问题,而任何单条记录规则都无法将其标记出来。

决定如何处理验证失败:

只有在验证失败会触发明确操作时,验证才有用,而正确的操作会因严重程度而异。

拒绝记录。适用于必填字段不可用的情况。记录会被隔离而不是删除,以便后续检查和重新处理。

接受并添加标记。适用于可选字段验证失败的情况。记录会与质量标记一起存储,以便下游使用者在需要时进行筛选。

修复。适用于确定性的修复,例如删除多余空格或统一大小写。修复过程绝不能猜测缺失值。

停止处理该批次。适用于批次级检查失败的情况。发布一个 97% 的价格都为 null 的批次,比完全不发布更加糟糕。

验证与信任:

数据集的价值取决于使用者是否信任它。一份基于错误解析结果生成的定价报告,造成的长期损害比缺失一天的数据更严重,因为它会改变人们对之后每一份报告的看法。

因此,验证应在数据管道内部进行,而不是放在分析层。等到分析师发现结果看起来不对时,错误数据早已被查询、制成图表并加以引用。

Bright Data 的数据集和网页抓取工具 API会在交付前执行验证,因此收到的记录已经过完整性和类型一致性检查,无需在接收后再增加验证层。

准备好开始了吗?