55 lines
2.3 KiB
Markdown
55 lines
2.3 KiB
Markdown
# tmkit — PDF 数据脱敏工具库(后端管线 · 最小可用版)
|
||
|
||
面向法律/金融文书 PDF 的 PII(个人敏感信息)自动脱敏。纯 Python 库,六大模块中的 4 个核心模块 + 测试 + 文档已交付。
|
||
|
||
## 模块
|
||
|
||
| 模块 | 功能 |
|
||
|---|---|
|
||
| `tmkit.splitter` | PDF 分流:`get_text` 检测文字层,逐页返回 `native` / `scanned` |
|
||
| `tmkit.extractor` | 原生页按词抽取(`get_text('words')`,含 bbox 坐标);扫描页 `raise NotImplementedError('OCR 待接入 PaddleOCR')` |
|
||
| `tmkit.detector` | PII 识别引擎:身份证 18 位(GB 11643 校验位)、大陆手机号、银行卡(Luhn)、统一社会信用代码(GB 32100)、邮箱;支持自定义规则注册 |
|
||
| `tmkit.redactor` | 坐标回写:`add_redact_annot` + `apply_redactions`(默认,删字最干净)或 `draw_rect` 黑条;输出脱敏后 PDF bytes |
|
||
|
||
## 用法示例
|
||
|
||
```python
|
||
import fitz
|
||
from tmkit import default_detector, extract_page_words, redact_pdf
|
||
|
||
pdf_bytes = open("input.pdf", "rb").read()
|
||
det = default_detector()
|
||
hits = []
|
||
with fitz.open(stream=pdf_bytes, filetype="pdf") as doc:
|
||
for pno, page in enumerate(doc):
|
||
hits += det.scan_page_words(extract_page_words(page), pno)
|
||
# hits: [{"type": "id_card", "text": "1101...", "page": 0, "bbox": (x0,y0,x1,y1)}, ...]
|
||
|
||
clean = redact_pdf(pdf_bytes, hits) # method="redact"(默认,真删除)
|
||
open("output.pdf", "wb").write(clean)
|
||
```
|
||
|
||
自定义规则:
|
||
|
||
```python
|
||
from tmkit import PIIDetector, Rule
|
||
import re
|
||
det = PIIDetector()
|
||
det.register(Rule("order_no", re.compile(r"(\d{4})京\d+民初\d+号")))
|
||
```
|
||
|
||
## 测试
|
||
|
||
```
|
||
cd tmkit && python3 -m pytest tests/ -v
|
||
```
|
||
|
||
样例 PDF 由 reportlab 在 conftest.py 中动态生成:纯文本页、身份证+手机号+银行卡+邮箱页、空白扫描页。断言覆盖:脱敏后 `get_text` 中 PII 不存在、非敏感文本保留、页数不变、黑块像素可见、校验算法正确性、无 PII 页零误报。
|
||
|
||
## 已知限制(下一步)
|
||
|
||
- 扫描页 OCR(PaddleOCR)尚未接入,`extractor` 直接抛 `NotImplementedError`
|
||
- PII 跨行换行(如身份证分两行)不会命中;词拼接按行内进行
|
||
- `method="draw"` 只画黑条不删字,文本层仍可提取,仅作对比保留
|
||
- 未处理旋转页 / 加密 PDF
|