Node.js爬虫之使用cheerio爬取图片

2022-09-08 16:56:07 浏览数 (1)

引入

在上一篇文章我们利用Node.js实现了一个基本的爬虫,但是要写很长的正则--实在太累了而且需要对正则绝对熟悉。 在写前端我们都知道jQuery能方便帮我我们进行各种DOM操作,通过DOM操作我们可以方便的获取元素的各种属性,不过jqDOM操作只能运行在客户端,如果服务端有这样的一个工具能帮我们进行DOM操作那不是就解决了之前不断写正则的问题? 当然有---cheerio

cheerio是jquery核心功能的一个快速灵活而又简洁的实现,主要是为了用在服务器端需要对DOM进行操作的地方

你可以把cheerio当做服务端的jQuery

我们先来看一个案例---爬取百度logo 如果是之前的方式我们要写一堆正则才能匹配到某网站的logo,而使用了cheerio后我们可以进行DOM操作直接获取数据

可以看到终端直接输出了百度logo

案例爬取表情包

安装cheerio npm i cheerio

如图我们要爬取该网站的表情包

分析

1.我们以列表页为起始页,该页面展示了表情包的分类,我们要获取所有分类的url

2.获取分类名称,根据分类名称创建文件夹

3.根据分类url获取到该分类的所有图片url

4.根据图片url,进行流请求将图片下载到相应的文件夹下面

1.首先通过入口页获取分类url 经过调试发现分类绑定在.bqba类名上,我们可以直接进行dom操作获取url

代码语言:javascript复制
const axios = require('axios')
const cheerio = require('cheerio')
const fs = require('fs')
const path = require('path')
const url = require('url')
let host = 'https://www.fabiaoqing.com'

async function getData(url){
    let res = await axios.get(url)
    let $ = cheerio.load(res.data)
    $('.bqba').each(async (i,e)=>{
    //图片分类地址
    let url = host $(e).attr('href')
    console.log(url)
    })

}
getData('https://www.fabiaoqing.com/bqb/lists/page/1.html')

获取到分类url

代码语言:javascript复制
https://www.fabiaoqing.com/bqb/detail/id/54195.html
https://www.fabiaoqing.com/bqb/detail/id/54176.html
https://www.fabiaoqing.com/bqb/detail/id/54194.html
https://www.fabiaoqing.com/bqb/detail/id/54198.html
https://www.fabiaoqing.com/bqb/detail/id/54193.html
https://www.fabiaoqing.com/bqb/detail/id/54196.html
https://www.fabiaoqing.com/bqb/detail/id/54186.html
https://www.fabiaoqing.com/bqb/detail/id/54185.html
https://www.fabiaoqing.com/bqb/detail/id/54169.html
https://www.fabiaoqing.com/bqb/detail/id/54135.html

接下来我们要获取到分类名并且创建分类文件夹

代码语言:javascript复制
async function getData(url){
    let res = await axios.get(url)
    let $ = cheerio.load(res.data)
    $('.bqba').each(async (i,e)=>{
    //图片分类地址
    let url = host $(e).attr('href')
    //图片分类名称 
    let folderReg = /\|/|:|*|?|<|>||/g
    let title = $(e).find('h1').text().replace(folderReg,'-')
    // 创建图片分类文件夹
    fs.mkdir('./img/' title,function(err){
        if(!err){
            console.log('成功创建目录' title)
        }
    })
    })
}

我们对分类名称进行了替换使其符合文件夹创建规则

接下来我们要根据分类url获取到相应的图片url 经过调试发现图片绑定了类名 bqbppdetail

代码语言:javascript复制
async function getData(url){
....
   // 下载图片
    parsePage(url,title)
    })

}
async function parsePage(url,title){

    let res = await axios.get(url)
    let $  = cheerio.load(res.data)
  
    $('.bqbppdetail').each((i,e)=>{
        let imgUrl = $(e).attr('data-original')
        console.log(imgUrl)
    })
}

获取到图片url

代码语言:javascript复制
...
http://wx2.sinaimg.cn/bmiddle/006GJQvhgy1ghovr2w8gtj31fr1ln4k0.jpg
http://wx1.sinaimg.cn/bmiddle/006GJQvhgy1ghovr51917j31rl1rlx4r.jpg
...

接下来我们将这些图片下载下来以流请求的方式下载

代码语言:javascript复制
async function getData(url){
....
   // 下载图片
    parsePage(url,title)
    })

}
async function parsePage(url,title){
    let res = await axios.get(url)
    let $  = cheerio.load(res.data)
    $('.bqbppdetail').each((i,e)=>{
        let imgUrl = $(e).attr('data-original')
        //图片后缀
        let ext =  path.extname(imgUrl)
        //图片文件名
        let imgPath = `./img/${title}/${title}-${i}${ext}`
        //创建写入流
        const ws = fs.createWriteStream(imgPath,function(err){
            if(err){
                console.log(err)
            }
        })
        //流的形式请求图片资源
        axios.get(imgUrl,{responseType:'stream'}).then(function(res){
            //写入流
            res.data.pipe(ws)
            console.log("图片加载完成:" imgPath)
            //监听关闭流操作
            res.data.on('close',function(){
                // ws.close()
                console.log('创建完毕' imgPath)
            })
        })
    })
}

图片下载成功

到此一个简单的爬虫完毕。

但是我们只爬取了单页的图片,一般网站都会涉及到分页,接下来我们将分页的数据一并爬取

分析

1.我们从起始页就可以获取到该网站的总页数

2.循环总页数获取数据每次url后缀 1

代码语言:javascript复制
https://www.fabiaoqing.com/bqb/lists/page/2.html
||
https://www.fabiaoqing.com/bqb/lists/page/3.html
||
...
https://www.fabiaoqing.com/bqb/lists/page/1066.html
代码语言:javascript复制
async function spider(url){
  
    let res = await axios.get(url)
    let $ = cheerio.load(res.data)
    //获取总页数
    let allPage = 30
    /*$('.disabled.item').next().text().trim()==1066*/
    //循环总页数下载图片
    for(let i=0;i<allPage;i  ){
        getData('https://www.fabiaoqing.com/bqb/lists/page/' i '.html')
    }
  
}

这里我们获取总页面进行循环,由于数据太多我这里只设置了30页,当请求的较多时我们应该控制请求,设置每秒请求多少次,这样尽可能保证资源的完整性也不至于将对方服务端搞崩溃。

如下构建延迟函数

代码语言:javascript复制
function witeMe(mis){
    return new Promise((resolve,reject)=>{
        setTimeout(function(){
            resolve("成功执行延迟函数,延迟:" mis)
        },mis)
    })
}

在请求多的地方调用该函数

代码语言:javascript复制
...
  for(let i=0;i<allPage;i  ){
        await witeMe(2000*i)
        getData('https://www.fabiaoqing.com/bqb/lists/page/' i '.html')
    }
...

0 人点赞