TextIn 表格提取 Skill 使用指南
从 PDF / 图片 / 扫描件 / Office 文档中结构化抽取表格,输出生产可用的 Excel(含目录 + 每表一 Sheet + 冻结表头 + autofilter + 斑马纹 + 类型化数字),并可选同步输出 CSV × N + tables.json 供下游数据管道使用。
🚀 使用工作流(Agent 严格按此执行)
当用户提交 PDF/图片/Office 文档并要求"提取表格 / 转 Excel / 抽表格 / 导 xlsx / 表格结构化"时,Agent 按以下顺序执行:
Step A · 调用主脚本
python scripts/main.py --files "<用户上传的文件绝对路径>"
其他常见组合(按用户明示需求选择):
| 用户诉求 | 追加参数 |
|---|---|
| 加密 PDF | --pdf-password "<密码>" |
| 只处理某几页 | --page-range "1-10" 或 "1-2,5-8" |
| 多份文件 | --files a.pdf b.pdf c.pdf |
| 每份文件独立 xlsx | 追加 --excel-mode per-source |
| 同时输出 CSV / JSON | 追加 --emit-csv --emit-json |
| 只看结构不导出 | 追加 --dry-run |
| 关闭负数保护(财报常用) | 参数 --protect-negatives 不加即为默认关闭 |
Step B · 处理执行结果(按 exit code)
| exit code | 含义 | 应该做的 |
|---|---|---|
| 0 | 全部成功 | 从 stderr 日志或 processing_summary.json 里取 xlsx 路径,用平台的文件下发工具把 xlsx 交给用户;简短告诉用户"抽取到 X 张表,已导出" |
| 2 | 部分文件失败 | 先把成功的 xlsx 给用户,然后总结失败原因(读 processing_summary.json.failed[]) |
| 1 或 3 | 未运行 / 全部失败 | 看 stderr 里的 tag 分类修复(见 Step C) |
Step C · 错误自愈路径
先看 stderr 里的错误 tag,不同 tag 对应不同修复策略:
| stderr 里出现 | 含义 | 立刻执行的修复动作 |
|---|---|---|
[MISSING_DEPS] |
沙箱缺 Python 依赖 | 按 stderr 里给的命令跑 pip install ...,然后重跑 Step A |
无可用引擎 |
既没 xparse-cli 也没 API 密钥 | 尝试装 CLI:Linux curl -fsSL https://dllf.intsig.net/download/2026/Solution/xparse-cli/install.sh \| bash;Windows irm https://dllf.intsig.net/download/2026/Solution/xparse-cli/install.ps1 \| iex;成功后重跑 Step A |
paid API requires credentials |
CLI 检测到用户想走 paid 但没 auth | 用 --engine api 重跑(前提是环境变量有 TEXTIN_APP_ID);否则明确告诉用户先去 textin.com 拿密钥 |
TextIn code=40423 |
PDF 密码错或需要密码 | 问用户要密码,加 --pdf-password "<pwd>" 重跑 |
TextIn code=40302 |
文件 > 500MB | 直接告诉用户"文件太大,超过 500MB 硬限",建议先拆分 PDF |
TextIn code=40422 |
文件损坏 | 让用户重新导出 PDF 后再试 |
TextIn code=40003 |
余额不足 | 让用户到 TextIn 控制台 充值 |
TextIn code=40307 |
免费额度用完 | 建议改用 --engine api(付费)或次日再试 |
| 其他错误 | 未预期 | 完整把 stderr 转发给用户,不要臆测 |
Step D · 返回给用户什么
- 成功:文件路径 + 简短统计(几张表、多少数字被类型化、多少汇总行、多少张图片)
- 失败:具体错误 + 需要用户配合的动作(提供密码 / 注册密钥 / 拆分文件)
- 不要:编造表格内容 / 声称成功但没跑脚本 / 不看 processing_summary.json 就总结
⚙️ 快速开始
Skill 有 三种引擎,默认 --engine auto 会按下面优先级自动选择:
1. 本机已安装 xparse-cli → 用 CLI(免登录,每日 1000 页免费额度)
2. 环境变量或凭据文件里有密钥 → 用 TextIn HTTP API(付费额度)
3. 都没有 → 尝试自动安装 xparse-cli;失败则报错
🟢 路径 A · 零门槛(推荐首次使用)· 免费 1000 页/天
无需注册、无需密钥,一行命令装完 CLI 就能跑:
# Linux / macOS
source <(curl -fsSL https://dllf.intsig.net/download/2026/Solution/xparse-cli/install.sh)
# Windows PowerShell
irm https://dllf.intsig.net/download/2026/Solution/xparse-cli/install.ps1 | iex
装完之后:
python scripts/main.py --files 财报.pdf
Skill 会自动检测到 xparse-cli 并使用它。默认 Skill 也会尝试自动装 CLI,若你不希望它自动装可加 --no-auto-install-cli。
🟡 路径 B · 付费额度(大文档 / 高级功能)· TextIn API
如果你需要处理 > 1000 页/天的文档、或需要更完整的能力开关,走 HTTP API 模式:
- 打开 https://www.textin.com/ 注册账号
- 进入 控制台 → 账号与开发者信息
- 复制
x-ti-app-id和x-ti-secret-code - 配置(二选一):
方式 1 · 环境变量:
export TEXTIN_APP_ID=<your app id>
export TEXTIN_SECRET_CODE=<your secret code>
方式 2 · 凭据文件:
// /run/secrets/textin.json
{"app_id": "...", "secret_code": "..."}
调用时加 --credentials-file /run/secrets/textin.json --engine api。
🔧 强制引擎
python scripts/main.py --files a.pdf --engine cli # 强制 CLI
python scripts/main.py --files a.pdf --engine api # 强制 API
python scripts/main.py --files a.pdf --engine auto # 自动(默认)
python scripts/main.py --files a.pdf --no-auto-install-cli # 禁止自动装 CLI
首次调用
# 最简一行
python scripts/main.py --files 财报.pdf
# 只看结构不导出,人工确认
python scripts/main.py --files 财报.pdf --dry-run
🎯 场景路由表
先判断用户的意图属于以下 3 大类中的哪一类,再选对应的调用组合。
用户意图
├─ 文档 → Excel(生产可用) → 1️⃣ 主流程:xlsx 主输出
├─ 文档 → 数据管道(CSV/JSON) → 2️⃣ 数据管道:--emit-csv --emit-json
└─ 只看识别结构不导出 → 3️⃣ Dry-run 预览:--dry-run
1️⃣ 主流程:Excel 输出
| 用户意图 / 关键词 | 命令 |
|---|---|
| 单份 PDF 抽表 | python scripts/main.py --files a.pdf |
| 多份 PDF 汇总到一个 xlsx | python scripts/main.py --files a.pdf b.pdf c.pdf |
| 多份 PDF 各自独立 xlsx | --excel-mode per-source |
| 所有表堆到一个 Sheet | --excel-mode single-flat |
| 加密 PDF | --pdf-password <密码> |
| 只处理某几页 | --page-range 1-10,15-20 |
| 指定输出目录 / 主文件名 | --output-dir /out --output-name 财报三表 |
2️⃣ 数据管道:CSV / JSON
| 场景 | 命令 |
|---|---|
| 每张表一份 CSV(Excel BOM,UTF-8) | --emit-csv |
| 所有表汇总到 tables.json | --emit-json |
| 只要纯数据无样式(关闭 xlsx 样式) | --no-freeze-header --no-zebra-stripe --no-autofilter |
3️⃣ Dry-run 预览
python scripts/main.py --files a.pdf --dry-run
输出:每张表规模、表头行数、合并后的表头字符串、汇总行位置、跨页信息。不产生 xlsx,不消耗 TextIn 计费的写盘成本(但仍会调 TextIn 解析一次)。
📎 深度定制(可选)
| 场景 | 参数 |
|---|---|
| 用规则文件定制列类型 / 空值 / 样式 | --rules references/rules.example.json |
| 强制表头行数 | --header-rows 2 / --header-rows none |
| 关闭多行表头合并 | --no-merge-multi-header |
| 关闭空行剔除 | --no-drop-empty-rows |
| 剔除空列 | --drop-empty-cols |
| 不识别汇总行 | --no-detect-summary |
| 会计负数格式 | --accounting-style dash 或 paren |
| 负数保护开关 | --protect-negatives |
| 图片下载策略 | --images-download auto/always/never |
🧠 智能 parse_mode(v1.1.1 新增)
TextIn 有 5 种解析模式,Skill 默认走 smart 自动选择:
| 页数 | 选择 | 理由 |
|---|---|---|
| < 100 页 | vlm(视觉语言模型) |
精度最高,适合关键报告 / 合同 / 精细表格 |
| >= 100 页 | scan(全当图片) |
速度快,避免 vlm 长文档超时 |
如何工作:Skill 优先用本地 pypdf 零成本读取 PDF 页数(沙箱预装);非 PDF 或 pypdf 不可用时自动回落到 vlm。
覆盖:
python scripts/main.py --files a.pdf --parse-mode vlm # 强制 vlm
python scripts/main.py --files a.pdf --parse-mode scan # 强制 scan
python scripts/main.py --files a.pdf --parse-mode-threshold 50 # 改阈值到 50 页
📁 目录结构
textin-table-extractor/
├── SKILL.md # 入口(本文件)
├── README.md # 项目概述
├── CHANGELOG.md # 版本变更
├── scripts/
│ ├── main.py # 编排入口
│ ├── textin_client.py # TextIn API 封装(含 smart parse_mode + 分段 + 重试)
│ ├── table_builder.py # 表格建模 + 跨页三段决策 + 空行剔除 + 汇总行识别
│ ├── header_detector.py # 多行表头信号打分探测 + 合并为单行
│ ├── cell_normalizer.py # 单元格清洗 + 8 种类型识别(日期/百分比/会计负数/...)
│ ├── postprocess_rules.py # 规则 JSON 加载 + 列类型解析(索引 / 表头正则)
│ ├── xlsx_exporter.py # openpyxl 导出:4 种 mode + 样式增强 + 目录超链接
│ ├── csv_json_exporter.py # CSV × N + tables.json 导出
│ ├── image_handler.py # 图片下载(多文件累计)+ image_index.json
│ ├── file_utils.py # 输入校验 + 输出目录 + 命名去重
│ └── validators.py # 验收报告
├── references/
│ ├── rules-guide.md # 后处理规则完整指南 ⭐
│ ├── rules.example.json # 可直接用的规则模板 ⭐
│ ├── crosspage-heuristics.md # 跨页合并算法细节
│ ├── design-decisions.md # 关键决策记录(为什么不用 exports.xlsx 等)
│ └── error-handling.md # TextIn 错误码 + Skill 处理策略
└── tests/
└── test_cases.md # 验收样本清单
> 依赖:本 Skill 依赖沙箱预装的 requests / openpyxl / pypdf 三个包(pypdf 可选,用于本地探测 PDF 页数以支持 smart parse_mode;缺失时 smart 自动回落到 vlm)。若运行时提示缺包,请联系平台管理员将对应依赖加入沙箱白名单。
📦 支持范围
| 项 | 说明 |
|---|---|
| 输入格式 | PDF、JPG/JPEG/PNG/BMP/GIF/WEBP/TIFF、DOC/DOCX、XLS/XLSX、PPT/PPTX、HTML、TXT、MD |
| 单文件大小 | ≤ 500 MB(TextIn 同步接口硬限) |
| 单次页数 | 免费用户 ≤ 100 页 / 付费 ≤ 500 页;超页自动分段调用 |
| 单批文件数 | 建议 ≤ 50 份(受 QPS 与调用时长影响) |
| 扫描件 | ✅ 支持(parse_mode=auto / scan,必要时切边矫正) |
| 加密 PDF | ✅ 支持(--pdf-password 或 document.password) |
| 水印文档 | 默认保留水印;--rules 里 remove_watermark: true 可开启去水印 |
不支持时的行为:文件格式不支持 / 空文件 / 超大文件 → 该文件单独记入失败列表,其他文件继续处理。
📄 输出
在 --output-dir 或首个输入文件同级的 textin_tables_<YYYYMMDD-HHMMSS>/ 下生成:
textin_tables_20260807-153022/
├── 表格提取结果.xlsx # ★ 主产物:目录 Sheet + 每张表一个 Sheet
├── csv/ # 可选 (--emit-csv):每张表一个 CSV
│ ├── sample_T1.csv
│ └── ...
├── tables.json # 可选 (--emit-json):所有表汇总,供下游程序
├── image_index.json # 图片元信息(供 Agent 传给多模态解读)
├── images/ # 下载的图片(印章 / 二维码 / 图表)
│ ├── img_p2_01_stamp.jpg
│ └── ...
└── processing_summary.json # 处理摘要(成功/失败/统计/验收报告)
🔧 关键专业特性
- 智能 parse_mode:本地 pypdf 探测页数 → < 100 vlm / >= 100 scan
- 跨页三段决策:引擎标记
is_continuation/ 启发式(列数一致 + 表头重复 / 贴页顶)/ 疑似(caption 不同就分表) - 多行表头:合并单元格 + 短文本 + 无数字 + 下一行是数据的信号打分探测
- 列类型识别:日期(8 种格式)/ 百分比 / 会计负数(
(1,234)/△1,234→ 数值 + 中式-1,234)/ 千位数字 / 化学式 / 前导零 / 长数字 - 空值识别:
-/N/A/无//等 12 种默认标记,可扩展 - 汇总行标注:合计 / 小计 / 总计 / Total 加粗 + 顶部粗边框
- 公式注入防护:
= + @前置';-默认不保护(因为财报里负数常态) - 合并单元格越界保护:与已合并区冲突时仅写文本 + warning,不强合并
- 单文件失败隔离:任一文件失败不影响其余文件,全部纳入
failed - 图片提取:印章 / 二维码 / 图表下载到
images/,Skill 不解读图片内容,交给 Agent 层的多模态
🚦 退出码
| 码 | 含义 |
|---|---|
| 0 | 全部文件成功 |
| 1 | 参数错误 / 密钥缺失 / 无有效输入 |
| 2 | 部分文件失败(xlsx 已生成,覆盖不全) |
| 3 | 全部文件失败(未生成 xlsx) |
🛠️ 问题定位指南
常见错误码(来自 TextIn)
| 错误码 | 说明 | 解决方案 |
|---|---|---|
| 40101 / 40102 | 鉴权失败(app-id / secret 无效) | 确认环境变量 TEXTIN_APP_ID / TEXTIN_SECRET_CODE 是否正确;到 控制台 复核 |
| 40103 | IP 不在白名单 | 到 TextIn 控制台把调用方 IP 加入白名单 |
| 40003 | 余额不足 | 到 TextIn 充值页 充值 |
| 40307 | 今日免费额度已用完 | 次日再试或升级付费 |
| 40306 | QPS 超限 | Skill 已内置指数退避重试,通常自动恢复 |
| 40422 | PDF 文件损坏 | 尝试用其他工具打开 PDF 并重新导出,确认无损 |
| 40423 | PDF 密码错误 | 用 --pdf-password <password> 传入密码 |
| 40424 | 页数超过 500 | 用 --page-range 1-500 分批调用,或改用异步接口(暂不支持) |
| 50207 | 部分页面解析失败 | 已解析的部分继续处理;processing_summary.json.diagnostics 会标注失败页 |
排查步骤
- 看
processing_summary.json:里面有failed、diagnostics、validation三个关键字段 - 确认密钥:
echo %TEXTIN_APP_ID%/echo $TEXTIN_APP_ID确认非空 - 先跑 dry-run:
--dry-run只看结构,快速验证 TextIn 调用是否通 - 看图片下载:
image_index.json里download_error字段能定位单张图失败原因
📚 深度参考
- references/rules-guide.md — 后处理规则完整指南(列类型 / 空值 / 样式 / 汇总行等)
- references/rules.example.json — 一份可直接用的规则模板
- references/crosspage-heuristics.md — 跨页合并算法细节
- references/design-decisions.md — 为什么不用 TextIn
exports.xlsx等专业决策 - references/error-handling.md — 完整错误码与处理策略
- tests/test_cases.md — 验收样本清单
🔒 安全承诺
- 密钥仅从环境变量或
--credentials-file读取,不写入任何日志、错误信息、输出文件 - 只读取用户显式传入的文件路径,不扫描其他目录
- 不修改、不覆盖原文件;输出目录带时间戳,同名文件自动追加序号
- 单元格文本以英文单引号前置的方式做公式注入防护,避免 Excel 打开时执行恶意公式