使用百度AI接口OCR自动识别验证码


使用百度 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。

注意

  1. 图片的 base64 编码是不包含图片头的,如(data:image/jpg;base64,)
  2. 使用 Postman 工具或 Python、PHP 等请求库会自动进行 urlencode,无需自行处理。

请求格式支持:PNG、JPG、JPEG、BMP、TIFF、PNM、WebP

接口名称 图片编码后大小限额
百度文字识别所有接口的图像大小限制 base64 编码 urlencode 后大小不超过 4M,最短边至少 15px,最长边最大 4096px

获取验证码 BASE64 文件及校验逻辑

  1. 获取验证码 html 文件
  2. 获取验证码图片元素
  3. 添加图片 onload 事件监听
  4. 添加元素到 DOM 渲染
  5. 获取图片 BASE64 文件
  6. 传输文件到 OCR 接口
  7. 获取验证码结果
  8. 发送 post 请求传输验证码
  9. 校验结果
    1. 真则等待一小时再次请求
    2. 否则重试
/**
 * 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();
    });
}

文章作者: iKnow
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 iKnow !