讯飞 OCR 图片识别与解析完整指南(python)
·
本文档手把手教你如何接入讯飞图片 OCR 服务,并实现智能解析。从零开始,10 分钟跑通整个流程。
目录
什么是讯飞 OCR?
讯飞 OCR(OCR for LLM)是讯飞开放平台提供的图片文字识别服务,可以将图片中的表格、文字自动识别并转换为结构化数据。
适用场景:
- 员工工时单识别
- 票据信息提取
- 表格数据自动化录入
- 文档数字化
你将学到:
- 如何配置和接入讯飞 OCR
- 如何调用 API 识别图片
- 如何编写解析规则提取关键信息
- 如何处理识别结果和常见错误
快速开始(10 分钟上手)
第一步:开通服务并获取密钥
- 访问讯飞开放平台
- 开通「OCR for LLM」服务
- 在应用详情页获取以下凭证:
IFLYTEK_APP_ID=你的应用ID
IFLYTEK_API_KEY=你的API密钥
IFLYTEK_API_SECRET=你的API密钥
IFLYTEK_API_BASE_URL=https://cbm01.cn-huabei-1.xf-yun.com
IFLYTEK_FUNCTION_ID=se75ocrbm # 可选,默认值
安全提示:请勿将真实密钥提交到代码仓库,统一通过 .env 文件或 CI/CD 平台的加密环境变量管理。
第二步:安装依赖
# 克隆项目
git clone <你的仓库地址>
cd 项目
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 安装依赖
pip install -r requirements.txt
第三步:配置环境变量
# 复制示例文件
cp .env.example .env
# 编辑 .env 文件,填入你的密钥
IFLYTEK_APP_ID=你的APP_ID
IFLYTEK_API_KEY=你的API_KEY
IFLYTEK_API_SECRET=你的API_SECRET
IFLYTEK_API_BASE_URL=https://cbm01.cn-huabei-1.xf-yun.com
IFLYTEK_FUNCTION_ID=se75ocrbm
第四步:启动后端服务
uvicorn backend.app:app --reload --host 127.0.0.1 --port 8000
注意:所有 API 请求都需要携带请求头 X-Token: financial
第五步:测试 OCR 识别
# 1. 创建测试工作区
curl -X POST http://127.0.0.1:8000/api/workspaces \
-H 'Content-Type: application/json' \
-H 'X-Token: financial' \
-d '{"month":"2025-08","name":"测试工作区"}'
# 2. 上传图片进行识别
curl -X POST 'http://127.0.0.1:8000/api/workspaces/2025-08/upload?file_type=timesheet' \
-H 'X-Token: financial' \
-F 'files=@/path/to/your/image.png'
# 3. 查看识别结果
curl -H 'X-Token: financial' \
http://127.0.0.1:8000/api/workspaces/2025-08/documents
如果看到返回结果中包含 ocr_metadata 或 ocr_table 字段,说明 OCR 识别已成功!
理解 OCR 返回数据
数据结构概览
OCR 识别后返回的数据结构如下:
{
"text": "识别的原始文本(JSON字符串或纯文本)",
"metadata": {
"provider": "iflytek", # 供应商标识
"result_format": "json", # 结果格式:json 或 plain
"tables": [[...], [...]], # 第一张识别到的表格(二维数组)
"all_tables": [[[...]], ...], # 所有识别到的表格(可选)
"raw": {...}, # 原始结构化对象(JSON格式时)
"raw_text": "...", # 原始文本(纯文本格式时)
"sid": "会话ID(用于排查)" # 可选
}
}
表格数据结构
表格以三维数组的形式存储:
# tables 是一个列表,包含多个表格
tables = [
# 第一个表格(二维数组)
[
["姓名", "标准工时", "加班", "实际出勤", "月份"], # 表头行
["张三", "152", "59", "211", "2025-08"], # 数据行1
["李四", "152", "45", "197", "2025-08"], # 数据行2
],
# 第二个表格(如果有)
[...]
]
在代码中使用
from pathlib import Path
from backend.infrastructure.iflytek import IFlyTekOCRClient
# 初始化客户端
client = IFlyTekOCRClient(
app_id="你的APP_ID",
api_key="你的API_KEY",
api_secret="你的API_SECRET",
api_base="https://cbm01.cn-huabei-1.xf-yun.com",
function_id="se75ocrbm",
)
# 识别图片
result = client.extract_text(Path("/path/to/your/image.png"))
# 查看结果
print("原始文本:", result.text[:200]) # 前200个字符
print("元数据键:", result.metadata.keys())
print("第一张表格:", result.metadata.get("tables"))
print("所有表格:", result.metadata.get("all_tables", []))
解析规则详解
OCR 识别只是第一步,我们还需要从识别结果中提取关键信息。系统支持多条规则并行执行,自动选择最佳结果。
规则配置
在 config/ocr_rules.py 中配置规则和阈值:
# 阈值配置
OCR_THRESHOLDS = {
"sum_tolerance": 0.1, # 数值校验容差:|actual - (standard+overtime)| <= 0.1
"header_scan_rows": 10, # 表头扫描行数上限
"max_search_steps": 6, # 标签右/下搜索步数
"sum_column_max_rows": 200, # 列求和行数上限
"numeric_max": 1000.0, # 数值上限(防止异常值)
}
# 规则配置
OCR_RULES = [
{
"id": "规则1",
"name": "工时结算单(多人)",
"priority": 3, # 优先级(数字越大优先级越高)
"enabled": True # 是否启用
},
{
"id": "规则2",
"name": "外派工时单(单人)",
"priority": 2,
"enabled": True
},
{
"id": "规则3",
"name": "外派人员工时表(单人)",
"priority": 1,
"enabled": True
},
]
规则执行流程
规则1:工时结算单(多人表)
适用场景:包含多行员工数据的表格,有明确的表头行。

