PDF解析神器测评:1.5秒100页,支持公式表格免费
处理图片、PDF、Word文档、网页等不同格式的文件,核心目标是快速精准提取关键信息。这看似简单,实际上耗时且繁琐。
PDF凭借便携性与格式稳性成为知识分享的主流载体,但其复杂与多样同样棘手——简单复制粘贴远无法满足精细信息提取需求。面对排版密集的学术论文、含大量表格与图表的企业报告,想要高质量结构化解析,必须借助更智能、更专业的工具。
坦白说,市面上开源工具不少,但许多用户反映上手门槛偏高,本地部署时频繁报错。今天介绍的,是合合信息旗下TextIn智能文档处理平台中的通用文档解析服务。它支持将图片、PDF等多种格式文件直接解析为Markdown或JSON文档。关键是输出格式对大型语言模型(LLM)非常友好,开箱即用。
该服务在PC端即可直接使用,新用户注册自动赠送1000页免费额度,日常处理需求基本覆盖。
产品优势
相比其他文档解析方案,TextIn几个核心亮点十分突出:
- 文件更大:单份文档最大支持500MB,无需预先拆分大文件。
- 页数更多:单次转换最多处理1000页,批量处理效率显著。
- 速度更快:解析100页PDF文档,最快仅需1.5秒。体验后便难以满足于缓慢工具。
产品特点
- 支持多种扫描内容:清晰扫描件、手机拍摄照片、杂乱截屏均可稳定处理。
- 支持多种语言:涵盖简体中文、繁体中文、英文、数字,以及西欧、东欧主流语言,总计超过50种。
- 表格识别效果好:有线表、无线表、密集表均能准确识别,合并单元格等复杂结构也能完整还原。
- 阅读顺序还原准:精准理解并还原文档真实结构与元素排列顺序。对多栏布局的论文、年报、业务报告等内容,此功能尤为关键。
功能演示
表格识别
- 竖向表格识别
- 复杂表格识别
阅读顺序还原
公式识别
古代状元殿试试卷识别
2024 高考数学试卷识别
应用场景
在AI领域,通用文档解析服务的应用场景极为广泛,常与其他AI技术结合,实现更高级的自动化与智能化。从实际落地来看,以下是几个最典型的集成方向:
- 自然语言处理(NLP):作为NLP任务的前置步骤,负责从各类文档中提取文本,供后续语义分析、情感分析或机器翻译使用。
- 大模型训练语料处理:在训练大模型前,识别各类文档内容,输出JSON或Markdown这类对大模型友好的数据格式。高质量解析结果可大幅减少人工纠错数据的时间,加速模型训练节奏。
- 智能问答系统:构建基于大模型的智能问答系统时,解析不同类型知识库内容——无论企业内部文档库还是公开文章报告。将解析后的文档内容喂给问答系统,能让大模型在生成答案时“言之有物”,有效抑制幻觉,提升回答质量。
- 智能摘要:从不同文档中提取完整信息,结合NLP技术生成摘要,大幅节省阅读时间。
随着技术持续演进,通用文档解析器在AI领域的应用范围仍在扩大。它在提升数据处理能力、促进知识发现与支持决策制定方面的作用将越来越重要。
快速接入
除了直接在TextIn平台使用,还可通过API方式将服务集成到自己的应用中。TextIn平台对开发者十分友好,提供多种语言的开箱即用示例。
借助这些示例,我们能快速上手,开发一个本地可用的AI通用文档解析CLI工具。
前置条件
确保本地环境已安装Node.js,版本大于v18.0.0。安装完成后,通过以下命令查看版本:
node -v
开发步骤
- 新建TextIn项目
- 安装项目所需依赖
npm install commander dotenv -S
# or
pnpm install commander dotenv -S
- dotenv:用于读取本地的
.env文件; - commander:用于开发Node.js CLI工具。
- 打开TextIn项目,在根目录下新增
textin.js文件
#!/usr/bin/env node
const fs = require("fs").promises;
const path = require("path");
const dotenv = require("dotenv");
const { program } = require("commander");
dotenv.config();
// Config subcommand
program
.command("config")
.description("Set appId and secretCode")
.option("-a, --appId ", "Set the appId")
.option("-s, --secretCode ", "Set the secretCode")
.action(async (options) => {
try {
await updateEnvFile(options.appId, options.secretCode);
} catch (error) {
console.error("Error updating configuration:", error.message);
process.exit(1);
}
});
// Main command
program
.version("1.0.0")
.option("-i, --inputfile ", "Input file path")
.option("-o, --outfile ", "Output file path")
.action(async (options) => {
const appId = process.env.APP_ID;
const secretCode = process.env.SECRET_CODE;
const inputFile = options.inputfile;
const outputFile = options.outfile;
if (!appId || !secretCode || !inputFile) {
console.error("Error: appId, secretCode, and inputfile are required.");
console.error(
'Use the "config" command to set appId and secretCode, or set them in a .env file.'
);
process.exit(1);
}
const url = "https://api.textin.com/ai/service/v1/pdf_to_markdown";
try {
const file = await fs.readFile(inputFile);
const response = await fetch(url, {
method: "POST",
headers: {
"x-ti-app-id": appId,
"x-ti-secret-code": secretCode,
},
body: file,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
const result = data.result || {};
const jsonData = result.detail;
if (outputFile) {
await ensureOutputDirectoryExists(outputFile);
await fs.writeFile(outputFile, JSON.stringify(jsonData, null, 2));
console.log(`Results written to ${outputFile}`);
} else {
console.log(JSON.stringify(jsonData, null, 2));
}
} catch (error) {
console.error("Error:", error.message);
process.exit(1);
}
});
program.parse(process.argv);
async function updateEnvFile(appId, secretCode) {
const envPath = path.join(process.cwd(), ".env");
let envContent = "";
try {
envContent = await fs.readFile(envPath, "utf8");
} catch (error) {
if (error.code !== "ENOENT") {
throw error;
}
}
const envLines = envContent.split("\n");
const updateLine = (key, value) => {
const index = envLines.findIndex((line) => line.startsWith(`${key}=`));
if (index !== -1) {
envLines[index] = `${key}=${value}`;
} else {
envLines.push(`${key}=${value}`);
}
};
if (appId) updateLine("APP_ID", appId);
if (secretCode) updateLine("SECRET_CODE", secretCode);
await fs.writeFile(envPath, envLines.join("\n"));
console.log("Configuration updated successfully.");
}
async function ensureOutputDirectoryExists(filePath) {
const dir = path.dirname(filePath);
try {
await fs.access(dir);
} catch (error) {
if (error.code === "ENOENT") {
await fs.mkdir(dir, { recursive: true });
console.log(`Created directory: ${dir}`);
} else {
throw error;
}
}
}
- 添加可执行权限
chmod +x textin.js
- 登录TextIn工作台,打开https://www.textin.com/console/dashboard/setting,获取
x-ti-app-id和x-ti-secret-code信息
- 配置
x-ti-app-id和x-ti-secret-code信息,-a参数对应x-ti-app-id,-s参数对应x-ti-secret-code
./textin.js config -a XXXXXX -s YYYYYY
- 运行
textin.js应用,-i参数对应输入本地文档的路径,-o参数对应输出json文件的路径
./textin.js -i ./MI-GAN.pdf -o ./MI-GAN.json
成功运行后,转换结果会保存到指定路径。如果需要保存解析后的Markdown文档,可自行修改textin.js相关代码。
