数据来源为一个txt文件
每一行用\t分割后 前面是域名后面是url 域名用来md5后作为截图名
pageSize控制一次最多打开多少个页面 防止网页过多占用内存过多
配置里的'--proxy-server=socks5://127.0.0.1:1080' 是用来走本地小飞机代理的
const puppeteer = require('puppeteer');
var fs = require('fs');
var readline = require('readline');
const crypto = require('crypto');
var file = '/test/snap.txt';
//最多多少个页面
const pageSize = 50;
var date = new Date();
var startTime = date.getTime();
readFileToArr(file ,async function (data){
console.log('本次需要截图[' + data.length+']条')
console.log('开始执行:'+startTime);
if(data.length>0){
snp(data).then(() => {
console.log('执行清空文件操作');
fs.access(file, fs.constants.F_OK, (err) => {
console.log(`${file} ${err ? '不存在' : '存在'}`);
var ws1 = fs.createWriteStream(file, 'utf-8');
ws1.write('');
ws1.end();
console.log(`${file}` +' 已清空');
});
}).catch(function (err) {
console.log(err);
});;
}
});
async function snp(arr){
const config = {
ignoreHTTPSErrors:true,
defaultViewport:{width:1920,height:1080},
args: [
'--no-sandbox','--proxy-server=socks5://127.0.0.1:1080'
],
headless: true
}
const browser = await puppeteer.launch(config);
var that = this;
while(arr.length>0){
var some;
if(arr.length >pageSize){
some = arr.splice(0,pageSize);
}else{
some = arr.splice(0,arr.length);
}
var length = some.length;
var a = [];
for (let j = 0; j < length; j++) {
if(some[j] === '') continue;
var line = some[j].split('\t');
if(line.length !==2 ){console.log('数据不全:' + some[j]);continue;}
var domain = line[0];
var url = line[1];
var page = await browser.newPage();
//默认30S超时
//page.setDefaultNavigationTimeout(3000);
try{
await page.goto(url);
console.log('已打开:'+url);
page.on('dialog', async dialog => {
await dialog.accept();
});
}catch(err){
console.log('打开网页出错:'+err);
}
a.push(page);
}
//等待时间 可调整
//await a[0].waitFor(1 * 1000);
for (i = 0; i < length; i++) {
if(some[i] === '') continue;
var line = some[i].split('\t');
if(line.length !==2 ){console.log('数据不全:' + some[i]);continue;}
var domain = line[0];
var url = line[1];
var fileName = getMD5(domain) + '.png';
try {
await a[i].screenshot({ path: '/test/snapshot/'+fileName });
console.log('截图成功: '+domain);
await a[i].close();
} catch (e) {
console.log('截图出错: '+e)
}
}
}
await browser.close();
var endTime = new Date().getTime();
console.log('执行结束:'+endTime);
console.log('本次执行时间:' + (endTime-startTime)/1000 + 's');
}
//读取文件
function readFileToArr(fReadName,callback){
var fRead = fs.createReadStream(fReadName);
var objReadline = readline.createInterface({
input:fRead
});
var arr = new Array();
objReadline.on('line',function (line) {
arr.push(line);
//console.log('line:'+ line);
});
objReadline.on('close',function () {
// console.log(arr);
callback(arr);
});
}
//获取md5值
function getMD5(data) {
// 加入字符编码
var md5 = crypto.createHash('md5').update(data, 'utf-8').digest('hex');
return md5;
}
目前还可以优化的地方:
当前流程是依次打开网页,等待当前网页加载完成后再去打开下一个网页,若某一个网页打开较慢或打不开,则会一直等待到超时。
可以改为调用goto后不等待,并行的打开网页,大大减少打开网页过程中花费的时间。
由于第一版 虽说是一次打开多个标签页了,但是实质上还是和串行一个个打开没有区别,我在page的load事件上也没有找到能保存当前页面上下文并使其在后面可选择使用的好办法。
所以不如直接使用串行 由于截图任务要的是准确第一 速度第二 所以改为串行也未尝不可
主要改动的地方就是snp()方法 并且删掉了pageSize这个常量
async function snp(arr){
const config = {
ignoreHTTPSErrors:true,
defaultViewport:{width:1920,height:1080},
args: [
'--no-sandbox','--start-maximized'
],
headless: false
}
const browser = await puppeteer.launch(config);
var page = await browser.newPage();
for(let i=0;i<arr.length;i++){
if(arr[i] === '') continue;
var line = arr[i].split('\t');
if(line.length !==2 ){console.log('数据不全:' + arr[i]);continue;}
var domain = line[0];
var url = line[1];
var fileName = getMD5(domain) + '.png';
await page.goto(url,{waitUntil: ['networkidle0']}).then(()=>{
console.log('已打开:'+url);
}).catch( err =>{
console.log('打开网页出错:' + url);
});
page.on('dialog', async dialog => {
await dialog.accept();
});
await page.screenshot({path: '/Users/rzx/Desktop/snapshot/'+fileName});
console.log('截图成功: '+domain);
}
await page.close();
await browser.close();
var endTime = new Date().getTime();
console.log('本次执行时间:' + (endTime-startTime)/1000 + 's');
}