提取逻辑:
- 在前 N 行内定位表头(“姓名”、“标准”、“加班”、"实际"等)
- 建立列索引映射
- 逐行提取数据
- 如果缺少"实际出勤",用"标准工时 + 加班"计算
伪代码示例:
def extract_rule1_jiesuandan(tables, filename):
# 1. 定位表头行和列索引
table, header_row, cols = locate_header(
row_keywords=['姓名'],
col_keywords=['标准', '加班', '实际', '结算']
)
results = []
# 2. 从表头下一行开始逐行提取
for r in range(header_row + 1, len(table)):
name = clean_name(table[r][cols['姓名']])
std = to_float(table[r][cols['标准']]) or 0
ot = to_float(table[r][cols['加班']]) or 0
act = to_float(table[r][cols.get('实际')]) or (std + ot) # 兜底计算
if name:
results.append({
'name': name,
'standard_hours': std,
'total_overtime': ot,
'actual_hours': act,
'month': infer_month(tables),
'data_source': filename,
'original_file': filename
})
return results
规则2:外派工时单(单人表,文本优先)
适用场景:单人员工数据,可能以文本形式呈现(如 “152+53.5+5.5=211”)。
提取逻辑:
- 姓名提取:优先从文本标签扫描,其次正则匹配,最后从文件名推断
- 数值提取:优先解析文本公式,失败则标签扫描
- 兜底策略:已知两项可推出第三项(如:actual = standard + overtime)
伪代码示例:
def extract_rule2_paigongdan(tables, filename, metadata=None):
# 1. 提取姓名(多种方式尝试)
name = (
scan_text_by_labels(['姓名', '员工姓名']) or
regex_find_name(metadata) or
filename_to_name(filename)
)
# 2. 优先从文本解析公式(如 "152+53.5+5.5=211")
std, basic_ot, extra_ot, act = parse_text_equation(metadata)
# 3. 如果文本解析失败,使用标签扫描
if not any([std, basic_ot, extra_ot, act]):
std = scan_numeric_by_labels(['标准工时', '正常工时'])
act = scan_numeric_by_labels(['结算工时', '实际出勤', '总工时'])
basic_ot = scan_numeric_by_labels(['基础加班', '平时加班'])
extra_ot = scan_numeric_by_labels(['超时加班'])
# 4. 兜底计算
act = act or sum_filter(std, basic_ot, extra_ot)
return [{
'name': name or '',
'standard_hours': std or 0,
'total_overtime': (basic_ot or 0) + (extra_ot or 0),
'actual_hours': act or 0,
'month': infer_month(tables),
'data_source': filename,
'original_file': filename
}]
规则3:外派人员工时表(行列交叉定位)

