需求分析
別人分享了很多網(wǎng)盤鏈接栋烤,自己每個手動去轉(zhuǎn)存很浪費時間秘通,而且,這些操作都是重復(fù)性勞動澡绩。與Pandownload的這個功能類似列疗,不過pandownload由于一些原因無法使用了滑蚯,所以只能自己實現(xiàn)。
思路
思路其實很簡單抵栈,就是完全模擬人為操作告材,將網(wǎng)盤鏈接存起來。我們可以把網(wǎng)盤鏈接分為兩種古劲,第一種是沒有提取碼的斥赋,第二種是有提取碼的。前者比后者少一個提交提取碼的步驟产艾。那么疤剑,我們
如何區(qū)分,訪問一個網(wǎng)盤鏈接的時候闷堡,究竟是哪種呢隘膘?
可以通過訪問頁面的title來決定。
這種是無提取碼的杠览,后綴是無限制
這種是需要填寫提取碼的弯菊,后綴是請輸入提取碼,寫代碼的時候踱阿,截取后三個字兒提取碼即可
填寫Cookie信息
批量轉(zhuǎn)存的前提是管钳,我們得登錄,我這里實現(xiàn)的方案是直接從瀏覽器中獲取到cookie信息软舌,復(fù)制到代碼中進行使用蹋嵌,查看網(wǎng)站的cookie信息可以用插件EditThisCookie
點擊右上角導(dǎo)出Cookie,cookie就放在粘貼板中了葫隙,復(fù)制到代碼中,然后使用循環(huán)躏仇,將所有cookie利用page.setCookie方法恋脚,將cookie放置到當前請求上下文中
填寫提取碼
等待頁面加載完畢,找到輸入提取碼框的元素焰手,找到其id糟描,然后使用page.$$eval()方法獲取并寫入東西
可以看見,這里的id為accessCode书妻。所以我們只需要寫
await page.$$eval('#accessCode', writeCode, code);
async function writeCode(nodes, code) {
for (let node of nodes) {
node.value = code;
}
}
就可以了船响。我這里是直接改的node.value,你也可以用puppeteer模擬人來填寫。
提取文件
找到提取文件按鈕见间,點擊聊闯,然后等待跳轉(zhuǎn)完畢。
值得注意的是米诉,我們點擊的一定是A標簽菱蔬,其他標簽需要進行過濾。
await page.$$eval('.g-button-blue-large', click);
await page.waitFor(1000);
async function click(nodes) {
for (let node of nodes) {
if (node.title === '提取文件') {
await node.click();
}
}
}
然后等待跳轉(zhuǎn)即可
選擇要保存的文件
這里簡單實現(xiàn)為史侣,選擇全部文件拴泌,不過值得注意的是,如果是單文件惊橱,是不需要點擊選擇全部文件的蚪腐。可以通過判斷元素是否存在判斷是否有復(fù)選框税朴,是否需要進行點擊
如圖回季,不過這里奇怪的是,class的值是一個不可讀的字符串掉房,我不確定這個值是不是與用戶有關(guān)茧跋,所以這里就打碼了。
既然需要做的就是找到這個元素卓囚,點擊就行了瘾杭,那我們照著做就行
await page.$$eval('.zbyDdwb', selectAll);
async function selectAll(nodes) {
for (let node of nodes) {
await node.click();
break;
}
}
點擊保存到網(wǎng)盤
與點擊提取文件一樣,找到目表按鈕哪亿,然后點擊就行粥烁,感興趣的朋友,可以自己試試
彈出框選擇保存的文件路徑
避免麻煩蝇棉,這里寫得比較簡單讨阻,如果有選擇跟上一次樣的框,那么就點擊選擇跟上一次一樣篡殷,如果沒有钝吮,就直接存儲在根目錄下。(可以根據(jù)你想要的需求自己改)
選擇前
選擇后
可以發(fā)現(xiàn)板辽,僅僅是改變了Class的值奇瘦,所以實現(xiàn)的時候,也改變一下值就行
await page.$$eval('.save-path-item', chooseLocation);
async function chooseLocation(nodes) {
for (let node of nodes) {
node.setAttribute('class', 'save-path-item check');
}
}
點擊確定按鈕
與之前幾個按鈕一樣劲弦,我們只需要找到確認按鈕耳标,點擊就行了。
await page.$$eval('[title=確定]', confirm)
async function confirm(nodes) {
for (let node of nodes) {
console.log(node.tagName);
if (node.tagName === 'A') {
await node.click();
}
}
}
這樣邑跪,整個邏輯就完成了次坡。
完整代碼
為了程序能正常運行呼猪,需要加入一些額外的waitFor等待,避免操作太快導(dǎo)致的諸如IP封禁砸琅,元素未加載出來等亂七八糟的問題宋距。同時,我這里數(shù)據(jù)都是從數(shù)據(jù)庫中出來的明棍,所以里面有與更新數(shù)據(jù)庫狀態(tài)的代碼乡革。
const Pan = require('../model/Pan');
const {Cluster} = require('puppeteer-cluster');
const crawler = require('./crawler');
const cookies = [];
async function selectAll(nodes) {
console.log('selectAll',nodes.length);
for (let node of nodes) {
await node.click();
break;
}
}
async function click(nodes) {
console.log('click',nodes.length);
for (let node of nodes) {
if (node.title === '提取文件') {
await node.click();
}
}
}
async function writeCode(nodes, code) {
console.log('writeCode',nodes.length);
for (let node of nodes) {
node.value = code;
}
}
async function confirm(nodes) {
console.log(nodes.length);
for (let node of nodes) {
console.log(node.tagName);
if (node.tagName === 'A') {
await node.click();
}
}
}
async function clickSave(nodes) {
for (let node of nodes) {
if (node.tagName === 'A') {
await node.click();
break;
}
}
}
async function chooseLocation(nodes) {
for (let node of nodes) {
node.setAttribute('class', 'save-path-item check');
}
}
async function saveBaidu() {
const cluster = await Cluster.launch(crawler.clusterLanuchOptionsPan);
await cluster.task(async ({page, data}) => {
let {id, url, code} = data;
for (let i = 0; i < cookies.length; i++) {
await page.setCookie(cookies[i]);
}
await page.goto(url);
await page.waitForSelector('html');
await page.content();
let title = await page.title();
let codeWrong = false;
let used = true;
let needCodeButNone = false;
if (title.indexOf('提取碼') !== -1) {
// todo 填寫提取碼
if (code === '-' || code === '+') {
needCodeButNone = true;
} else {
await page.$$eval('#accessCode', writeCode, code);
await page.$$eval('.g-button-blue-large', click);
await page.waitFor(1000);
let content = await page.content();
if (content.indexOf('驗證碼錯誤') !== -1) {
used = false;
} else if (content.indexOf('提取碼錯誤') !== -1) {
codeWrong = true;
used = false;
} else {
await page.waitFor(2000);
}
}
}
if (used && !needCodeButNone) {
title = await page.title();
await page.waitFor(1000);
console.log(title);
if (title.indexOf('不存在') !== -1) {
} else {
// todo 找到title=保存到網(wǎng)盤的a標簽并點擊
let x = await page.$$('.zbyDdwb');
console.log(x.length);
if (!(x === null || x===undefined || x.length === 0)){
await page.$$eval('.zbyDdwb', selectAll);
}
await page.$$eval('[title=保存到網(wǎng)盤]', clickSave);
await page.$$eval('.save-path-item', chooseLocation);
await page.waitFor(2000);
await page.$$eval('[title=確定]', confirm);
}
}
await Pan.update({
used: used,
code_wrong: codeWrong,
need_code: needCodeButNone
}, {
where: {
id: id
}
});
await page.waitFor(3000);
});
let pans = await Pan.findAll({
where: {
site_id:12,
reachable: true,
used:false,
code_wrong:false
}
});
console.log(pans.length);
for (let i = 0; i < pans.length; i++) {
if (pans[i].url.startsWith('http://pan.baidu') || pans[i].url.startsWith('https://pan.baidu')) {
await cluster.queue({
id: pans[i].id,
url: pans[i].url,
code: pans[i].code
});
}
}
await cluster.idle();
}
(async () => {
await saveBaidu();
})();
// crawler.clusterLanuchOptionsPan 是啟動配置項,如下
const launchOptions = {
headless: true,
ignoreHTTPSErrors: true, // 忽略證書錯誤
waitUntil: 'networkidle2',
defaultViewport: {
width: 1920,
height: 1080
},
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-web-security',
'--disable-xss-auditor', // 關(guān)閉 XSS Auditor
'--no-zygote',
'--no-sandbox',
'--disable-setuid-sandbox',
'--allow-running-insecure-content', // 允許不安全內(nèi)容
'--disable-webgl',
'--disable-popup-blocking',
//'--proxy-server=http://127.0.0.1:8080' // 配置代理
],
executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
};
const clusterLanuchOptionsPan = {
concurrency: Cluster.CONCURRENCY_PAGE, // 單Chrome多tab模式
maxConcurrency: 1, // 并發(fā)的workers數(shù)
retryLimit: 2, // 重試次數(shù)
skipDuplicateUrls: true, // 不爬重復(fù)的url
monitor: false, // 顯示性能消耗
puppeteerOptions: launchOptions,
};
炒雞辣雞原創(chuàng)文章摊腋,轉(zhuǎn)載請注明來源