问题出现得很突然,日志倒是留得挺全。pdfplumber 抽出的文本里夹着 \u0000,写入 PostgreSQL 时报 invalid byte sequence for encoding UTF8: 0x00,整批 400 页全部回滚。
刚开始盯着「编码检测把 GBK 段落误判成 UTF-8」,折腾了半个多小时,排掉了。
排查方向其实有限,先把可能性排一排。PDF 字体映射产生空字符,附着在文本流里。另一种情况是断字连字符被替换成不可见字符。
另外,写入前没有做清洗和长度校验。
先统计文本里各类控制字符的数量
python -c "s=open('page.txt',encoding='utf-8').read();print({hex(ord(c)):s.count(c) for c in set(s) if ord(c)<32})"
入库前统一清洗控制字符
python -c "s=open('page.txt',encoding='utf-8').read();print(''.join(ch for ch in s if ord(ch)>=32 or ch in '\n\t'))"
接着用显式编码重新解码源文件
python -c "b=open('page.txt','rb').read();print(b.decode('utf-8','ignore')[:40])"
剩下的就是写库前加一条断言拦截空字符
python -c "s='abc';assert '\x00' not in s;print('clean')"
怎么算真的好了,看的是同一批现象。清洗后 400 页全部入库成功,文本里不再有 0x00,检索到的段落也没有乱码,回滚的批次重跑一次通过。
这里还有个坑。PostgreSQL 的 text 类型不接受空字符,这类脏数据要在解析层清掉,别留给数据库报错。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。
