"没有什么问题是一行代码解决不了的,如果有,那就再加一行。" ------某位在 PDF 地狱里摸爬滚打的开发者
🧩 一、PDF 的"阴间属性"
PDF 是一个神奇的存在, 看起来是文本,其实是图片; 看起来整齐,其实结构乱七八糟。
尤其当客户甩给你几百份扫描件时,你才体会到: "这哪是文件,这是数字版的迷宫。"
所以,我决定不再手抄。 我要召唤两位 AI 战神出场: - 📖 智普 OCR:看得懂 PDF; - 🤖 阿里云大模型:读得懂内容。
🧠 二、为什么选择智普 OCR?
说实话,我一开始是想用阿里云的 OCR 的。 结果一试,才发现------ 它对 PDF 的支持并不理想,准确率也不高。
阿里云 OCR 对图片识别还行,但不支持直接解析 PDF; 而我主要处理的是 PDF 格式。
这时候智普 OCR 给了我惊喜:
- ✅Expert 模式 支持直接上传 PDF;
- 💰按页计费:限时 6 折优惠,仅 0.012 元/页;
- 🧠能高精度识别表格、公式、复杂版面;
- 🧾在科研、教辅、财报、标准等多场景表现稳定。
一句话:准确、稳定、便宜,还支持 PDF! 这还选啥?当然是智普。
详细的智普ocr类型如下:
| 服务类型 | 支持格式 | 最大文件大小 | 解析结果 | 计费方式 | 核心优势 | 推荐场景 |
|---|---|---|---|---|---|---|
| Prime | pdf, docx, doc, xls, xlsx, ppt, pptx, png, jpg, jpeg, csv, txt, md, html, epub, bmp, gif, webp, heic, eps, icns, im, pcx, ppm, tiff, xbm, heif, jp2 | PDF/DOC/DOCX/PPT ≤100MB XLS/XLSX/CSV ≤10MB PNG/JPG/JPEG ≤20MB | 图片 + Markdown 文件 + 包含布局信息的 JSON 文件 | 按解析页数消耗后付费 优惠后 0.12 元/页 | - 支持多种复杂版式(双栏、混排、三栏等) - 高精度解析图文、公式、段落、表格、页眉页脚等 - 多模态模型,适配复杂排版 - 精度行业领先,适合高要求场景 | - 科研出版:学术论文、技术书籍、会议资料 - 教育考试:试卷、教材、讲义 - 行业文档:合同、行业报告、白皮书 |
| Expert | ≤100MB | 图片 + Markdown 文件 | 按页数计费 限时 6 折优惠后 0.012 元/页 | - PDF、图片适配能力突出 - 高精度识别 PDF 表格与公式 - 在科研、教辅、企业、财报、标准等多领域表现稳定 - 性价比高,适合大规模解析 | - 学术研究:论文、学术报告、专利 - 教育出版:教辅书籍、教育资料 - 商业金融:年报、财报、研究报告、国家标准 | |
| Lite | pdf, docx, doc, xls, xlsx, ppt, pptx, png, jpg, jpeg, csv, txt, md | ≤50MB | 纯文本(无图片) | 按调用次数计费 当前免费(2025-10-08 起 0.01 元/次) | - 全格式支持,覆盖常见办公文档 - 提供基本结构化解析,速度快 - 成本低,适合对版面还原要求不高的任务 | - 办公场景:标准合同、规章制度、公告 - 批量解析:资料归档、文本抽取、快速预处理 |
💡 三、用 Java 调智普 OCR(Unirest版)
既然确定了 OCR 工具,那就上代码!
下面这段 Java 代码用 kong.unirest.Unirest 上传 PDF文件与获取解析结果的代码 👇
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import java.io.File;
import static com.scm.ai.AiConstaints.Z_API_KEY;
public class OrderParserOcr {
/**
* 上传需要解析的pdf文件
* @return
*/
public static ParserFileResponse upload(String filePath){
File file = new File(filePath);
HttpResponse<ParserFileResponse> response = Unirest.post("https://open.bigmodel.cn/api/paas/v4/files/parser/create")
.header("Authorization", "Bearer " + Z_API_KEY)
.field("tool_type", "expert")
.field("file_type", "PDF")
.field("file", file)
.asObject(ParserFileResponse.class);
return response.getBody();
}
/**
* 获取解析结果
* @param taskId
* @return
*/
public static ParserResultResponse getParseResult(String taskId){
HttpResponse<ParserResultResponse> response = Unirest.get("https://open.bigmodel.cn/api/paas/v4/files/parser/result/"+taskId+"/text")
.header("Authorization", "Bearer " + Z_API_KEY)
.asObject(ParserResultResponse.class);
return response.getBody();
}
}
其中Z_API_KEY是智普API的key。
智普是分两步实现
-
把需要解析的文件上传
📦 响应结果:
{ "success": true, "message": "任务创建成功", "task_id": "task_123456789" } -
获取解析的结果
📦 示例响应:
{ "status": "succeeded", "message": "结果获取成功", "content": "这是解析后的文本内容...", "task_id": "task_123456789", "parsing_result_url": "https://example.com/download/result.zip" }
⚙️ 四、为什么不直接用智普的大模型?
其实智普也有自己的大模型,体验也不错。 但我最终选择了 阿里云通义千问,原因很现实 👇
智普的 Java SDK 目前还不太完善, 无法设置 请求超时时间,而且在处理多页 PDF 时速度偏慢。
比如我识别一份 17 页的 PDF, 整整花了 300 多秒 才返回结果 🕐。
而阿里云的 Java API 体验就好得多:
- 支持自定义超时;
- 返回速度快;
- Java SDK 相对成熟;
- qwen-plus模型提取结构化数据的能力非常稳。
所以,我决定------ 识别用智普,理解用阿里云。 这波组合拳,刚柔并济,效率拉满 💪。
☁️ 五、阿里云大模型:让文字变得聪明
OCR 出来是纯文本内容,我们希望它变成结构化数据。 这就要靠通义千问(Qwen)来理解内容。
💬 Java 调用示例
-
BasicAliyunChat
import java.time.Duration; import java.util.Arrays; import java.lang.System; import com.alibaba.dashscope.aigc.generation.Generation; import com.alibaba.dashscope.aigc.generation.GenerationParam; import com.alibaba.dashscope.aigc.generation.GenerationResult; import com.alibaba.dashscope.common.Message; import com.alibaba.dashscope.common.Role; import com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.InputRequiredException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.protocol.ConnectionConfigurations; import com.alibaba.dashscope.utils.Constants; import lombok.Data; import static com.scm.ai.AiConstaints.ALIYUN_API_KEY; @Data public class BasicAliyunChat { private String systemContent = "You are a helpful assistant."; private String userContent = "你是谁?"; private String model = "qwen-plus"; // 若使用新加坡地域的模型,请释放下列注释 // static {Constants.baseHttpApiUrl="https://dashscope-intl.aliyuncs.com/api/v1";} public static GenerationResult callWithMessage(String systemContent, String userContent,String model,String apiKey) throws ApiException, NoApiKeyException, InputRequiredException { Generation gen = new Generation(); Message systemMsg = Message.builder() .role(Role.SYSTEM.getValue()) .content(systemContent) .build(); Message userMsg = Message.builder() .role(Role.USER.getValue()) .content(userContent) .build(); GenerationParam param = GenerationParam.builder() // 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:.apiKey("sk-xxx") // .apiKey(System.getenv("DASHSCOPE_API_KEY")) .apiKey(apiKey) // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models .model(model) .messages(Arrays.asList(systemMsg, userMsg)) .resultFormat(GenerationParam.ResultFormat.MESSAGE) .build(); return gen.call(param); } } -
OrderParserChat
import com.alibaba.dashscope.aigc.generation.GenerationResult; import com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.InputRequiredException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.protocol.ConnectionConfigurations; import com.alibaba.dashscope.utils.Constants; import com.scm.ai.ocr.OrderParserOcr; import com.scm.ai.ocr.ParserResultResponse; import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.time.Duration; import static com.scm.ai.AiConstaints.ALIYUN_API_KEY; @Slf4j @Data public class OrderParserChat { public static final String QWEN_PLUS = "qwen-plus"; private String systemPrompt = "作为一名订单解析专家,提供的文本是ocr识别的文本内容,要求返回的格式json。"; private String prompt="帮忙识别采购订单的客户、归属部门,并识别列表中的订单编码(订单编码在项目中7位数字串)、数量、单价"; public String createPrompt(String order){ StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(this.systemPrompt) .append(prompt) .append("下面是需要解析的内容"+"\n\n") .append(order); return stringBuilder.toString(); } public String parse(String taskId){ //如果数量大了,容易超时,需要设置readTimeout的时间 Constants.connectionConfigurations = ConnectionConfigurations.builder() // .connectTimeout(Duration.ofSeconds(10)) // 建立连接的超时时间, 默认 120s .readTimeout(Duration.ofSeconds(600)) // 读取数据的超时时间, 默认 300s // .writeTimeout(Duration.ofSeconds(60)) // 写入数据的超时时间, 默认 60s // .connectionIdleTimeout(Duration.ofSeconds(300)) // 连接池中空闲连接的超时时间, 默认 300s // .connectionPoolSize(256) // 连接池中的最大连接数, 默认 32 // .maximumAsyncRequests(256) // 最大并发请求数, 默认 32 // .maximumAsyncRequestsPerHost(256) // 单个主机的最大并发请求数, 默认 32 .build(); OrderParserChat chat = new OrderParserChat(); ParserResultResponse response = OrderParserOcr.getParseResult(taskId); try { GenerationResult generationResult = BasicAliyunChat.callWithMessage(chat.systemPrompt, chat.createPrompt(response.getContent()), QWEN_PLUS, ALIYUN_API_KEY); return generationResult.getOutput().getChoices().get(0).getMessage().getContent(); } catch (ApiException | NoApiKeyException | InputRequiredException e) { log.info("错误信息:"+e.getMessage()); return null; } } public static void main(String[] args) { String result = new OrderParserChat().parse("dccd5ead5fc94b83bbdf35a5290f9963"); log.info(result); } }
🧠 输出结果:
{ "订单编码明细": [
{
"物品号": "F040900008CP",
"数量": 2.00,
"单价": 10.00
},
{
"物品号": "F049900087CP",
"数量": 40.00,
"单价": 7.00
},
{
"物品号": "F050100042CP",
"数量": 3.90,
"单价": 7.50
},
{
"物品号": "F050100067CP",
"数量": 50.00,
"单价": 7.20
},
{
"物品号": "F050100077CP",
"数量": 20.00,
"单价": 6.00
},
{
"物品号": "F059900038CP",
"数量": 7.50,
"单价": 8.50
},
{
"物品号": "F083600039CP",
"数量": 2.50,
"单价": 4.60
}
]}
🔗 六、组合拳:OCR + LLM 一条龙
import com.scm.ai.chat.OrderParserChat;
import com.scm.ai.ocr.OrderParserOcr;
import com.scm.ai.ocr.ParserFileResponse;
import com.scm.ai.ocr.ParserResultResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderParserHandler {
public static void parser(String filePath) {
ParserFileResponse upload = OrderParserOcr.upload(filePath);
if(upload.getSuccess()){
String taskId = upload.getTaskId();
ParserResultResponse parseResult = OrderParserOcr.getParseResult(taskId);
String status = parseResult.getStatus();
//等待处理完毕
while ("processing".equals(status)){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
parseResult = OrderParserOcr.getParseResult(taskId);
status = parseResult.getStatus();
}
if ("succeeded".equals(status)) {
String content = parseResult.getContent();
OrderParserChat chat = new OrderParserChat();
//返回json格式
String result = chat.parse(taskId);
log.info(result);
}
}
}
}
💡 七、实战经验分享
-
✅ 智普 OCR Expert 模式对 PDF 特别友好,能识别表格和公式
-
⚡ 阿里云 qwen-plus性能稳定,支持超时控制
-
🧱 建议 OCR 后先清洗文本,去除多余换行或噪音
-
🧵 批量处理时可使用线程池提升效率
-
🧾 如果文本中有公式或表格,可保留原格式,方便后续处理
🌈 八、结语:从痛苦到优雅的跃迁
过去: 🕸我盯着 PDF,怀疑人生。
现在: 🍿我敲一下 Enter,看着 AI 自动提取。
智普 OCR 负责"看懂", 阿里云大模型负责"理解"。
AI 不只是替我干活, 更是让我------少熬夜多喝咖啡。 ☕😎
📦 九、项目结构参考
pdf-smart-extractor/
├── src/
│ ├── main/java/
│ │ ├── BasicAliyunChat.java
│ │ ├── OrderParserChat.java
│ │ ├── OrderParserOcr.java
│ │ └── OrderParserHandler.java
├── resources/
│ └── order.pdf
└── pom.xml
Maven 依赖
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-java</artifactId>
<version>3.14.1</version> <!-- 请注意版本更新 -->
</dependency>
<!-- alibaba的ai依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<!-- 请将 'the-latest-version' 替换为最新版本号:https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
<version>2.21.11</version>
</dependency>
<dependency>
<groupId>ai.z.openapi</groupId>
<artifactId>zai-sdk</artifactId>
<version>0.0.6</version>
</dependency>