nodejs 写爬虫

爬虫:按照一定规则 自动抓取网络信息的程序,搜索引擎就是一个超级大的爬虫;

反爬虫:

           user-agent: 如果user-agent是爬虫的话 就拒绝;    

           验证码

          单位时间 访问量的限制

          关键信息图片混淆

          异步加载    返回空的 html, 使用 利用js 异步来  加载页面;

 

 

写爬虫一般都是通过两个工具来实现的 ; 

superagent  帮助我们发送请求 得到html;

cheerio   把 html内容 转换成一个  jq 对象,就可以使用jq 去操作html的内容了;但是 他饶不过反爬虫规则;

 

 

# 这里使用puppeteer来实现;

 google chrome   的 headless/puppeteer  可以使用谷歌浏览器来 操作;所有的操作都是异步的,可以饶过反爬虫规则;

安装 headless/puppeteer   cnpm i  -S puppeteer

 

新建 文件 screenshot.js


// 引入 puppeteer
const puppeteer = require("puppeteer");
const path = require("./config/default");

//写一个立即执行函数或者写一个函数下面调用;  反正都是异步函数;  所有的操作都是异步的;
(async () => {
       // 实例化一个浏览器对象
        const browser = await puppeteer.launch()

         // 实例化一个浏览器对象的页面对象;
        const page = await browser.newPage()

        // goto baidu.com
       await page.goto("https://baidu.com")

       // 截屏  保存地址为 根目录 下的screenshot 文件夹
       await  page.screenshot({

         path:`${path.screenshot}/${new Date()}.png`
            })

       // 关闭浏览器

       await   browser.close();
})();

 

新建文件夹 config 其内新建文件 default.js

const path = require("path");

module.exports={
    // 路径为根目录 下的screenshot 文件夹 // 需要预设文件夹否则报错;
    screenshot:path.resolve(__dirname,"../screenshot")
};

预设储存 截屏图片的 文件夹  screenshot ,否则允许报错;

 

 

开始操作 

主代码 ;


// 引入 puppeteer
const puppeteer = require("puppeteer");
const path = require("./config/default");
 const srcToimg=require("./helper/srcToimg");

//写一个立即执行函数或者写一个函数下面调用;  反正都是异步函数;  所有的操作都是异步的;
(async () => {
       // 实例化一个浏览器对象
        const browser = await puppeteer.launch({headless: false})

         // 实例化一个浏览器对象的页面对象;
        const page = await browser.newPage()

        // goto image.baidu.com
       await page.goto("https://image.baidu.com/")
       console.log( "image.baidu.com");

      //设置 窗口的大小变得很大 以获取更多的图片 方便抓取;
       await page.setViewport({
           width:1920,
           height:1080
       })

      console.log("reset viewport")

     // 使得搜索框聚焦 模拟键入 "狗" 模拟点击搜索;
       await page.focus("#kw");

       await page.keyboard.sendCharacter("狗");
     console.log(22);
       await page.click('.s_search');


       console.log("go to search list");

       // 异步加载完成以后; 由于回调函数要异步 所以 要用到 async
    await  page.on('load', async ()=>{
           console.log("page loading done , start fetch");

      //  使用page.evaluate 来拿到dom
           const srcs = await page.evaluate(()=>{
               //。拿到所有的图片集合 ,但是集合不是数组 ,所以不能调用map方法
               const imgs = document.querySelectorAll('.main_img');
               //使用数组的map方法绑定 ,并且处理 拿到 原来item的src,变成一个所有图片src的集合
              return Array.prototype.map.call(imgs,(img)=>{return img.src});

           });


           //循环 处理这个页面图片src的数据;
           srcs.forEach(async (src)=>{

               //sleep 休息200毫秒再去调用 这样做是为了反爬虫;
               await page.waitFor(200);
               await  srcToimg(src,path.mn);
           })

       })


       // 关闭浏览器

      // await   browser.close();
})();

 

工具函数 

//引入http和https模块;
const http = require("http");
const https = require("https");
// 引入fs
const fs = require("fs");
//引入path
const path = require("path");

//引入 异步模块
const {promisify} = require("util");

//使用异步模块处理fs。WriteFile;
const writeFile=promisify(fs.writeFile)

//处理src 储存的 函数;
module.exports = async  (url, dir) => {
    var reg = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i;

    //判断 是否是base的;来匹配方法
   if(!reg.test(url)){
    await urlToimg(url, dir)
   }else {
   await  base64Toimg(url, dir)
   }
};
// url to  img// 由于之后要使用到异步函数; 所以这个函数要promisify 一下 方便使用 之后使用await
const urlToimg =promisify ( (url,dir,callback) => {

    //拿到url的后缀名 (带.点的);
    const ext = path.extname(url);
   //文件的存储路径  使用传入的路径 + 后缀名  拼接成的;
    const filePath = path.join(dir,`${Date.now()}${ext}`)


     // 分清楚url是http还是https; 分别使用不同的模块去处理地址;
    //如果是https开头的那么 就使用https模块否则使用http模块;
     const mod = /^https:/.test(url)?https:http;

     // 使用对应的模块使用   get方法 拿到对应的处理 结果 ;是一个流文件;
     mod.get(url,(res)=>{
         // 把流 pipe到磁盘上; 使用 fs的书写方法 书写到文件路径上去;
       res.pipe(fs.createWriteStream(filePath)).on("finish",()=>{
           callback();
           console.log(filePath)
       })
     })


})

//base64 to img
const base64Toimg =async (base64,dir) => {



    try{
        //data:image/jpeg;base64,/qwqwdq    base64格式;

        //使用match 匹配 base64的字符串;规则
        // 意思是 以 data: 开头的, +?(关闭贪婪模式)遇到 ;base64, 停止
        // 这样就有2个 .+ 的两段内容; 被放置到 match的数组里了;

        const matches = base64.match(/^data:(.+?);base64,(.+)$/)

        const ext =matches[1].split("/")[1].replace("jpeg","jpg");  //取到jpeg,如果遇到jpeg 那么就换成jpg;

        //文件的存储路径  使用传入的路径 + 后缀名  拼接成的;
        const fileName = path.join(dir,`${Date.now()}.${ext}`)


        //三个参数,文件名,内容,编码格式(默认buffer);
        await writeFile(fileName,matches[2],"base64");
        console.log(fileName);

        }catch (e) {


       console.log("base64is not")

        }

};

 

路径类 

const path = require("path");

module.exports={
    // 路径为根目录 下的screenshot 文件夹 // 需要预设文件夹否则报错;
    screenshot:path.resolve(__dirname,"../screenshot"),
    mn:path.resolve(__dirname,"../mn")
};

 

 

 

你可能感兴趣的:(nodejs 写爬虫)