
本文介绍在数据爬取后、写入 mongodb 前,如何系统性地将含混杂单位(如 "$ 75 thousand"、"600 gm")的字符串字段标准化为统一单位(如 usd、grams),并解析为数值类型,强调预处理优先原则及可扩展的脚本化方案。
本文介绍在数据爬取后、写入 mongodb 前,如何系统性地将含混杂单位(如 "$ 75 thousand"、"600 gm")的字符串字段标准化为统一单位(如 usd、grams),并解析为数值类型,强调预处理优先原则及可扩展的脚本化方案。
在构建数据管道时,单位标准化必须发生在入库前——MongoDB 本身不提供字段级单位自动识别、转换或规范化功能。它是一个无模式文档数据库,仅负责存储原始值(如字符串 "$ 1.2 Lakh" 或 "15 Kgs"),不会执行语义解析或单位换算。因此,标准化逻辑必须由爬虫层或 ETL 预处理阶段承担。
核心策略:分步标准化 + 数值解析
标准化过程可分为两个关键阶段:
- 文本归一化(Normalization):清洗格式(空格、逗号、大小写)、统一单位符号(如 "Kg" → "kg"、"gm" → "g");
- 语义解析与换算(Parsing & Conversion):识别数量级(Thousand, Lakh, Kgs, mg)并转换为基准单位数值。
以下以 Python 风格伪代码 + 可落地的正则+映射方案为例(适用于 Norconex、Scrapy、自研爬虫等):
import re
# 单位映射表(按字段维度定义)
UNIT_CONVERSIONS = {
"amount": {
r'(?i)thousand': 1e3,
r'(?i)lakh': 1e5, # 1 Lakh = 100,000
r'(?i)million': 1e6,
r'(?i)crore': 1e7, # 印度常用,1 Crore = 10,000,000
r'[^\d.]': '', # 移除非数字字符(保留小数点和数字)
},
"weight": {
r'(?i)kg|kgs': 1e3, # kg → grams
r'(?i)g|gs|gram|grams': 1.0,
r'(?i)mg|milligrams': 1e-3,
r'(?i)ton|tonne': 1e6, # metric ton → grams
r'[^\d.]': '',
}
}
def standardize_field(value: str, field: str) -> float:
if not isinstance(value, str):
return 0.0
# 步骤1:提取纯数字部分(含小数点)
num_part = re.sub(r'[^\d.]', '', value)
if not num_part or '.' in num_part and len(num_part.split('.')[0]) > 10:
return 0.0
try:
base_value = float(num_part)
except ValueError:
return 0.0
# 步骤2:匹配并应用单位倍率
multiplier = 1.0
for pattern, factor in UNIT_CONVERSIONS.get(field, {}).items():
if re.search(pattern, value):
multiplier = factor
break
# 步骤3:返回标准化数值(例如:"$ 75 Thousand" → 75000.0)
return round(base_value * multiplier, 2)
# 使用示例
print(standardize_field("$ 75 Thousand", "amount")) # → 75000.0
print(standardize_field("600 gm", "weight")) # → 600.0
print(standardize_field("25 Kgs", "weight")) # → 25000.0
print(standardize_field("1.2 Lakh", "amount")) # → 120000.0
在 Norconex 爬虫中集成(推荐方案)
若使用 Norconex Web Crawler,ScriptTagger 是最灵活的选择,可直接嵌入上述逻辑(支持 Groovy/JavaScript):
<handler class="ScriptTagger"><script><![CDATA[
def amountStr = metadata.getString('amount')
if (amountStr) {
// 复用上面的解析逻辑(Groovy 兼容 Java 正则与数学运算)
def multiplier = 1.0
if (amountStr.toLowerCase().contains('thousand')) multiplier = 1000
else if (amountStr.toLowerCase().contains('lakh')) multiplier = 100000
// ... 其他规则
def num = amountStr.replaceAll(/[^0-9.]/, '') as Double
metadata.set('amount_normalized', num * multiplier)
metadata.set('amount_unit', 'USD') // 显式记录单位
}
def weightStr = metadata.getString('weight')
if (weightStr) {
def multiplier = 1.0
if (weightStr.toLowerCase().matches('.*kg.*')) multiplier = 1000
else if (weightStr.toLowerCase().matches('.*gm|g.*')) multiplier = 1
else if (weightStr.toLowerCase().matches('.*mg.*')) multiplier = 0.001
def num = weightStr.replaceAll(/[^0-9.]/, '') as Double
metadata.set('weight_grams', num * multiplier)
metadata.set('weight_unit', 'grams')
}
]]></script></handler>
✅ 关键优势:ScriptTagger 支持完整编程逻辑,可复用单位表、异常处理、日志输出,且修改不影响爬虫核心流程。
注意事项与最佳实践
- 永远保留原始字段:写入 MongoDB 时,同时保存 amount_raw: "$ 1.2 Lakh" 和 amount_usd: 120000.0,便于审计与纠错;
- 地域单位需显式配置:Lakh/Crore 主要在南亚使用,Thousand/Million 为国际通用,建议按目标市场动态加载单位词典;
- 避免浮点精度陷阱:对金额类字段,考虑使用整数分(cents)存储,或引入 Decimal 类型(Python)/ BigDecimal(Java);
- MongoDB 不替代 ETL:不要依赖 $convert 或聚合管道做实时单位转换——它无法理解 "Thousand" 的语义,仅能处理已知格式(如 "1234.56" → double);
- 测试覆盖边界情况:如 "15,000 gm"(含千位逗号)、"$ 75.5 Thousand"(带小数)、"N/A"(缺失值)需有 fallback 机制。
综上,单位标准化是数据质量的第一道闸门。通过在爬虫阶段嵌入轻量、可维护的脚本化解析器(如 Norconex 的 ScriptTagger),配合结构化单位映射表,即可高效、可靠地将异构字符串转化为统一数值,确保 MongoDB 中存储的是语义清晰、可计算、可分析的高质量数据。











