使用puppeteer 进行批量网页截图

时间:2022-07-22
本文章向大家介绍使用puppeteer 进行批量网页截图,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

数据来源为一个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');
}
  1. 为什么使用--start-maximized这个参数:有些网页在最大化下和它默认大小下 截出来的图片不一样 默认尺寸下可能会出现拼接的情况
  2. await page.goto(url,{waitUntil: ['networkidle0']}) 此处waitUntil的作用:有些网页 打开后会继续请求js 做出动画或改变样式 添加此参数的意思是直到没有任何网络连接 视为跳转成功。可以有效避免部分网页刚打开 load事件触发了就截图 截出的图不完整