适用逻辑:
- 通过列头定位列索引
- 在包含关键行头(如"合计"或姓名行)的位置取值
- 如果取不到值,在表头下方对该列进行 N 行求和
伪代码示例:
def extract_rule3_paiyuan(tables, filename):
# 1. 找到数据行(包含"合计"或姓名)
row = find_data_row(['合计', '姓名'])
# 2. 定位列索引
col_std = find_col(['标准工时', '正常工时'])
col_ot = find_col(['加班', '总加班'])
col_act = find_col(['实际出勤', '结算工时', '总工时'])
# 3. 读取值,如果为空则对列求和
std = read_or_sum(row, col_std)
ot = read_or_sum(row, col_ot)
act = read_or_sum(row, col_act)
return [{
'name': infer_name(row) or '',
'standard_hours': std or 0,
'total_overtime': ot or 0,
'actual_hours': act or 0,
'month': infer_month(tables),
'data_source': filename,
'original_file': filename
}]
评分与择优机制
系统会同时执行所有启用的规则,然后按以下标准选择最佳结果:
- 是否有有效值:优先选择
actual_hours > 0的结果 - 实际值大小:在都有值的情况下,选择
actual_hours更大的 - 规则优先级:如果前两项相同,按配置的
priority选择
def score(rule_id, records):
if not records:
return (-1, 0, 0) # 无效结果
first = records[0]
act = float(first.get('actual_hours') or 0)
pri = get_rule_priority(rule_id, 0) # 从配置读取优先级
return (
1 if act > 0 else 0, # 是否有值
act, # 实际值大小
pri # 规则优先级
)
# 选择最佳结果
candidates = [
("规则1", results1),
("规则2", results2),
("规则3", results3)
]
candidates.sort(key=lambda x: score(x[0], x[1]), reverse=True)
best_result = candidates[0][1]
有效性校验
在评分前,系统会对每条规则的结果进行有效性校验:
def is_valid(record):
std = float(record.get("standard_hours") or 0)
act = float(record.get("actual_hours") or 0)
ot = float(record.get("total_overtime") or 0)
tol = OCR_THRESHOLDS["sum_tolerance"]
# 情况1:actual ≈ standard + overtime(允许容差)
if act > 0 and abs(act - (std + ot)) <= tol:
return True
# 情况2:三者全为0(可能是空记录)
if std == 0 and act == 0 and ot == 0:
return True
# 情况3:仅有actual有值(也视为有效)
if act > 0 and std == 0 and ot == 0:
return True
return False
常见问题排查
1. 401/403 错误(认证失败)
可能原因:
- 环境变量配置错误
- API 密钥过期或无效
- 请求头缺少
X-Token
解决方法:
# 检查环境变量
echo $IFLYTEK_APP_ID
echo $IFLYTEK_API_KEY
# 检查 .env 文件
cat .env | grep IFLYTEK
# 确保请求头正确
curl -H 'X-Token: financial' ...
2. 上传成功但表格为空
可能原因:
- 图片质量不佳(模糊、倾斜、光线不足)
- 表格边框不完整
- 规则阈值设置过严
解决方法:
# 1. 检查 all_tables(可能有其他候选表格)
result = client.extract_text(path)
all_tables = result.metadata.get("all_tables", [])
print(f"识别到 {len(all_tables)} 张表格")
# 2. 调整阈值(config/ocr_rules.py)
OCR_THRESHOLDS = {
"header_scan_rows": 15, # 增加表头扫描行数
"max_search_steps": 8, # 增加搜索步数
}
# 3. 检查图片质量
# - 确保图片清晰、正对拍摄
# - 避免强光和阴影
# - 表格边框完整
3. 返回格式不是 JSON(result_format=plain)
说明:当 OCR 无法识别为结构化表格时,会返回纯文本。
处理方法:
if result.metadata.get("result_format") == "plain":
# 使用文本回退策略
raw_text = result.metadata.get("raw_text", "")
# 可以用正则表达式或关键词扫描提取信息
extracted = parse_text_fallback(raw_text)
4. 超时或限频(429 错误)
解决方法:
- 避免并发请求过多
- 实现重试机制(指数退避)
- 检查配额和 QPS 限制
import time
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def call_ocr_with_retry(client, path):
return client.extract_text(path)
5. 规则选择不正确
解决方法:
# 在 config/ocr_rules.py 中调整优先级
OCR_RULES = [
{"id": "规则1", "priority": 3, "enabled": True}, # 提高优先级
{"id": "规则2", "priority": 2, "enabled": True},
{"id": "规则3", "priority": 1, "enabled": False}, # 临时关闭
]
进阶扩展
如何新增自定义规则
场景:你遇到了一种新的表格样式,需要添加专用解析规则。
步骤:
- 在配置中添加规则(
config/ocr_rules.py):
OCR_RULES = [
# ... 现有规则 ...
{
"id": "规则4",
"name": "新样式表格",
"priority": 2,
"enabled": True
},
]
- 实现规则函数(
backend/extractors/ocr_image.py):
def extract_rule4_newstyle(tables, filename, metadata=None):
"""
新规则实现
输入:tables(三维数组)、filename(文件名)、metadata(元数据)
输出:list[dict],字段包含 name/standard_hours/total_overtime/actual_hours/month 等
"""
results = []
# 1. 定位表头/行头/列头
# 2. 提取数据
# 3. 返回统一格式
return [{
'name': name,
'standard_hours': std or 0,
'total_overtime': ot or 0,
'actual_hours': act or 0,
'month': infer_month(tables),
'data_source': filename,
'original_file': filename
}]
- 在择优函数中调用(
extract_image_data_with_multiple_rules):
def extract_image_data_with_multiple_rules(tables, filename, metadata=None):
results1 = extract_rule1_jiesuandan(tables, filename)
results2 = extract_rule2_paigongdan(tables, filename, metadata)
results3 = extract_rule3_paiyuan(tables, filename)
results4 = extract_rule4_newstyle(tables, filename, metadata) # 新增
candidates = [
("规则1", results1),
("规则2", results2),
("规则3", results3),
("规则4", results4), # 新增
]
# ... 择优逻辑 ...
- 调整阈值(如需要):
OCR_THRESHOLDS = {
"header_scan_rows": 12, # 根据新表格调整
"max_search_steps": 7,
}
- 编写测试用例:
def test_rule4_newstyle():
tables = [[["表头1", "表头2"], ["数据1", "数据2"]]]
result = extract_rule4_newstyle(tables, "test.png")
assert len(result) > 0
assert result[0]["name"] == "预期值"
阈值调优建议
| 阈值参数 | 说明 | 调优建议 |
|---|---|---|
sum_tolerance |
数值校验容差 | 如果数据精度要求高,设为 0.01;一般设为 0.1 |
header_scan_rows |
表头扫描行数 | 表格表头在下方时,增加此值(如 15-20) |
max_search_steps |
标签搜索步数 | 表格单元格间距大时,增加此值(如 8-10) |
sum_column_max_rows |
列求和行数上限 | 数据行很多时,适当增加(如 300-500) |
numeric_max |
数值上限 | 防止异常值,根据业务调整 |
关键词词表外置(推荐)
将规则中使用的关键词抽取到配置文件,便于维护:
# config/ocr_keywords.py
KEYWORDS = {
"name": ["姓名", "员工姓名", "员工"],
"standard": ["标准工时", "正常工时", "应出勤"],
"overtime": ["加班", "总加班", "加班合计"],
"actual": ["实际出勤", "结算工时", "总工时", "实际"],
"month": ["月份", "年月", "结算月份"],
}
# 在规则中使用
from config.ocr_keywords import KEYWORDS
name_col = find_col(KEYWORDS["name"])
生产环境部署
Docker Compose 配置:
# docker-compose.prod.yml
services:
backend:
environment:
- IFLYTEK_APP_ID=${IFLYTEK_APP_ID}
- IFLYTEK_API_KEY=${IFLYTEK_API_KEY}
- IFLYTEK_API_SECRET=${IFLYTEK_API_SECRET}
- IFLYTEK_API_BASE_URL=${IFLYTEK_API_BASE_URL}
- IFLYTEK_FUNCTION_ID=${IFLYTEK_FUNCTION_ID}
CI/CD 配置(Bitbucket Pipelines):
- 在仓库设置中添加加密环境变量
- 推送到主分支后自动构建和部署
总结
通过本文档,你已经学会了:
- 配置和接入:如何获取密钥、配置环境、启动服务
- 基本使用:如何调用 API、查看返回数据
- 规则编写:理解三种规则的工作原理和适用场景
- 问题排查:常见错误的定位和解决方法
- 进阶扩展:如何新增规则、调优阈值
下一步建议:
- 尝试识别不同类型的图片,观察识别效果
- 根据实际业务需求调整规则和阈值
- 为常用表格样式编写专用规则
- 在生产环境中部署并监控识别准确率
相关资源:
- 讯飞开放平台文档
- 项目代码:
backend/infrastructure/iflytek.py(OCR 客户端) - 规则实现:
backend/extractors/ocr_rules/(规则模块) - 配置示例:
config/ocr_rules.py(规则和阈值配置)
更多推荐



所有评论(0)