經常刷網頁版的抖音,但隔一段時間就會出現驗證碼,有滑塊和文字點選。今天嘗試開發一個自動完成驗證的插件,但遇到了跨域圖片不能直接獲得base64數據的問題。
準備插件工具: https://pan.baidu.com/s/1Dok2WKq15bnHyIVRAGDqNg?pwd=pve2 提取碼: pve2
工具使用說明:
post方式向http://127.0.0.1:2000提交數據 {type:"ocrCode",img:圖形base64} 識別文字字母等 {type:"detection",img:圖形base64} 識別點選文字范圍 {type:"slide",targetImg:滑塊base64,backgroundImg:背景base64} 識別滑塊 {type:"clickImage",key:"窗口關鍵字",img:按鈕base64} 點擊圖形按鈕 {type:"passKey",key:按鍵} 模擬鍵盤按鍵 {type:"activityWindow",window:"要激活的窗口標題關鍵字",key:按鍵【可選】} 激活窗口并按鍵 {type:"click"} 點擊鼠標 {type:"clickPoint",x:X坐標,y:Y坐標} 指定坐標點擊鼠標 {type:"move",x:X坐標,y:Y坐標} 移動鼠標 {type:"moveAndClick",x:X坐標,y:Y坐標} 移動鼠標到指定坐標再點擊 {type:"write",text:"要輸入的內容"} 模擬鍵盤輸入文字
第一種:通常獲取圖片數據直接用canvas drawImage簡單直接快速。
但在部分網站中是禁止使用canvas的,所以我就用
第二種: 使用 XMLHttpRequest異步(注意只能異步)讀取blob數據轉base64。
但如果圖片是跨域的,第一、二種都會失效
第三種(支持跨域): 在瀏覽器插件的background.js
中使用 XMLHttpRequest
讀取圖片然后傳給inject_script.js
。也可以用于其它數據的讀取。
以下代碼示例,過抖音驗證(文字點選類識別率較低,需要訓練模型然后更新到識別程序中才能完善)。
//識別驗證碼 function ocrFormApi(_data) { var _code = "本地未開啟P娃兒貓驗證碼識別工具的web服務。"; try { console.log("提交后臺識別中..."); $.ajax({ type: "POST", url: "http://127.0.0.1:2000/", timeout: 2 * 1000, async: false, data: _data, success: function (data) { _code = data; }, error: function (_d) { _code = "識別錯誤"; } }); } catch (e) { } return _code; } //滑塊操作(滑塊元素ID,要滑動的距離) function mockVerify(_imgId, _distance) { var btn = document.querySelector("#" + _imgId); var mousedown = document.createEvent('MouseEvents'); var rect = btn.getBoundingClientRect(); var x = rect.x; var y = rect.y; mousedown.initMouseEvent('mousedown', true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); btn.dispatchEvent(mousedown); var dx = 0; var dy = 0; var interval = setInterval(function () { var mousemove = document.createEvent('MouseEvents'); var _x = x + dx; var _y = y + dy; mousemove.initMouseEvent('mousemove', true, true, window, 0, _x, _y, _x, _y, false, false, false, false, 0, null); btn.dispatchEvent(mousemove); console.log("MOVE"); btn.dispatchEvent(mousemove); if (_x - x >= _distance) { clearInterval(interval); var mouseup = document.createEvent('MouseEvents'); mouseup.initMouseEvent('mouseup', true, true, window, 0, _x, _y, _x, _y, false, false, false, false, 0, null); btn.dispatchEvent(mouseup); console.log("END"); } else { dx += Math.ceil(Math.random() * 50); } }, 60); } //傳入圖像元素通過canvas轉換 function getImgBase64(_img) { var canvas = document.createElement("canvas"); canvas.width = _img.naturalWidth; canvas.height = _img.naturalHeight; var ctx = canvas.getContext("2d"); ctx.drawImage(_img, 0, 0); return canvas.toDataURL("image/png").replace("data:image/png;base64,", ""); } //傳入圖片地址(不能跨域),通過blob轉換(只能異步) function getImgBase64ByUrl(_url, _success) { var xhr = new XMLHttpRequest(); xhr.responseType = "blob"; xhr.open("POST", _url, true); xhr.onload = function (data, textStatus, request) {// 請求完成處理函數 if (this.status === 200) { var _blob = this.response;// 獲取返回值 let _f = new FileReader(); _f.onload = function (_e) { console.log(_e.target.result); _success(_e.target.result); } _f.readAsDataURL(_blob); } else { console.log(this.status); } }; xhr.send(); } function checkCodeOut() { if ($("#captcha-verify-image:visible").length) { //獲取跨域圖片數據(把 getImgBase64ByUrl 中的方法放到插件的background中,因為background沒有跨域問題) window.sendMessage({ type: "getDataFromUrl", data: { url: $("#captcha-verify-image:visible").attr("src"), blob: 1, type: "GET" } }, function (_base64) { var _backgroundImg = _base64.replace("data:image/jpeg;base64,", ""); //點選文字 if ($("#verify-bar-code:visible").length) { window.sendMessage({ type: "getDataFromUrl", data: { url: $("#verify-bar-code:visible").attr("src"), blob: 1, type: "GET" } }, function (_base64) { var _targetImg = _base64.replace("data:image/jpeg;base64,", ""); //識別出要求點選的文字 var _data = ocrFormApi({ type: "ocrCode", img: _targetImg }); //_data = JSON.parse(_data); var _txt = _data.code; //識別背景圖上的文字及對應坐標 _data = ocrFormApi({ type: "detection", img: _backgroundImg }); console.log(_data) _data = _data.code; for (var _i = 0; _i < _txt.length; _i++) { if (_data[_txt.charAt(_i)]) { console.log("“" + _txt.charAt(_i) + "”字點擊范圍:", _data[_txt.charAt(_i)]) } else { console.log("“" + _txt.charAt(_i) + "”字范圍未能識別成功,請刷新。"); $(".secsdk_captcha_refresh--text").click(); } } }); } //滑塊 if ($(".captcha_verify_img_slide:visible").length) { window.sendMessage({ type: "getDataFromUrl", data: { url: $(".captcha_verify_img_slide:visible").attr("src"), blob: 1, type: "GET" } }, function (_base64) { var _targetImg = _base64.replace("data:image/jpeg;base64,", ""); var _data = ocrFormApi("slide", { type: "slide", targetImg: _targetImg, backgroundImg: _backgroundImg }); console.log("滑塊需要操作的數據:", _data); $(".captcha_verify_img_slide:visible").attr("id", "52pj_slide"); mockVerify("52pj_slide", _data.x); }); } }); return; } setTimeout(checkCodeOut, 1000); } function init() { var _l = location.href; if (_l.indexOf("www.douyin.com") != -1) { checkCodeOut(); return; } return; } if (typeof $ == 'undefined') { var s = document.createElement("script"); s.onload = init; s.src = "https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"; document.getElementsByTagName("HEAD")[0].appendChild(s); } else { init(); }