Node.js爬虫之使用puppeteer爬取百度图片

2022-09-14 15:20:16 浏览数 (1)

本文通过puppeteer实现对百度图片的抓取,这里简单介绍下puppeteer puppeteer可以使我们编写一套代码控制浏览器动作,“你可以在浏览器中手动执行的绝大多数操作都可以使用 Puppeteer 来完成” 因此Puppeteer常用于测试和爬虫---官方文档

示例--爬取百度图片

本项目源码已上传至GitHub npm i puppeteer bufferutil utf-8-validate optimist

1.引入相关模块和初始配置

代码语言:javascript复制
//baidu-img.js
const puppeteer = require('puppeteer')
const imgLoad = require('./imgload')
const httpUrl = 'https://image.baidu.com/'
var argv = require('optimist').argv;

let options = {
   word:argv.word || '图片',
   num:argv.num || 60,
   dir:argv.dir || 'images',
   delay:argv.delay || 600

}
...
  1. 我们引入了imgLoad 这个模块主要做图片下载相关逻辑
  2. 引入optimist用于接收控制台参数
  3. 设置默认参数
  4. word:搜索词
  5. num:要下载的图片数量
  6. dir:图片存放目录
  7. delay:图片下载延迟时间(图片较多时这个时间要设置长点,防反爬虫)

2.图片下载逻辑

代码语言:javascript复制
//imgload.js
const path = require('path')
const fs = require('fs')
const http  = require('http')
const https = require('https')
const {promisify} = require('util')
const writeFile = promisify(fs.writeFile);

module.exports = async (src,dir)=>{
   if(/.(jpg|png|jpeg|gif)$/.test(src)){
       await urlToImg(src,dir)
   } else {
       await base64ToImg(src,dir)
   }
}


const urlToImg = (url,dir)=>{
   const mod = /^https:/.test(url)?https:http
   const ext = path.extname(url)
   fs.mkdir(dir,function(err){
       if(!err){
           console.log('成功创建目录')
       }
   })
   const file = path.join(dir, `${Date.now()}${ext}`)
   //请求图片路径下载图片
   mod.get(url,res=>{
       res.pipe(fs.createWriteStream(file))
       .on('finish',()=>{
           console.log(file ' download successful');
       })
   })
 
}

//base64-download
const base64ToImg = async function(base64Str,dir){
   //data:image/jpg;base64,/fdsgfsdgdfghdfdfh
   try{
       const matches = base64Str.match(/^data:(. ?);base64,(. )$/)
       const ext = matches[1].split('/')[1].replace('jpeg','jpg')//获取后缀
       fs.mkdir(dir,function(err){
           if(!err){
               console.log('成功创建' dir '目录')
           }
       })
       const file = path.join(dir, `${Date.now()}.${ext}`)
       const content = matches[2]
       await writeFile(file,content,'base64')
       console.log(file ' download successful');
     
   }catch(e){
       console.log(e);
   }
  
}

百度图片有两种各种一种是url的形式另一种是base64的形式,我们封装了两个函数分别处理url和base64

3.抓取图片逻辑 3.1

代码语言:javascript复制
...
;( async function(){
   let config = {
       headless:true,//无界面操作 ,false表示有界面
       defaultViewport:{
           width:1920,
           height:1000,
       },
   }
   let browser = await puppeteer.launch(config)
   let page = await browser.newPage()
   await page.goto(httpUrl)
   await page.focus('#kw')
   await page.keyboard.sendCharacter(options.word);//搜索词
   await page.click('.s_search')
...

我们将所以逻辑封装在自执行的异步函数

  1. 创建浏览器对象
  2. 打开一个新的页面 (browser.newPage())
  3. 跳转到百度图片
  4. 使搜索框获得焦点
  5. 填入搜索词
  6. 使搜索按钮被点击

这里的部分比较简单,我们只需找到对应的元素,赋予相应的操作即可

当搜索按钮被点击的时候我们监听onLoad事件,进行图片的抓取 3.2

代码语言:javascript复制
    //页面搜索跳转 执行的逻辑
    page.on('load',async ()=>{
        console.warn('正在为你检索【' options.word '】图片请耐心等待...');
        await page.evaluate((options)=>{
            ///获取当前窗口高度  处理懒加载
            let height = document.body.offsetHeight
            let timer = setInterval(()=>{
                //窗口每次滚动当前窗口的2倍
                height=height*2
                window.scrollTo(0,height);
            },2000)

            window.onscroll=function(){
                let arrs = document.querySelectorAll('.main_img')
                //符合指定图片数
                if(arrs.length>=options.num){
                    clearInterval(timer)
                    console.log(`为你搜索到${arrs.length}张【${options.word}】相关的图片n准备下载(${options.num})张`);
                }  
            }
        },options)
    })

由于百度图片使用了懒加载,这里我们通过page.evaluate使浏览器执行我们自定义的js,在 page.evaluate我们优雅的处理了懒加载,并监听页面滚动事件,每次滚动的时候计算页面图片的数量,并展示提示信息(console.log)这个打印并不只是打印,后面我们要监听console事件执行图片下载逻辑

3.3

代码语言:javascript复制
 await page.on('console',async msg=>{
        console.log(msg.text());
        //提取图片的src
        let res = await page.$$eval('.main_img',eles=>eles.map((e=>e.getAttribute('src'))))
        res.length = options.num
        res.forEach(async (item,i)=>{
            await page.waitFor(options.delay*i)//延迟执行
            await imgLoad(item,options.dir)

        })
        
        await browser.close()
        
    })

我们监听浏览器的console事件,当触发console时说明需要的图片已经找到,此时可以执行图片url提取,将其下载,至于为什么不在page.evaluate执行图片下载逻辑 是因为page.evaluate只能写“前端”的js图片下载需要用到fs、path等模块,我们在page.evaluate里面是无法使用的

到此一个小爬虫完成 我们来看看效果

http://www.zihanzy.com/uploads/images/article_con/202008/24/article_con_1598268382_X4zjDPUK3R.gif

0 人点赞