。。。。最一开始我只是想弄个帮女友自动清购物车的脚本顺带尝试一下puppeteer,but噩梦开始了javascript
文档在这里
对我这种英语差的人实在是心累,好在api简洁明了。。。虽然有些地方还不完善,不要紧干的就是盲人摸象的工做。
入门教程网上仍是不少的我随便贴一个Puppeteer 与 Chrome Headless —— 从入门到爬虫html
而后是打开淘宝的基础代码(太简单了,没啥要看的)java
puppeteer.launch({
headless: false
}).then(async browser => {
const page = await browser.newPage();
// 设置Viewport
await page.setViewport({
width: 1366,
height: 768
});
await page.goto('https://login.taobao.com/member/login.jhtml');
console.log('进入淘宝登录页');
});复制代码
一开始我觉的扫码多麻烦,还要把图片弄到本地啥的,果断用帐号+密码+验证码来撸,git
puppeteer.launch({
headless: false
}).then(async browser => {
...这里代码就不重复了
await page.waitForSelector('#J_Quick2Static');
await page.click('#J_Quick2Static');
console.log('切换至普通模式登录');
// 判断一下登录帐号密码是否存在,不存在就输入
loginData = Object.keys(loginData).length ? loginData : await inputLoginData();
console.log('成功获取用户帐号密码,开始输入');
await sleep(1000);
const usernameInput = await page.$('input[name=TPL_username]');
await usernameInput.click();
await usernameInput.type(loginData.username, {
delay: 50
});
密码也是这样
console.log('输入完毕');
const loginButton = await page.$('#J_SubmitStatic');
await loginButton.click();
console.log('登录成功');
});复制代码
// 用户输入帐号密码的函数,能够不用看
const inputLoginData = async () => {
const result = {};
await readSyncByRl('输入淘宝帐号: ').then((msg) => {
result.username = msg;
});
await readSyncByRl('输入淘宝密码: ').then((msg) => {
result.password = msg;
});
return result;
}
function readSyncByRl(tips) {
tips = tips || '> ';
return new Promise((resolve) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question(tips, (answer) => {
rl.close();
resolve(answer.trim());
});
});
}复制代码
好,这样实际上是能够了。。。。可是有几个问题:github
输入文字不能太快,这个其实调整delay
在必定程度上能够解决chrome
我一开始以为只要拖过去就能够了= =,事实证实我太天真,网上找找看到了这传送门,大概就是说尽可能模拟人的操做。。。。而后我尝试了canvas
await page.$('#nc_1_n1z').then(async (element) => {
const point = await element.boundingBox();
console.log('进行滑动验证');
await moveSlide(point, page);
}).catch(() => {
console.log('无滑动验证码');
});复制代码
// 滑动验证
async function moveSlide(point, page) {
await page.waitForSelector('#nc_1_n1z', {
visible: true,
});
await sleep(1000);
// 果真高中物理都还给老师了,下面模拟一个16加速度,时长为2的滑动
const mouse = page.mouse;
const x = point.x + Math.round(Math.random() * point.width / 4) + point.width / 3;
const y = point.y + Math.round(Math.random() * point.height / 4) + point.height / 3;
await mouse.move(x, y);
await mouse.down();
await sleep(200);
for (let i = 2; i > 0; i--) {
await mouse.move(x + 16 * i ** 2, y + 0.2 * i ** 2, {
steps: 2,
});
}
for (let i = 8; i > 0; i--) {
await mouse.move(x + 64 + 32 * i, y + 0.2 * i ** 2, {
steps: 2,
});
}
await mouse.up();
// 判断验证s是否经过
let ncResult = await Promise.race([
page.waitForSelector('#nocaptcha .btn_ok', {
visible: true,
}).then(() => {
return true;
}),
page.waitForSelector('#nocaptcha', {
visible: true,
}).then(() => {
return false;
}),
]);
// 失败从新滑动
if (!ncResult) {
await page.waitForSelector('#nocaptcha a', {
visible: true,
});
await sleep(1000);
const nocaptcha = await page.$('#nocaptcha a');
await nocaptcha.click();
await sleep(1000);
await moveSlide(point, page);
}
}复制代码
原本这样有3成的几率能过。。。。可是一段时间后,我发现一直都是失败,我就人工试了试,发现个人帐号只要出现滑动严重人工都不能验证经过,否则就是直接能够登录进去(T_T)
这条路就此断绝,可是我尚未放弃api
嘛。。。扫就扫。。。浏览器
本来是想直接保存二维码到本地而后打开图片扫码less
await page.waitForSelector('#J_QRCodeImg');
await page.$('#J_QRCodeImg').then(async (element) => {
const point = await element.boundingBox();
await page.screenshot({
path: "code.png",
clip: point,
});
});复制代码
想一想太很差了,显得太傻。。。忽然想到了把图片转成ascii字符图,还挺好的,就只有黑白都不用作灰度处理。
let img = new Image();
var result = '';
img.src = document.getElementById('J_QRCodeImg').childNodes[0].src + '?t=123';
img.crossOrigin = "Anonymous";
img.onload = async () => {
let canvas = document.createElement('canvas');
let canvasContext = canvas.getContext("2d");
canvasContext.drawImage(img, 0, 0);
let data = [];
for (let h = 0; h < img.height; h += 2) {
for (let w = 0; w < img.width; w += 2) {
let imgData = canvasContext.getImageData(w, h, 2, 2);
let imgDataArray = imgData.data;
data.push(imgDataArray.reduce((sum, value) => {
return sum + value;
}) / (2*2*4));
}
}
let arr = ['██', ' '];
data.forEach((item, index) => {
result += arr[Math.floor(item / 157)];
if ((index + 1) % 70 == 0) {
result += '\n';
}
})
}复制代码
怎么执行呢?
executionContext这个就是exec在浏览器内部的对象
const qrcode = await executionContext.evaluate(async () => {
上文内容
// 让程序停2S防止返回空字符串
await sleep(2000);
return Promise.resolve(result);
async function sleep(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(1)
} catch (e) {
reject(0)
}
}, delay);
});
};
});复制代码
其实乍一看效果仍是不错的,就是有点大,在chrome上这个特殊字符宽高比是1:2
可是在cmd上就不是这回事了
在cmd中宽高是1:1因此
// 不适用
// let arr = ['██', ' '];
let arr = ['█', ' '];复制代码
这个问题实际上是字符集对于这个特殊字符的定义不一样,这个就很难办了。。。。。总不能让别人都用一种字体
咳咳。。。你们都来想一想办法,还有什么好方法,请在下面留言