营业执照识别:从 curl 快速验证到工程级 Python 封装
适用场景与接口能力在企业资质审核、合规风控、供应链管理中经常需要批量核验营业执照信息。传统人工录入效率低且易出错通过 OCR 识别接口可以自动提取统一社会信用代码、公司名称、法人、准备资本、经营期限等 12 个关键字段直接对接数据库或审批流程。本文使用的接口为「营业执照识别」其 base URL 为https://v1.apizero.cn/api/business-license单张图片 QPS 上限 2 次/秒支持 JPG/PNG 格式文件大小不超过 10 MB。图片需清晰完整、无遮挡且文字方向正确。接口参数与鉴权该接口通过 HTTP POST 请求调用需要携带两个 HeaderAuthorization: Bearer 你的 API Key— 认证凭据每个调用者需从服务商获取独立 Key。Content-Type: application/json— 请求体为 JSON 格式。请求体是一个 JSON 对象包含两个必填字段字段名类型说明input_typestring图片传入方式固定值url或base64input_datastring图片 URL 或 base64 编码字符串最大 10MB第一步用 curl 快速验证在正式工程化之前建议先用 curl 发送一次请求确认接口可用性、网络连通性以及返回结构是否符合预期。以下示例使用图片 URL 方式调用请替换占位符curl -sS \ -X POST \ -H Authorization: Bearer YOUR_API_KEY \ -H Content-Type: application/json \ -d {input_type: url, input_data: https://example.com/business-license.jpg} \ https://v1.apizero.cn/api/business-license成功响应将返回 200 状态码及类似以下 JSON{ code: 0, msg: 成功, request_id: req_abc123, data: { unified_social_credit_code: 91310000XXXXXXXXXX, company_name: 某某科技有限公司, legal_representative: 张三, registered_capital: 100万人民币, established_date: 2015-06-01至无固定期限, business_scope: 软件开发信息技术咨询服务, domicile: 上海市浦东新区某某路123号, company_type: 有限责任公司自然人独资, approval_date: 2025-01-10, certificate_type: 营业执照, established_time: 2015-06-01, website: http://www.gsxt.gov.cn } }如果返回code非零可以根据msg字段排查常见问题详见错误处理小节。第二步从 curl 到 Python 工程封装生产环境中不可能每次调用都手动执行 curl必须通过代码将鉴权、请求、重试、异常处理、日志记录等逻辑封装为一个可复用的函数或类。下面以 Python 为例逐步构建。2.1 基础请求函数import requests import json class LicenseRecognizer: 营业执照识别客户端 ENDPOINT https://v1.apizero.cn/api/business-license def __init__(self, api_key: str): self.headers { Authorization: fBearer {api_key}, Content-Type: application/json } def recognize(self, input_data: str, input_type: str url) - dict: 识别营业执照 :param input_data: 图片URL或base64字符串 :param input_type: 传入方式url或base64 :return: 接口返回的完整JSON payload { input_type: input_type, input_data: input_data } resp requests.post( self.ENDPOINT, headersself.headers, jsonpayload, timeout15 ) resp.raise_for_status() # 非2xx状态码抛出异常 return resp.json()2.2 增加重试与速率控制接口 QPS 为 2 次/秒如果并发请求过高可能被限流。建议使用tenacity库实现指数退避重试并使用time.sleep或ratelimit控制频率。from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type from requests.exceptions import RequestException, HTTPError from time import sleep class AdvancedRecognizer(LicenseRecognizer): retry( stopstop_after_attempt(3), waitwait_exponential(multiplier1, min1, max10), retryretry_if_exception_type((RequestException, HTTPError)), reraiseTrue ) def recognize_with_retry(self, input_data: str, input_type: str url) - dict: # 先检查与本地上次请求间隔是否 0.5 秒 now time.time() if hasattr(self, _last_call_time) and now - self._last_call_time 0.5: sleep(0.5 - (now - self._last_call_time)) result self.recognize(input_data, input_type) self._last_call_time time.time() # 如果业务状态码非0也视为需要重试的异常 if result.get(code) ! 0: raise ValueError(f业务错误: {result.get(msg,)}) return result2.3 将返回数据映射为结构化对象为方便下游使用可以将识别结果封装为数据类from dataclasses import dataclass from typing import Optional dataclass class LicenseInfo: unified_social_credit_code: str company_name: str legal_representative: str registered_capital: str established_date: str business_scope: str domicile: str company_type: str approval_date: Optional[str] None certificate_type: Optional[str] None established_time: Optional[str] None website: Optional[str] None def parse_license_data(data: dict) - LicenseInfo: return LicenseInfo( unified_social_credit_codedata.get(unified_social_credit_code, ), company_namedata.get(company_name, ), legal_representativedata.get(legal_representative, ), registered_capitaldata.get(registered_capital, ), established_datedata.get(established_date, ), business_scopedata.get(business_scope, ), domiciledata.get(domicile, ), company_typedata.get(company_type, ), approval_datedata.get(approval_date), certificate_typedata.get(certificate_type), established_timedata.get(established_time), websitedata.get(website) )2.4 批量处理与异常隔离当需要处理多张图片时建议使用线程池控制并发数避免超 QPSfrom concurrent.futures import ThreadPoolExecutor, as_completed def batch_recognize(recognizer: AdvancedRecognizer, image_urls: list[str], max_workers: int 2): 批量识别返回 {url: LicenseInfo} 字典 results {} with ThreadPoolExecutor(max_workersmax_workers) as executor: future_map {executor.submit(recognizer.recognize_with_retry, url): url for url in image_urls} for future in as_completed(future_map): url future_map[future] try: resp future.result() info parse_license_data(resp[data]) results[url] info except Exception as e: # 记录失败但继续处理其他图片 print(f识别失败 {url}: {e}) results[url] None return results返回值解读从响应示例可以看到data字段里包含了营业执照上几乎所有结构化信息。具体字段与含义如下字段说明unified_social_credit_code统一社会信用代码18 位字母数字组合company_name公司全称legal_representative法定代表人姓名registered_capital准备资本含单位如“100万人民币”established_date成立日期至有效期如“2015-06-01至无固定期限”business_scope经营范围domicile住所准备地址company_type公司类型如“有限责任公司自然人独资”approval_date核准日期如无则返回空certificate_type证件类型固定为“营业执照”established_time成立日期纯日期website公示系统链接固定为http://www.gsxt.gov.cn注意code为 0 表示成功非 0 值需检查msg和request_id可用于向服务商排查问题。常见错误与排查HTTP 状态code 值possible reason处理方式401—Authorization 缺失或无效检查 API Key 是否正确确保前缀Bearer4001000图片无法下载或 base64 解析失败确认input_data可访问且为有效图片4001001图片格式不支持或超过 10MB转换格式或压缩图片4001002图片内容不清晰无法识别选择光线均匀、文本无遮挡的图片429—请求频率超过 QPS 2次/秒加入 sleep 或使用令牌桶限流5009999服务端异常稍后重试或联系技术支持并提供request_id在工程封装中建议对非 200 状态码或code ! 0都进行捕获并记录日志同时提供友好的错误提示给上游调用方。工程化注意事项API Key 管理切勿将 API Key 硬编码在代码中。应通过环境变量、配置中心或密钥管理服务注入。超时与连接池使用requests.Session并设置pool_connections与pool_maxsize避免频繁创建连接。日志记录每个请求的request_id是问题定位的关键线索务必打印或存储。图片预处理若用户上传的图片方向不正或存在噪点可先用 OpenCV 做角度校正或增强后再传入接口。熔断降级如果依赖的接口连续失败应触发熔断切换到备用方案如人工审核队列。测试覆盖建议针对返回的 12 个字段做字段完整性检查并对常见错误场景编写单元测试。参考文档营业执照识别接口详情https://apizero.cn/aidocs/business-license原始 Markdown 文档https://apizero.cn/aidocs/business-license/raw.md