拓冰建站拓冰建站
首页 / 资讯中心 / 正文

微信小程序集成百度OCR:四类证照识别从鉴权到字段校验

简介微信小程序调用百度API实现身份证、车牌号、驾驶证、行驶证识别的完整工程包面向需要在小程序中快速接入图像识别能力的前端开发者与云函数学习者。资源围绕百度智能云通用文字识别服务展开涵盖云端应用创建、APP_ID/API_KEY/SECRET_KEY配置以及基于baidu-aip-sdk的imageClassify调用示例并给出云函数入口文件与可选参数设置适合作为从零集成百度AI能力的参考工程。压缩包共2055个文件以1194个js文件、694个md文档、100个json配置为主另有少量html、xml、css与txt文件整体大小26.84MB目录结构便于按功能检索。已有244人学习下载适合希望通过现成代码快速验证证件识别流程、同时理解小程序云函数与第三方API对接细节的开发者也能帮助初学者熟悉百度AI开放平台的应用创建与鉴权机制。1. 微信小程序调百度API做四类证照识别先看这套链路怎么闭拍一张照片表单自动填完——这是驾校报名、停车场缴费、车险理赔、租车押金对“识别”最朴素的期待。标题里的 zip 是一套演示工程小程序采集图片百度 OCR 把证件照片变成结构化字段靠一张 access_token 串起来。四个接口长得像调用姿势却各不相同。身份证要分正反面传 id_card_side车牌返回号码加颜色驾驶证和行驶证字段相似但接口路径差一点就报错。这套东西的难点不在调通一次而在参数边界和结果进表单前的校验后者的坑更隐蔽。下面把这四条链路拆开讲先解决鉴权和选型再走通选图到提交的完整链路最后落在字段解析和错误码处理上。适合正在接多个 OCR 场景的开发者也适合把识别能力并进 uni-app 的同事。2. 百度API四类识别接口的选型逻辑与access_token缓存方案2.1 身份证、车牌、驾驶证、行驶证四个接口的关键差异先统一口径百度 OCR 下挂的身份证识别、车牌号识别、驾驶证识别、行驶证识别是四个独立接口共享同一套鉴权但路径、必传参数、返回字段各不一样。把它们放进同一张表后面封装代码时才好对齐。接口请求路径rest/2.0/ocr/v1 之后必传参数返回核心字段身份证识别/idcardid_card_sidefront 或 back姓名、公民身份号码、住址、签发机关、有效期限车牌号识别/license_plateimage 或 urlnumber、color、vertexes_location驾驶证识别/driving_licenseimage 或 url证号、姓名、准驾车型、有效期限行驶证识别/vehicle_licenseimage 或 url号牌号码、车辆识别代号、发动机号码、注册日期四个接口的返回结构都是 words_result 包裹一坨字段键值对外加 words_result_num 表示识别出多少个字段。也就是说小程序端只需要一个统一的请求函数传入接口路径和附加参数返回结果再做按场景拆分。这也是为什么标题这套 demo 能塞进一个 zip核心代码量其实不大大头在参数边界。选择这四个接口而不是自训模型原因很直接证件类识别的免费额度对中小业务基本够用且返回字段是百度预置好的不需要自己标注样本。真要自己做驾驶证和行驶证的正本副本、印章压字、反光这些样本收集成本远高于调用费用。2.2 先用 curl 验证 API Key 能换到 access_token写小程序代码之前我一般先拿 curl 把鉴权链路打通。这一步能筛掉大部分“账号没开通”或“Key 抄错”的问题。curl -i https://aip.baidubce.com/oauth/2.0/token?grant_typeclient_credentialsclient_idYOUR_API_KEYclient_secretYOUR_SECRET_KEY返回体里拿到 access_token 与 expires_in 即为成功。expires_in 默认 2592000单位是秒也就是 30 天。注意这里两个参数一个都不能混client_id 填的是 API Keyclient_secret 填的是 Secret Key控制台创建应用时两个值并排展示不少人把位置对调结果永远报 110 access token invalid。响应里如果出现 error 字段优先去控制台确认应用是否创建成功、是否启用了对应接口的权限。2.3 小程序端 access_token 缓存策略一次换取29 天复用token 要缓存是常识但具体怎么存有个坑直接用 30 天整作为过期点会在到期当天出现一批请求同时去刷新 token 的情况刚好撞上 QPS 限制。常见做法是提前一天过期代码里判断剩余有效期小于 86400000 毫秒就重新拉取。// utils/baidu-auth.js const TOKEN_URL https://aip.baidubce.com/oauth/2.0/token function requestToken() { return new Promise((resolve, reject) { wx.request({ url: TOKEN_URL, method: POST, data: { grant_type: client_credentials, client_id: 你的APIKey, client_secret: 你的SecretKey }, success: (res) { if (res.data res.data.access_token) { resolve(res.data) } else { reject(new Error(token获取失败: ${JSON.stringify(res.data)})) } }, fail: reject }) }) } async function getBaiduToken() { const cached wx.getStorageSync(baidu_ocr_token) const expireAt wx.getStorageSync(baidu_ocr_token_expire) || 0 if (cached expireAt - Date.now() 86400000) { return cached } const data await requestToken() wx.setStorageSync(baidu_ocr_token, data.access_token) wx.setStorageSync(baidu_ocr_token_expire, Date.now() data.expires_in * 1000) return data.access_token } module.exports { getBaiduToken }缓存键和过期时间分开存而不是拼成一个对象是为了后面排查“token 失效但是缓存还在”时能单独清某个键。真机调试时如果出现间歇性 110先删掉这两个 storage 再试。参数上需要注意 expires_in 单位是秒与 Date.now() 的毫秒做运算时要乘 1000这个单位差最容易让人困惑。2.4 密钥放前端还是服务端三种调用方式的取舍小程序直连百度是 demo 最快的路径但 API Key 和 Secret Key 都打包在代码包里反编译小程序就能翻出来。百度侧虽然支持在控制台绑定 IP 白名单但小程序真机的出口 IP 不固定白名单基本锁不死客户端。所以直连只适合个人学习。常见做法是密钥放后端或微信云函数前端把图片 base64 传给自己的接口由服务端完成 token 获取和识别调用。三种方式的取舍可以用一张表说清调用方式密钥安全开发成本适用阶段小程序直连差密钥随包下发最低学习验证、内测微信云函数好密钥在云端中需上传云函数个人正式小项目自建后端转发好可统一限流与审计高需服务端联调团队项目、已有后端云函数方案的识别代码和直连几乎一样只是把 wx.request 换成云函数调用token 缓存在云函数内存或云数据库里。前端不用关心 access_token拿到的是已经解析好的识别结果。3. 微信小程序前端链路选图、压缩、base64 与识别请求组装3.1 用 wx.chooseMedia 采集图片并按接口体积上限压缩小程序选图的标准入口是 wx.chooseMedia替代了早年废弃的 wx.chooseImage。拿到临时文件路径后不能直接转 base64 就提交因为百度侧对图片有体积限制base64 编码后通常要求 4MB 以内而编码本身会让体积再膨胀约三分之一。一台手机拍出的照片动辄 2MB 以上必须先压缩。// utils/image.js - 选择并压缩图片 function chooseAndCompress(quality 80) { return new Promise((resolve, reject) { wx.chooseMedia({ count: 1, mediaType: [image], sourceType: [album, camera], success: ({ tempFiles }) { wx.compressImage({ src: tempFiles[0].tempFilePath, quality, // 0-100数值越小体积越小 success: ({ tempFilePath }) { const fs wx.getFileSystemManager() fs.readFile({ filePath: tempFilePath, encoding: base64, success: (res) resolve(res.data), fail: reject }) }, fail: reject }) }, fail: reject }) }) }quality 参数不是压得越低越好。身份证这类对纹理敏感的目标压到 60 以下时边缘发虚识别率明显下降车牌和驾驶证这类远距离拍摄的建议 80 到 90。不同识别目标可以参考下面的经验值识别目标压缩质量建议注意事项身份证80-90姓名和号码区域要保持清晰车牌80反光时不要过度压缩驾驶证 / 行驶证85字段密集压太低易丢字另外 wx.compressImage 只改变质量不改变分辨率遇到超大尺寸图片比如 4000px 宽的全景依然可能超限后续可以在底层对宽高做一次等比缩放。3.2 统一请求函数四个接口共用一套提交逻辑四个接口的请求协议完全一致POST 到 /rest/2.0/ocr/v1/{接口路径}query 上带 access_tokenbody 里传 image 的 base64 字符串。所以封装一个 ocrRequest 函数比给每个接口各写一遍 wx.request 要省事得多。这段代码在 uni-app 里同样成立只要把 wx.request 换成 uni.request其余逻辑原样复用。// utils/ocr.js - 百度 OCR 统一请求 const { getBaiduToken } require(./baidu-auth) const OCR_BASE https://aip.baidubce.com/rest/2.0/ocr/v1 async function ocrRequest(apiPath, imageBase64, extraParams {}) { const token await getBaiduToken() const data Object.assign({ image: imageBase64 }, extraParams) return new Promise((resolve, reject) { wx.request({ url: ${OCR_BASE}/${apiPath}?access_token${token}, method: POST, header: { Content-Type: application/x-www-form-urlencoded }, data, success: (res) { if (res.data res.data.error_code) { reject(new Error([${res.data.error_code}] ${res.data.error_msg})) return } resolve(res.data) }, fail: reject }) }) } module.exports { ocrRequest }这里有个非常容易踩的坑base64 字符串里包含 、/、 三个字符。如果手工把 image 拼进 URL必须经过 encodeURIComponent 处理否则 会被解析成空格百度那边收到的图片是坏的。上面代码把 image 放在 data 对象里交给 wx.request由框架按表单格式编码能避免手工拼接带来的转义问题。access_token 本身是安全的字母数字组合可以直接放 query。header 里 Content-Type 用 x-www-form-urlencoded与 body 里的表单数据对应。如果你在后端用 axios 转发记得用 URLSearchParams 或 qs 序列化不要直接传 JSON 对象否则百度会报参数类型错误。3.3 修改刚进入的识别页加载状态避免重复点击识别过程包含压缩、上传、识别三段串行操作用户手快会重复触发。常见做法是在页面 data 里加识别中标志进入识别流程时置为 true提交按钮绑定 disabled。加载文案按阶段区分压缩中、识别中、整理结果。这部分对体验的影响比想象中大用户在弱网环境下等不到反馈会疯狂点按钮直接把 QPS 打满。// pages/recognize/recognize.js 的片段 Page({ data: { recognizing: false }, async onTapRecognize() { if (this.data.recognizing) return this.setData({ recognizing: true }) try { const imageBase64 await chooseAndCompress(85) const result await ocrRequest(idcard, imageBase64, { id_card_side: front, detect_direction: true }) // 结果回填表单 } catch (err) { wx.showToast({ title: err.message, icon: none }) } finally { this.setData({ recognizing: false }) } } })finally 里复位标志位是必须的否则失败一次后按钮就永久置灰。错误信息直接透传百度返回的 error_msg 对中文用户已经够友好不必自己再包一层。3.4 真机调试时用 charles 抓包确认请求报文微信小程序的请求走的是小程序容器开发者工具的 Network 面板看不到部分真机行为。遇到“开发工具正常、真机报错”时常见做法是用 charles 给手机配置证书并开启 SSL 解密过滤器里填 aip.baidubce.com直接看 POST 的 body 是否正确携带 image 字段、返回的 error_code 是什么。burpsuite 也能做同样的事只是手机端证书安装步骤略繁琐。这类抓包能看到一个容易忽略的问题小程序请求合法域名必须在 mp 后台配置没配 https://aip.baidubce.com 的话真机请求会在容器层被拦掉charles 里根本看不到这个请求。所以排错顺序是先确认域名白名单再抓包看报文。4. 身份证、车牌、驾驶证、行驶证四接口的参数差异与错误码处理4.1 身份证识别id_card_side 不传必报错身份证识别是四个接口里唯一一个必须额外传参的。front 返回姓名、性别、民族、出生、住址、公民身份号码back 返回签发机关和有效期限。不传 id_card_side接口直接返回错误而不是自动判断正反面。// 识别身份证正面 const frontResult await ocrRequest(idcard, imageBase64, { id_card_side: front, detect_risk: true, detect_photo: true, detect_direction: true }) // frontResult.words_result.公民身份号码detect_risk 开启后的返回里会带风险类型标记对复印件、翻拍、屏幕翻拍等场景能给出提示detect_photo 用于检测证件头像区域是否存在。这两个开关在实名认证类业务里是标配不识别真伪的业务可以关掉以省时。身份证号码字段名是“公民身份号码”不是“身份证号”解析表单时直接按中文键名取值即可这个键名不少新手会拼错。4.2 车牌号识别多车牌场景用 multi_detect 参数车牌识别的返回核心是 number 和 colornumber 是车牌号码字符串color 是蓝、黄、绿等枚举值。地下停车场那种同时出现多辆车的图片默认只识别一张需要开启 multi_detect 才会返回多组结果。// 识别车牌允许一张图里出现多张车牌 const plateResult await ocrRequest(license_plate, imageBase64, { multi_detect: true }) // plateResult.words_result 是数组每项含 number / color / location注意 multi_detect 开启后 words_result 从对象变成数组代码里要区分判断。倾斜车牌和夜间反光车牌是识别失败的重灾区前置步骤可以引导用户尽量正对车牌拍摄接口侧把 detect_direction 打开能改善一定程度的倾斜。另外新能源车牌是 8 位普通蓝牌是 7 位后面做校验时可以用这个规则兜底避免把 O 识别成 0 这类低级错误直接入库。4.3 驾驶证与行驶证接口路径别混返回字段也别交叉使用驾驶证和行驶证是两本不同的证接口也是两个driving_license 对应驾驶证vehicle_license 对应行驶证。把驾驶证图片传行驶证接口百度不会报错但识别出来的字段大概率是空的或者错位的因为两本证的版式差异很大。// 驾驶证识别 const dl await ocrRequest(driving_license, imageBase64, { accuracy: high }) // 驾驶证字段证号、姓名、性别、准驾车型、有效起始日期、有效期限 // 行驶证识别 const vl await ocrRequest(vehicle_license, imageBase64, { accuracy: high }) // 行驶证字段号牌号码、车辆类型、所有人、品牌型号、车辆识别代号、发动机号码、注册日期、发证日期两本证的字段对比可以放在一张表里方便查阅证件关键字段常见用途驾驶证证号、姓名、准驾车型、有效起始日期、有效期限租车资格核验、代驾接单行驶证号牌号码、车辆识别代号、发动机号码、注册日期车辆绑定、违章处理行驶证有主页和副页之分副页上的号牌号码、档案编号和主页不完全一致接口返回的是当前拍摄页面的字段。表单回填时要做空值兜底比如注册日期缺失时不要直接覆盖已有数据提示用户补拍另一页。accuracy 参数用 high 时识别更准但耗时长一点对证件类这种静态目标值得开。4.4 高频错误码速查与应对百度 OCR 的错误码体系是稳定的下面这张表是四个接口最常撞到的几类error_code含义处理方式110access_token 失效重新拉取并清理本地缓存17当日调用量超限检查配额或者换付费方案18QPS 超限请求加节流控制并发216200图片为空检查 base64 是否传空216201图片格式错误确认是 jpg/png/bmp 之一216202图片大小超限压缩后再传216630识别错误换更清晰的图片重试216631识别失败图片内容不清晰或未对准证件282000后端内部错误稍后重试一般是百度侧抖动遇到 216630 或 216631 时把原图保存下来人工复核比反复重试更有价值。识别接口本身不返回置信度所以用规则校验结果字段比指望接口给一个分数更可靠。免费额度相关错误码 17 和 18 出现时先确认是日配额还是并发配额日配额可以排队处理并发配额只能降速。5. 四类证照识别结果的规则校验与复用封装5.1 字段级规则校验拦住接口都放过的错误OCR 的识别错误集中在形近字数字 0 和字母 O1 和 I证号和车牌这类强格式字段可以用正则做一层硬校验。身份证是 18 位前 17 位数字最后一位可能是数字或 X车牌普通蓝牌 7 位、新能源 8 位第一位是省份汉字加字母。// utils/validate.js - 识别结果强校验 function isValidIdNumber(v) { return /^\d{17}[\dX]$/.test(String(v || ).toUpperCase()) } function isValidPlate(v) { const s String(v || ) // 新能源省份汉字字母6位数字D/F普通省份汉字字母5位数字字母 return /^[\u4e00-\u9fa5][A-Z][0-9A-Z]{6}$/.test(s) || /^[\u4e00-\u9fa5][A-Z][0-9A-Z]{5}[DF][0-9A-Z]?$/.test(s) }注意 OCR 返回的身份证号可能是小写 x转大写后再校验。日期字段出生日期、有效起始日期、注册日期按 YYYY-MM-DD 正则检查再交给 Date.parse 确认不是 2 月 30 日这类非法日期。这些校验不过时提示用户“识别内容可能有误请核对”比直接入库后让下游系统报错要体面得多。5.2 把整套能力收敛成一个小程序工具模块前面分散的选图、请求、校验代码最后可以并成一个模块业务页只需要引入一个函数// utils/vehicleOcr.js - 四证识别统一入口 const { chooseAndCompress } require(./image) const { ocrRequest } require(./ocr) const { isValidIdNumber, isValidPlate } require(./validate) async function recognizeCard(type, side) { const imageBase64 await chooseAndCompress(85) const params { detect_direction: true } if (type idcard) params.id_card_side side if (type license_plate) params.multi_detect true if (type driving_license || type vehicle_license) params.accuracy high const result await ocrRequest(type, imageBase64, params) return validateByType(type, result) }validateByType 里按类型对识别结果做字段级校验通过的才返回给页面。这样页面层不用关心接口差异也方便后续把直连换成云函数调用只要 ocrRequest 内部实现替换业务代码不动。整个四证识别的复杂度被压在这三个模块内排查问题时只需要看对应模块。以当前模块的结构新增银行卡识别时只需要复制一个类型分支把 apiPath 换成 bankcard其余链路不动。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门