使用百度 AI 接口 OCR 自动识别验证码
百度 AI OCR 接口的使用
https://cloud.baidu.com/doc/OCR/s/Ck3h7y2ia
请求格式
POST 方式调用
注意:Content-Type 为application/x-www-form-urlencoded
,然后通过urlencode
格式化请求体。
返回格式
JSON 格式
请求限制
请求图片需经过base64编码
及urlencode
后传入:图片的 base64 编码指将一副图片数据编码成一串字符串,使用该字符串代替图像地址。您可以首先得到图片的二进制,然后去掉编码头后再进行 urlencode。
注意:
- 图片的 base64 编码是不包含图片头的,如
(data:image/jpg;base64,)
; - 使用 Postman 工具或 Python、PHP 等请求库会自动进行 urlencode,无需自行处理。
请求格式支持:PNG、JPG、JPEG、BMP、TIFF、PNM、WebP
接口名称 | 图片编码后大小限额 |
---|---|
百度文字识别所有接口的图像大小限制 | base64 编码 urlencode 后大小不超过 4M,最短边至少 15px,最长边最大 4096px |
获取验证码 BASE64 文件及校验逻辑
- 获取验证码 html 文件
- 获取验证码图片元素
- 添加图片 onload 事件监听
- 添加元素到 DOM 渲染
- 获取图片 BASE64 文件
- 传输文件到 OCR 接口
- 获取验证码结果
- 发送 post 请求传输验证码
- 校验结果
- 真则等待一小时再次请求
- 否则重试
/**
* OCR
*/
async function captcha() {
return new Promise(async (resolve, reject) => {
const user = getUserFromName();
const type = 'application/x-www-form-urlencoded';
const url = `${user.votedUrl}id=topthreads:setstatus&tid=${user.freeTid}&handlekey=k_setstatus&infloat=1&freeon=yes&inajax=1`;
const captchaPage = await postData(url, urlSearchParams({ captcha_input: '' }).toString(), type)
.then(res => turnCdata(res.responseXML))
.catch(e => {
console.log(e);
});
if (!captchaPage) {
new MessageBox('访问失败,正在重试...');
reject();
return;
} else if (captchaPage === 'Access denied.') {
new MessageBox(captchaPage);
return;
} else if (typeof captchaPage !== 'object') {
new MessageBox('访问失败,正在重试...');
reject();
return;
}
const image = captchaPage.querySelector('#captcha');
document.body.append(image);
image.onload = async function () {
//文件的Base64字符串
const base64 = getBase64Image(image);
const ma = await readImage(base64).catch(e => {
console.log(e);
});
if (ma.includes('Access token invalid or no longer valid')) {
new MessageBox(
'Access token invalid or no longer valid. 令牌无效(需要令牌请私聊 or 发送邮件到 kished@outlook.com )',
10000
);
user.token = '';
GM_setValue(user.username, user);
reject(ma);
return;
}
const result = await postData(url, urlSearchParams({ captcha_input: ma }).toString(), type)
.then(res => turnCdata(res.responseXML))
.catch(e => {
console.log(e);
});
if (result === '更新完成!若狀態仍沒更新,請嘗試刷新頁面') {
new MessageBox('更新完成!自動‘現在有空’中,請不要刷新頁面!', user.freeTime);
resolve(result);
} else {
new MessageBox('验证失败,正在重试...');
reject(result);
}
};
});
}
/**
* 图像转Base64
*/
function getBase64Image(img) {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.height);
const ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
const dataURL = canvas.toDataURL('image/' + ext);
return dataURL;
}
主要代码
https://github.com/Eished/jkforum_helper
/**
* OCR
*/
async function captcha() {
return new Promise(async (resolve, reject) => {
const user = getUserFromName();
const type = 'application/x-www-form-urlencoded';
const url = `${user.votedUrl}id=topthreads:setstatus&tid=${user.freeTid}&handlekey=k_setstatus&infloat=1&freeon=yes&inajax=1`;
const captchaPage = await postData(url, urlSearchParams({ captcha_input: '' }).toString(), type)
.then(res => turnCdata(res.responseXML))
.catch(e => {
console.log(e);
});
if (!captchaPage) {
new MessageBox('访问失败,正在重试...');
reject();
return;
} else if (captchaPage === 'Access denied.') {
new MessageBox(captchaPage);
return;
} else if (typeof captchaPage !== 'object') {
new MessageBox('访问失败,正在重试...');
reject();
return;
}
const image = captchaPage.querySelector('#captcha');
document.body.append(image);
image.onload = async function () {
//文件的Base64字符串
const base64 = getBase64Image(image);
const ma = await readImage(base64).catch(e => {
console.log(e);
});
if (ma.includes('Access token invalid or no longer valid')) {
new MessageBox(
'Access token invalid or no longer valid. 令牌无效(需要令牌请私聊 or 发送邮件到 kished@outlook.com )',
10000
);
user.token = '';
GM_setValue(user.username, user);
reject(ma);
return;
}
const result = await postData(url, urlSearchParams({ captcha_input: ma }).toString(), type)
.then(res => turnCdata(res.responseXML))
.catch(e => {
console.log(e);
});
if (result === '更新完成!若狀態仍沒更新,請嘗試刷新頁面') {
new MessageBox('更新完成!自動‘現在有空’中,請不要刷新頁面!', user.freeTime);
resolve(result);
} else {
new MessageBox('验证失败,正在重试...');
reject(result);
}
};
});
}
/**
* 图像转Base64
*/
function getBase64Image(img) {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.height);
const ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
const dataURL = canvas.toDataURL('image/' + ext);
return dataURL;
}
/**
*Base64字符串转二进制
*/
function dataURLtoBlob(dataurl) {
const arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime,
});
}
async function readImage(base64) {
const user = getUserFromName();
const url = `${user.OCRUrl}access_token=${user.token}&Content-Type=application/x-www-form-urlencoded`;
const body = urlSearchParams({ image: base64 }).toString();
return postData(url, body)
.then(res => {
const code = JSON.parse(res.responseText);
if (code?.words_result) {
return code.words_result[0].words;
}
return code.error_msg;
})
.catch(e => {
console.log(e);
});
}
function postData(url, postData, type = 'document', usermethod = 'POST') {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: usermethod,
url: url,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
data: postData,
responseType: type,
timeout: 1 * 60 * 1000,
onload: function (response) {
if (response.status == 200) {
resolve(response);
} else {
reject(response.status);
}
},
onerror: function (error) {
reject(error);
},
ontimeout: () => {
reject('timeout');
},
});
});
}
async function autoCompleteCaptcha() {
const user = getUserFromName();
if (!user.token) {
user.token = prompt('请输入验证码识别的 api 令牌(需要令牌请私聊 or 发送邮件到 kished@outlook.com ):');
const reg = /.*\..*\..*\..*/g;
if (reg.test(user.token)) {
GM_setValue(user.username, user);
} else {
new MessageBox('无效的令牌');
return;
}
}
if (!user.freeTid) {
const status = document.querySelector('#topthread_status');
if (status) {
let tid = location.href.split('-')[1]; // 不同链接地址不同
if (!tid) {
tid = new URLSearchParams(location.href).get('tid'); // 用于获取分类贴链接下的 tid
}
console.log(tid);
user.freeTid = tid;
GM_setValue(user.username, user);
} else {
new MessageBox('找不到指定页面元素!请先打开自己的帖子再试');
return;
}
}
captcha()
.then(res => {
const msId = new MessageBox();
playVideo(msId);
setTimeout(() => {
msId.removeMessage();
autoCompleteCaptcha();
}, user.freeTime);
})
.catch(e => {
// console.log(e);
autoCompleteCaptcha();
});
}