内容简介:之前有写过关于puppeteer的相关文章
之前有写过关于puppeteer的相关文章
前一段时间,LZ又接到一个需求,要爬取某快递公司网站的订单数据,起初觉得不就是爬一下数据嘛,虽然nodejs玩的不是特别溜,但爬一些简单数据还是难不倒我这种战五渣的。
当我打开网站,输入数据,准备来一波页面结构分析的时候,突然间跳出来一个滑块验证码。卧槽......
WTF,你让我爬个鸟啊.....
盯着滑块验证码瞅了两天,终于我得出一个结论 滑块验证码阻止了人类文明的进步
!
每天早上产品
笑眯眯来问我进度的时候,我的内心都是崩溃的
难受归难受,但业务还是要做的。最后,我想到了之前用puppeteer开发的模拟cas(单点登录)来解决我司某些应用在开发、测试环境自动登录的功能。现在我就以一种情况为例,来看下怎么用node+puppeteer高效的破解滑块验证码。
之前有一兄弟在掘金上写过 用puppeteer破解滑块验证码 , 接下来我们就用一些另外的思路去破解
这里我们也以前端网为例:
const puppeteer = require("puppeteer"); const fs = require("fs"); const path = require("path"); const pixels = require("image-pixels"); const resemble = require("resemblejs"); let page = null; const bgImg = path.resolve(__dirname, "bg.png"); const fullbgImg = path.resolve(__dirname, "fullbg.png"); async function run() { const browser = await puppeteer.launch({ headless: false }); page = await browser.newPage(); // 打开前端网 await page.goto("https://www.qdfuns.com/"); await page.waitForSelector(".hand"); await page.click("a[data-type=login]"); const geetest_btn = ".geetest_btn"; await page.waitForSelector(geetest_btn); await page.click(geetest_btn); await page.waitFor(1000); // 获取滑动距离 async function getDistance() { // 获取canvas let { bg, fullbg } = await page.evaluate(() => { const fullbg = document.querySelector(".geetest_canvas_fullbg"); const bg = document.querySelector(".geetest_canvas_bg"); return { bg: bg.toDataURL(), fullbg: fullbg.toDataURL() }; }); bg = bg.replace(/^data:image\/\w+;base64,/, ""); fullbg = fullbg.replace(/^data:image\/\w+;base64,/, ""); var bgDataBuffer = new Buffer(bg, "base64"); var fullbgDataBuffer = new Buffer(fullbg, "base64"); fs.writeFileSync(bgImg, bgDataBuffer); fs.writeFileSync(fullbgImg, fullbgDataBuffer); // 通过resemble比较背景图和缺口图的不同 resemble(bgImg) .compareTo(fullbgImg) .ignoreColors() .onComplete(async function(data) { fs.writeFileSync(path.resolve(__dirname, `diff.png`), data.getBuffer()); }); var { data } = await pixels(path.resolve(__dirname, `diff.png`), { cache: false }); // 获取缺口距离左边的做小位置,即计为需要滑动的距离 let arr = []; for (let i = 10; i < 150; i++) { for (let j = 80; j < 220; j++) { var p = 260 * i + j; p = p << 2; if (data[p] === 255 && data[p + 1] === 0 && data[p + 2] === 255) { arr.push(j); break; } } } return Math.min(...arr); } const distance = await getDistance(); const button = await page.$(".geetest_slider_button"); const box = await button.boundingBox(); const axleX = Math.floor(box.x + box.width / 2); const axleY = Math.floor(box.y + box.height / 2); await btnSlider(distance); // 滑动滑块 async function btnSlider(distance) { await page.mouse.move(axleX, axleY); await page.mouse.down(); await page.waitFor(200); await page.mouse.move(box.x + distance / 4, axleY, { steps: 20 }); await page.waitFor(200); await page.mouse.move(box.x + distance / 3, axleY, { steps: 18 }); await page.waitFor(350); await page.mouse.move(box.x + distance / 2, axleY, { steps: 15 }); await page.waitFor(400); await page.mouse.move(box.x + (distance / 3) * 2, axleY, { steps: 15 }); await page.waitFor(350); await page.mouse.move(box.x + (distance / 4) * 3, axleY, { steps: 10 }); await page.waitFor(350); await page.mouse.move(box.x + distance + 30, axleY, { steps: 10 }); await page.waitFor(300); await page.mouse.up(); await page.waitFor(1000); const text = await page.evaluate(() => { return document.querySelector(".geetest_result_box").innerText; }); console.log(text); let step = 0; if (text) { // 如果失败重新获取滑块 if ( text.includes("怪物吃了拼图") || text.includes("拖动滑块将悬浮图像正确拼合") ) { await page.waitFor(2000); await page.click(".geetest_refresh_1"); await page.waitFor(1000); step = await getDistance(); await btnSlider(step); } else if (text.includes("速度超过")) { console.log("success"); } } } } run(); 复制代码
执行该程序,控制台输出如下(运气好的话,可能一次就过了,具体要看中间的处理过程怎么优化求解)
这里面,需要注意以下几点
缺口图存在干扰缺口图,resemble在比对的时候需要会得到两个缺口,这里目前没有一个很好的办法来确定到底哪个缺口是我们所需要的(下面我们会提到一个针对该问题的方法来避免该干扰项) 滑动的时候需要控制下滑动速度,具体怎么个滑动法,那就仁者见仁智者见智了
你以为这样就结束了
很多情况下滑块验证码并不会给我们完整的背景图,这时候我们该怎么有效的去定位缺口呢,在这里我们可以使用 gm 把我们的背景图片模糊以下,然后在用 resemblejs
去比对下两个图片,但是此时图片会有很多地方比对出不同,此时我们可以获取到小滑块图片距离父辈元素的位置,借此来减少像素比对范围(这可以有效解决我们上面所提到的避免干扰项问题)
const puppeteer = require("puppeteer"); const fs = require("fs"); const path = require("path"); const pixels = require("image-pixels"); const resemble = require("resemblejs"); const gm = require("gm"); let page = null; const bgImg = path.resolve(__dirname, "bg.png"); const bgBlurImg = path.resolve(__dirname, "bgBlur.png"); const bgDiffImg = path.resolve(__dirname, "bgDiff.png"); async function run() { const browser = await puppeteer.launch({ headless: false }); page = await browser.newPage(); await page.goto( "https://x.tongdun.cn/onlineExperience/slidingPuzzle?source=baidu&plan=%E5%8F%8D%E6%AC%BA%E8%AF%88&unit=%E6%99%BA%E8%83%BD%E9%AA%8C%E8%AF%81&keyword=%E6%99%BA%E8%83%BD%E9%AA%8C%E8%AF%81%E7%A0%81&e_creative=24659987438&e_adposition=cl1&e_keywordid=101045415224&e_keywordid2=101045415224&audience=236369" ); await page.waitForSelector("#loginBtn"); await page.click("#loginBtn"); const slidetrigger = ".td-pop-slidetrigger"; await page.waitForSelector(slidetrigger); await page.click(slidetrigger); await page.waitFor(1000); const slideIdentity = ".td-pop-slide-identity"; await page.waitFor(slideIdentity); // 获取小滑块的top值,来减少比对范围 const top = await page.evaluate(() => { const identity = document.querySelector(".td-pop-slide-identity"); return identity.offsetTop; }); async function getDistance() { // 获取缺口图片 let { bg } = await page.evaluate(() => { const bg = document.querySelector(".td-bg-img"); return { bg: bg.toDataURL() }; }); bg = bg.replace(/^data:image\/\w+;base64,/, ""); var bgDataBuffer = new Buffer(bg, "base64"); fs.writeFileSync(bgImg, bgDataBuffer); // 图片模糊 gm(bgImg) .blur(1) .write(bgBlurImg, function(err) { if (!err) console.log("done"); }); // 图片对比 resemble(bgImg) .compareTo(bgBlurImg) .ignoreColors() .onComplete(async function(data) { fs.writeFileSync(bgDiffImg, data.getBuffer()); }); var { data } = await pixels(bgDiffImg, { cache: false }); let arr = []; // 比对范围内的像素点 for (let i = top; i < top + 44; i++) { for (let j = 60; j < 320; j++) { var p = 320 * i + j; p = p << 2; if (data[p] === 255 && data[p + 1] === 0 && data[p + 2] === 255) { arr.push(j); break; } } } const { maxStr } = getMoreNum(arr); return Number(maxStr); } const distance = await getDistance(); const button = await page.$(slidetrigger); const box = await button.boundingBox(); const axleX = Math.floor(box.x + box.width / 2); const axleY = Math.floor(box.y + box.height / 2); console.log(distance, "distance"); console.log(box.x + distance); await btnSlider(distance); async function btnSlider(distance) { await page.mouse.move(axleX, axleY); await page.mouse.down(); await page.waitFor(200); await page.mouse.move(box.x + distance / 4, axleY, { steps: 20 }); await page.waitFor(200); await page.mouse.move(box.x + distance / 3, axleY, { steps: 18 }); await page.waitFor(350); await page.mouse.move(box.x + distance / 2, axleY, { steps: 15 }); await page.waitFor(400); await page.mouse.move(box.x + (distance / 3) * 2, axleY, { steps: 15 }); await page.waitFor(350); await page.mouse.move(box.x + (distance / 4) * 3, axleY, { steps: 10 }); await page.waitFor(350); await page.mouse.move(box.x + distance + 20, axleY, { steps: 10 }); await page.waitFor(300); await page.mouse.up(); await page.waitFor(1000); } } run(); function getMoreNum(arr) { var obj = {}; var arr1 = []; for (var i = 0; i < arr.length; i++) { if (arr1.indexOf(arr[i]) == -1) { obj[arr[i]] = 1; arr1.push(arr[i]); } else { obj[arr[i]]++; } } var max = 0; var maxStr; for (var i in obj) { if (max < obj[i]) { max = obj[i]; maxStr = i; } } return { max, maxStr }; } 复制代码
该示例没添加错误之后重滑逻辑
此种方法存在的问题
-
自己模糊化背景图片就行像素比较,成功率较低,需优化(亦可以通过比对的图片通过其灰度值来锁定区域)
以上,我们介绍了两种方法来破解解决滑块验证码。此外,LZ还尝试了使用图片二值化方法来进行图片缺口的定位,该方法的成功率远高于第二种方法,具体实现方法就不写了,读者可以自行探索哈。
示例代码均可在 github 查看
以上所述就是小编给大家介绍的《使用node+puppeteer破解验证码》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 小学数学破解滑动拼图验证码
- 92、破解极验滑动验证码
- Frida应用基础及APP https证书验证破解
- [深度学习] 使用Darknet YOLO 模型破解中文验证码点击识别
- 谷歌 ReCaptcha 系统被破解,机器语音验证准确率高达 85%
- BUF早餐铺 | 大疆回应美国土安全部数据质疑,安全性经全球验证;John the Ripper 支持 FPGA 破解密...
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。