Puppeteer是一个流行的Node.js库,在开发者中广泛使用的用于网页爬取和自动化任务的工具。本文将介绍如何使用puppeteer模块实现爬取网页图片链接并使用axios下载到本地的方法。
1.puppeteer介绍
puppeteer模块:Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。你可以在浏览器中手动执行的绝大多数操作都可以使用 Puppeteer 来完成!
参考文档:Puppeteer中文文档
2.配置相关属性并创建实例
// 配置浏览器宽度高度以及其他参数 const options = { defaultViewport: { width: 1920, height: 1080 // 懒加载图片时可能会导致页面高度不够 需要设置 }, headless: false, // 打开浏览器 // devtools: true, // 是否打开Devtool,如果设置为true,headless将强制为false // slowMo:1000 // 慢慢加载 } const browser = await puppeteer.launch(options); // 打开浏览器 const page = await browser.newPage(); // 创建一个新的页面 // 目标链接地址 const url ='https://image.baidu.com/search/index?tn=baiduimage&ps=1&ct=201326592&lm=-1&cl=2&nc=1&ie=utf-8&dyTabStr=MTEsMCw0LDYsMywxLDUsMiw4LDcsOQ%3D%3D&word=%E9%A3%8E%E6%99%AF'; await page.goto(url); //跳转到指定页面
3.获取图片dom元素
如图所示,此页面中的图片元素都有着同一个类名main_img,我们便可以直接获取到类名为main_img的dom元素(其它任何页面均由此分析,只需要找到图片所对应的dom元素即可)
:
//自定义事件 获取页面中 class='main_img' 的标签并解析出 url 地址 let urlList = await page.$$eval('.main_img', imgs => { let urlList = []; imgs.forEach(img => { urlList.push(img.src); }) return urlList; })
4.监听页面请求事件
在上面代码中,我们可以获取到当前页面的所有图片链接。但是如果我们下滑加载更多,加载后的图片并不会被我们获取到。这时候我们就需要监听页面的请求事件,因为所有的图片肯定是来自于网络请求,我们可以在监听到页面请求时触发自定义函数,解析出图片链接数组。
// 页面发送请求触发该事件 page.on('request', () => { let urlList = await page.$$eval('.main_img', imgs => { let urlList = []; imgs.forEach(img => { urlList.push(img.src); }) return urlList; }) })
由于请求的数量过多,我们没必要每一次都去获取一遍dom元素,可以使用防抖技术在最后一次请求后的一段时间再执行操作。
let timer = null; // 定时器 用于防抖技术 page.on('request', () => { // 使用防抖技术 在请求结束3秒后执行自定义函数 clearTimeout(timer); timer = setTimeout(async () => { //自定义事件 获取页面中 class='main_img' 的标签并解析出 url 地址 let urlList = await page.$$eval('.main_img', imgs => { let urlList = []; imgs.forEach(img => { urlList.push(img.src); }) return urlList; }) }, 3000) })
5.使用axios下载图片
使用 axios 封装一个图片下载的函数:
// 当使用 axios 请求图片时,设置返回的类型为二进制:responseType : "arraybuffer"; // 使用 fs.writeFile() 方法下载图片时,同样设置为二进制的格式:'binary' const axios = require('axios') const fs = require('fs') // 批量下载图片到指定文件夹中 // 图片链接数组 文件名 索引 function downloadImg(urlarr, dir, index) { // 如果还有图片资源,则继续下载 递归出口 if (index >= urlarr.length) { console.log("已下载完所有图片"); return; } let url = urlarr[index]; axios({ method: 'get', url, responseType: 'arraybuffer' }) .then(res => { // 下载当前图片文件到文件夹中 fs.writeFile(`${dir}/${index+1}.png`, res.data, 'binary', (err) => { if (err) { console.log(`第 ${index+1} 张下载失败`, err); } else { console.log(`第 ${index+1} 张下载成功`); } // 递归调用 downloadImg(urlarr, dir, index + 1); }) }) }
在上述案例中使用:
let timer = null; let cnt = 0; // 下载图片数量 // 页面发送请求触发该事件 page.on('request', () => { clearTimeout(timer); timer = setTimeout(async () => { let urlList = await page.$$eval('.main_img', imgs => { let urlList = []; imgs.forEach(img => { urlList.push(img.src); }) return urlList; }) if (urlList.length > cnt) { // 批量下载 url 链接数组中的图片 await downloadImg(urlList, './image/', cnt); cnt = urlList.length; } }, 3000) })
至此,本案例就已经实现完成。对于不同的网页,区别就在于dom元素的不同,需要开发者打开控制台找到相应元素的dom元素并提取出想要的信息。