Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Javascript] node爬虫来一份 #24

@zgfang1993

Description

@zgfang1993

自己写的小项目可能需要一些数据,自己写mock数据又麻烦,可以通过爬虫来获取一些数据。

  • 需要的模块,及简单讲解
  • 一个简单的爬虫demo

需要使用的模块

  • superagent 获取页面数据
  • cheerio 解析页面数据
  • fs 数据保存本地文件 node自带文件系统模块
npm install superagent cheerio --save

superagent 页面数据下载

超级代理,是一个http请求模块,常用于get、post等请求,可以实现请求操作,还有一些表单post操作,模拟ajax请求等,支持链式调用。

superagent.post(url)
    .set(headers)  //设置请求头
    .set('Cookie', cookie)  // 设置cookie
    .type('form')
    .send({
        //表单数据
    })
    .end(function(err, res) {
        //数据与错误处理
    })

扩展:superagent-charset
superagent只支持UTF-8,用了这个库可以指定编码,当用superagent爬出乱码时可以用它。

superagent.get(url)
    .set(headers) 
    .charset('gbk') //指定编码
    .end(function(err, res) {
        if (err) {
            return console.err(err);
        }
    })
    

cheerio 页面数据解析

可以使用大量jQuery的语法来获取你爬虫请求到的数据

$ = cheerio.load(res.text);
// $(".XXX")选中class
// $("#XXX")选中id

demo 下厨房里面的菜谱数据

  1. 引入工具模块
const superagent = require('superagent');
const cheerio = require('cheerio');

2.发请求获取数据

const reptileUrl = "www.xiachufang.com/explore";

superagent
  .get(reptileUrl)
  .end(function (err, res) {
     if(err){
        throw Error(err);
     }
    // 处理数据
});

现在可以运行 node app.js看一下请求。此时会报错。获得了一个Forbidden错误。网站对于访问进行了检查,我们复制请求头,重写网络请求。

superagent
  .get(reptileUrl)
  .set('Connection','keep-alive')
  .set('User-Agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36')
  .set('Host', 'www.xiachufang.com')
  .end(function (err, res) {
     if(err){
        throw Error(err);
     }
    // 处理数据
});

3.解析数据

superagent
  .get(reptileUrl)
  .set('Connection','keep-alive')
  .set('User-Agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36')
  .set('Host', 'www.xiachufang.com')
  .end(function (err, res) {
     if(err){
        throw Error(err);
     }
    // 处理数据
   /**
      * res.text 包含未解析前的响应内容
      * 通过cheerio的load方法解析整个文档,就是html页面所有内容.
      */
   let $ = cheerio.load(res.text);
   // console.log($.html());
});

4.分析页面结构,拼接数据
现在需要打开下厨房网站本周最受欢迎页面,列表展示的信息都是服务端渲染的。爬虫就是获取服务端渲染的数据,ajax获取的数据的话,直接调api接口即可。我们就以获取第一列数据为例子。

// 列表数据都存在$('.normal-recipe-list')这个容器里面,只需要遍历
const list = $('.normal-recipe-list .list li');
list.each((i, elem)=>{
  // 拼接数据
})

定义数据结构

{
  "id": "100519211", // 菜谱id
  "name": "超级无敌好吃的奥利奥牛奶雪糕!!", // 菜谱名字
  "exclusive": true, // 是否独家
  "img": "", 
  "material": "", // 菜谱材料
  "author": {
    "id": "",
    "name": ""
  }
}
 function replaceText(text){
   return text.replace(/\n/g, "").replace(/\s/g, ""); // 去除回车符/n和空格符/s
 }
  list.each((i, elem)=>{
    const $this = $(elem);
    const item = {
      "id": $this.children('a').attr('href').replace(/\/recipe\//, '').replace(/\/$/, ''),
      "name": replaceText($this.find('.info .name').text()), // 名字
      "exclusive": !!$this.find('.info .exclusive-icon'), // 是否独家
      "img": $this.find('.cover img').attr('src'),
      "material": replaceText($this.find('.info .ing').text()),
      "author": {
        "id": $this.find('.info .author a').attr('href').replace(/\/cook\//, "").replace(/\/$/, ''),
        "name": replaceText($this.find('.info .author a').text())
      }
    }
    data.push(item);
  });

5.保存数据到本地文件

  // 写入数据
  fs.writeFile(__dirname + `/data/${new Date().getTime()}.json`, JSON.stringify({
    status: 0,
    data: data
  }), function (err) {
    if (err) throw err;
    console.log('写入完成');
  });

完整代码

const fs = require('fs');
const superagent = require('superagent');
const cheerio = require('cheerio');

const reptileUrl = "www.xiachufang.com/explore";

// 去除回车符/n和空格符/s
function replaceText(text){
  return text.replace(/\n/g, "").replace(/\s/g, "");
}

superagent
  .get(reptileUrl)
  .set('Connection','keep-alive')
  .set('User-Agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36')
  .set('Host', 'www.xiachufang.com')
  .end(function (err, res) {
  // 抛错拦截
   if(err){
      throw Error(err);
   }
  /**
  * res.text 包含未解析前的响应内容
  * 通过cheerio的load方法解析整个文档,就是html页面所有内容.
  */
  let $ = cheerio.load(res.text);
  // console.log($.html());
  const data = [];
  const list = $('.normal-recipe-list .list li .recipe');
  list.each((i, elem)=>{
    const $this = $(elem);
    const item = {
      "id": $this.children('a').attr('href').replace(/\/recipe\//, '').replace(/\/$/, ''),
      "name": replaceText($this.find('.info .name').text()), // 名字
      "exclusive": !!$this.find('.info .exclusive-icon'), // 是否独家
      "img": $this.find('.cover img').attr('src'),
      "material": replaceText($this.find('.info .ing').text()),
      "author": {
        "id": $this.find('.info .author a').attr('href').replace(/\/cook\//, "").replace(/\/$/, ''),
        "name": replaceText($this.find('.info .author a').text())
      }
    }
    data.push(item);
  });
  
  // 创建data文件夹
  if (!fs.existsSync('data')) {
    fs.mkdirSync('data');
  }
  // 写入数据
  fs.writeFile(__dirname + `/data/${new Date().getTime()}.json`, JSON.stringify({
    status: 0,
    data: data
  }), function (err) {
    if (err) throw err;
    console.log('写入完成');
  });
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions