破解哔哩哔哩登录滑动验证码

现在越来越多的平台登录增加了滑动验证码.这给我们做自动化爬虫带来了一些阻碍.最近在研究怎么破解这些验证码.发现了puppeteer这个神器.跟selenium类似.不过我个人更看好puppeteer.今天就网上查看各位大神的文章.学习了破解B站验证码.
本文主要参考了https://juejin.im/post/5c343f4ff265da611639f5bb的文章.然后在这个基础上针对b站做了滑动验证码的优化

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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: true,
// headless: false,
// defaultViewport: { width: 1366, height: 768 }
});
page = await browser.newPage();
let timeAdd = Math.ceil(Math.random()*20);
// 打开bilibili
await page.goto("https://passport.bilibili.com/login");

//开始输入账号密码
await page.waitFor(2000+timeAdd)
page.focus('#login-username'); // 先获取焦点
await page.waitFor(300+timeAdd)
page.type('#login-username', "***",50); // 输入变慢,像一个用户
await page.waitFor(500+timeAdd)
page.type('#login-passwd', "***",50); // 输入变慢,像一个用户
await page.waitFor(300+timeAdd)
page.click('.btn-login')
await page.waitFor(2000+timeAdd)
// 获取滑动距离
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+timeAdd });
// await page.waitFor(200);
// await page.mouse.move(box.x + distance / 3, axleY, { steps: 18+timeAdd });
// await page.waitFor(350);
// await page.mouse.move(box.x + distance / 2, axleY+timeAdd, { steps: 15+timeAdd });
// await page.waitFor(400);
// await page.mouse.move(box.x + (distance / 3) * 2, axleY, { steps: 45 });
// await page.waitFor(350);
await page.mouse.move(box.x + (distance / 4) * 3, axleY+timeAdd, { steps: 10+timeAdd });
await page.waitFor(250+timeAdd);
await page.mouse.move(box.x + (distance / 5) * 4, axleY+timeAdd, { steps: 7+timeAdd });
await page.waitFor(350);
await page.mouse.move(box.x + distance+20, axleY, { steps: 5+timeAdd });
await page.waitFor(300+timeAdd);
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");
}
await browser.close();
}
}
}
run();

运行

1
2
3
4
npm i puppeteer
npm i image-pixels
npm i resemblejs
node 文件.js