引入cheerio,超级简单的node爬虫

什么是爬虫?

爬虫(Web Crawler)是一种自动获取互联网信息的程序或脚本,它可以在网页上自动抓取信息、提取数据并进行处理。爬虫通常通过网络协议(如HTTP)访问网站,按照一定的规则和算法浏览网页并收集所需的信息。

爬虫是一种十分强大的工具,可以帮助我们快速有效的获取网络上的信息,但是我们在使用的时候也要谨慎,避免对目标网站造成过大的访问压力 或者是造成目标网站的崩溃,损失其权益。

什么是cheerio?

Cheerio 是一个基于 jQuery 核心原则的快速、灵活、专门用于在服务器端实现 DOM 操作的工具。它主要用于解析和操作 HTML 文档,提供了类似 jQuery 的 API,使得在服务器端(如 Node.js 环境)可以方便地对 HTML 进行操作和提取所需信息。

node爬虫步骤

一、选取网页url,使用http协议获取网页的数据

这里我们选择一个斗图的表情包网站(斗图表情包表情套图大全斗了个图 (dogetu.com)

请求http协议,拿到网页中的所有数据

const 复制代码
//这里创建的数组是存放进入表情包详情页的
const srcArr=[]
const titleArr=[]
//这个函数写的是进入分页中,获取表情包详情页地址
const mainFunction = () =>{
    return new Promise((resolve, reject) => {
        https.get('https://dogetu.com/bao.html?page=0',function(res){
            let html=''
            //每次有数据的时候,就将拿到的数据拼接到html
            res.on('data',function(chunk){
                html += chunk
            })
            //直到请求结束的时候,获得全部网页的数据
            res.on('end',function(){
                console.log(html)
                resolve()
            })
        }).on('error', (err) => {
            reject(err)
        })
    })
}

二、使用cheerio工具对获取到的网页进行捕获和解析

javascript 复制代码
const mainFunction = () =>{
    return new Promise((resolve, reject) => {
        https.get('https://dogetu.com/bao.html?page=0',function(res){
            let html=''
            res.on('data',function(chunk){
                html += chunk
            })
            res.on('end',function(){
                const $ = cheerio.load(html)
                
                $('.container div>.panel-heading a').each(function(){
                    const src = $(this).attr('href')
                    //获取该表情包的标题
                    let title = $(this).text()
                    title =title.replace(/[\/:*?"<>|]/g, ' ');
                    title = title.replace(/[\n\s]/g, '');
                    //获取到详情图片的网页链接之后,创建一个文件夹
                    fs.mkdir('./img/'+title,function(err){
                        if(err){
                            console.log(err)
                        }
                        else{
                            console.log('成功创建目录:'+'./img/'+title)
                        }
                    })
                    //将获取到的表情包详情页的地址存入数组中
                    srcArr.push(src)
                    titleArr.push(title)
                })
                resolve()
            })
        }).on('error', (err) => {
            reject(err)
        })
    })
}

爬虫的目的就在于我们要获得网页的数据,所以,你就需要打开网页的检查去查找需要的内容在哪一个标签下面,然后通过$符号去拿到需要的内容。

而这里我就拿到了我所需要的进入表情包详情页的网址 和对应表情包的标题

在这里我对得到的title进行了一个正则替换,同时用fs.mkdir创建了一个新的文件夹,方便后续将表情包下载到本地。

这个正则替换的作用是防止有特殊符号和多余空格等,使得创建文件夹失败。

javascript 复制代码
title =title.replace(/[\/:*?"<>|]/g, ' ');
                    title = title.replace(/[\n\s]/g, '');
                    //获取到详情图片的网页链接之后,创建一个文件夹
                    fs.mkdir('./img/'+title,function(err){
                        if(err){
                            console.log(err)
                        }
                        else{
                            console.log('成功创建目录:'+'./img/'+title)

三、进入表情包详情页下载图片

javascript 复制代码
res.on('end',() =>{
                //设置一个放置具体图片地址的数组
                const imgUrlArr=[]
                const $ = cheerio.load(infoHtml)
                $('.group-pic .item img').each(function(i){
                    const imgSrc = $(this).attr('src')
                    imgUrlArr.push(imgSrc)
                    //获取拿到的文件的文件后缀
                    let extName = path.extname(imgSrc)
                    //在这里的时候,先把图片写入流的文件路径拼接好
                    let imgpath = `./img/${title}/${title}-${i}${extName}`
                    //在这里去创建一个写入图片流
                    let ws = fs.createWriteStream(imgpath)
                    axios.get(imgSrc,{responseType:'stream'}).then(function(res){
                        res.data.pipe(ws)
                        // 监听写入完成事件
                        ws.on('finish', function() {
                            console.log(`Image saved: ${imgpath}`);
                        });
​
                        // 监听写入错误事件
                        ws.on('error', function(err) {
                            console.error(`Error saving image ${imgpath}:`, err);
                        });
                    })
                })
                resolve()
            })

和前两个步骤一样,我们首先做的是进入那个网页获得网页中我们需要的数据,也就是表情包的url。

但是我们知道图片的格式是有很多种的,jpg、png等等,所以这里就需要用到path模块的extname()方法去解析文件后缀名。

ini 复制代码
 let extName = path.extname(imgSrc)

然后就是下载图片,首先,我们要知道,网页的图片也是需要去请求获得的。那么我们获得了图片对应的url地址之后,我们就可以通过对这个url地址发送请求获得对应的图片。

再通过fs模块的createWriteStream()方法创建一个文件写入流,之后再用res.data.pipe()将得到的图片数据流通过管道(pipe)传输写入流中。

其中{responseType:'stream'}是将获得到的数据转化为数据流

javascript 复制代码
axios.get(imgSrc,{responseType:'stream'}).then(function(res){
                        res.data.pipe(ws)
                        // 监听写入完成事件
                        ws.on('finish', function() {
                            console.log(`Image saved: ${imgpath}`);
                        });
​
                        // 监听写入错误事件
                        ws.on('error', function(err) {
                            console.error(`Error saving image ${imgpath}:`, err);
                        });

源码

javascript 复制代码
const https = require('https')
const cheerio = require('cheerio')
const fs = require('fs')
const path = require('path')
const axios=require('axios')
​
//这里创建的数组是存放进入表情包详情页的
const srcArr=[]
const titleArr=[]
//这个函数写的是进入分页中,获取表情包详情页地址
const mainFunction = () => {
    return new Promise((resolve, reject) => {
        https.get('https://dogetu.com/bao.html?page=0',function(res){
            let html=''
            res.on('data',function(chunk){
                html += chunk
            })
            res.on('end',function(){
                const $ = cheerio.load(html)
                
                $('.container div>.panel-heading a').each(function(){
                    const src = $(this).attr('href')
                    //获取该表情包的标题
                    let title = $(this).text()
                    title =title.replace(/[\/:*?"<>|]/g, ' ');
                    title = title.replace(/[\n\s]/g, '');
                    //获取到详情的图片链接之后,创建一个文件夹
                    fs.mkdir('./img/'+title,function(err){
                        if(err){
                            console.log(err)
                        }
                        else{
                            console.log('成功创建目录:'+'./img/'+title)
                        }
                    })
                    //将获取到的表情包详情页的地址存入数组中
                    srcArr.push(src)
                    titleArr.push(title)
                })
                resolve()
            })
        }).on('error', (err) => {
            reject(err)
        })
    })
}
//定义一个进入详情页的进行爬取数据的函数
const infoData = (src,title) => {
    return new Promise((resolve, reject) => {
        https.get(`https://dogetu.com${src}`,function(res){
            let infoHtml=''
            res.on('data',(chunk) => {
                infoHtml += chunk
            })
            res.on('end',() =>{
                //设置一个放置具体图片地址的数组
                const imgUrlArr=[]
                const $ = cheerio.load(infoHtml)
                $('.group-pic .item img').each(function(i){
                    const imgSrc = $(this).attr('src')
                    imgUrlArr.push(imgSrc)
                    //获取拿到的文件的文件后缀
                    let extName = path.extname(imgSrc)
                    //在这里的时候,先把图片写入流的文件路径拼接好
                    let imgpath = `./img/${title}/${title}-${i}${extName}`
                    //在这里去创建一个写入图片流
                    let ws = fs.createWriteStream(imgpath)
                    axios.get(imgSrc,{responseType:'stream'}).then(function(res){
                        res.data.pipe(ws)
                        // 监听写入完成事件
                        ws.on('finish', function() {
                            console.log(`Image saved: ${imgpath}`);
                        });
​
                        // 监听写入错误事件
                        ws.on('error', function(err) {
                            console.error(`Error saving image ${imgpath}:`, err);
                        });
                    })
                })
                resolve()
            })
        }).on('error',(err) => {
            reject(err)
        })
    })
}
//这里定义的是一个真正要使用的函数,目的是,让整个过程保持的异步调用,等待响应结束,拿到完整的网页地址数据
async function use(){
    await mainFunction()
    //将获得到的表情包详情页的数组放到循环中进行操作
    //这里只爬取两个表情包详情页的表情包
    for(let i=0;i<2;i++){
        await infoData(srcArr[i],titleArr[i])
        console.log('-----------------------------------------------')
    }
}
use()

总结

node的爬虫其实也是很简单方便的,对于前端新手来说掌握这么一个小技能对自己学习node的帮助也是很大的。当然,我也是一个新手,哈哈,欢迎大家的留言讨论。

相关推荐
寅时码3 小时前
从“一键部署”到“可观测、可定制的发布流”:我如何打造一个企业级部署工具
运维·开源·node.js
这是个栗子3 小时前
【Node.js安装注意事项】-安装路径不能有空格
前端·npm·node.js
chancygcx_4 小时前
前端核心技术Node.js(二)——path模块、HTTP与模块化
前端·http·node.js
丘色果4 小时前
NPM打包时,报reason: getaddrinfo ENOTFOUND registry.nlark.com
前端·npm·node.js
自学也学好编程7 小时前
【BUG】nvm无法安装低版本Node.js:The system cannot find the file specified解决方案
node.js·bug
牧码岛7 小时前
服务端之nestJS常用异常类及封装自定义响应模块
node.js·nestjs
奕辰杰12 小时前
关于npm前端项目编译时栈溢出 Maximum call stack size exceeded的处理方案
前端·npm·node.js
yzzzzzzzzzzzzzzzzz1 天前
node.js之Koa框架
node.js
Java陈序员1 天前
轻松设计 Logo!一款 Pornhub 风格的 Logo 在线生成器!
vue.js·node.js·vite
gongzemin1 天前
使用Node.js开发微信第三方平台后台
微信小程序·node.js·express