资讯专栏INFORMATION COLUMN

node爬取拉勾网数据并导出为excel文件

dkzwm / 1518人阅读

摘要:前言之前断断续续学习了,今天就拿拉勾网练练手,顺便通过数据了解了解最近的招聘行情哈方面算是萌新一个吧,希望可以和大家共同学习和进步。

前言
之前断断续续学习了node.js,今天就拿拉勾网练练手,顺便通过数据了解了解最近的招聘行情哈!node方面算是萌新一个吧,希望可以和大家共同学习和进步。
一、概要

我们首先需要明确具体的需求:

可以通过node index 城市 职位来爬取相关信息

也可以输入node index start直接爬取我们预定义好的城市和职位数组,循环爬取不同城市的不同职位信息

将最终爬取的结果存储在本地的./data目录下

生成对应的excel文件,并存储到本地

二、爬虫用到的相关模块

fs: 用于对系统文件及目录进行读写操作

async:流程控制

superagent:客户端请求代理模块

node-xlsx:将一定格式的文件导出为excel

三、爬虫主要步骤: 初始化项目

新建项目目录

在合适的磁盘目录下创建项目目录 node-crwl-lagou

初始化项目

进入node-crwl-lagou文件夹下

执行npm init,初始化package.json文件

安装依赖包

npm install async

npm install superagent

npm install node-xlsx

命令行输入的处理

对于在命令行输入的内容,可以用process.argv来获取,他会返回个数组,数组的每一项就是用户输入的内容。
区分node index 地域 职位node index start两种输入,最简单的就是判断process.argv的长度,长度为四的话,就直接调用爬虫主程序爬取数据,长度为三的话,我们就需要通过预定义的城市和职位数组来拼凑url了,然后利用async.mapSeries循环调用主程序。关于命令分析的主页代码如下:

if (process.argv.length === 4) {
  let args = process.argv
  console.log("准备开始请求" + args[2] + "的" + args[3] + "职位数据");
  requsetCrwl.controlRequest(args[2], args[3])
} else if (process.argv.length === 3 && process.argv[2] === "start") {
  let arr = []
  for (let i = 0; i < defaultArgv.city.length; i++) {
    for (let j = 0; j < defaultArgv.position.length; j++) {
      let obj = {}
      obj.city = defaultArgv.city[i]
      obj.position = defaultArgv.position[j]
      arr.push(obj)
    }
  }
  async.mapSeries(arr, function (item, callback) {
    console.log("准备开始请求" + item.city + "的" + item.position + "职位数据");
    requsetCrwl.controlRequest(item.city, item.position, callback)
  }, function (err) {
    if (err) throw err
  })
} else {
  console.log("请正确输入要爬取的城市和职位,正确格式为:"node index 城市 关键词" 或 "node index start" 例如:"node index 北京 php" 或"node index start"")
}

预定义好的城市和职位数组如下:

{
    "city": ["北京","上海","广州","深圳","杭州","南京","成都","西安","武汉","重庆"],
    "position": ["前端","java","php","ios","android","c++","python",".NET"]
}

接下来就是爬虫主程序部分的分析了。

分析页面,找到请求地址

首先我们打开拉勾网首页,输入查询信息(比如node),然后查看控制台,找到相关的请求,如图:

这个post请求https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false就是我们所需要的,通过三个请求参数来获取不同的数据,简单的分析就可得知:参数first是标注当前是否是第一页,true为是,false为否;参数pn是当前的页码;参数kd是查询输入的内容。

通过superagent请求数据

首先需要明确得是,整个程序是异步的,我们需要用async.series来依次调用。
查看分析返回的response:

可以看到content.positionResult.totalCount就是我们所需要的总页数
我们用superagent直接调用post请求,控制台会提示如下信息:

{"success": False, "msg": "您操作太频繁,请稍后再访问", "clientIp": "122.xxx.xxx.xxx"}

这其实是反爬虫策略之一,我们只需要给其添加一个请求头即可,请求头的获取方式很简单,如下:

然后在用superagent调用post请求,主要代码如下:

// 先获取总页数
    (cb) => {
      superagent
        .post(`https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false&city=${city}&kd=${position}&pn=1`)
        .send({
          "pn": 1,
          "kd": position,
          "first": true
        })
        .set(options.options)
        .end((err, res) => {
          if (err) throw err
          // console.log(res.text)
          let resObj = JSON.parse(res.text)
          if (resObj.success === true) {
            totalPage = resObj.content.positionResult.totalCount;
            cb(null, totalPage);
          } else {
            console.log(`获取数据失败:${res.text}}`)
          }
        })
    },

拿到总页数后,我们就可以通过总页数/15获取到pn参数,循环生成所有url并存入urls中:

(cb) => {
      for (let i=0;Math.ceil(i

有了所有的url,在想爬到所有的数据就不是难事了,继续用superagent的post方法循环请求所有的url,每一次获取到数据后,在data目录下创建json文件,将返回的数据写入。这里看似简单,但是有两点需要注意:

为了防止并发请求太多而导致被封IP:循环url时候需要使用async.mapLimit方法控制并发为3, 每次请求完都要过两秒在发送下一次的请求

在async.mapLimit的第四个参数中,需要通过判断调用主函数的第三个参数是否存在来区分一下是那种命令输入,因为对于node index start这个命令,我们使用得是async.mapSeries,每次调用主函数都传递了(city, position, callback),所以如果是node index start的话,需要在每次获取数据完后将null传递回去,否则无法进行下一次循环

主要代码如下:

// 控制并发为3
    (cb) => {
      async.mapLimit(urls, 3, (url, callback) => {
        num++;
        let page = url.split("&")[3].split("=")[1];
        superagent
          .post(url)
          .send({
            "pn": totalPage,
            "kd": position,
            "first": false
          })
          .set(options.options)
          .end((err, res) => {
            if (err) throw err
            let resObj = JSON.parse(res.text)
            if (resObj.success === true) {
              console.log(`正在抓取第${page}页,当前并发数量:${num}`);
              if (!fs.existsSync("./data")) {
                fs.mkdirSync("./data");
              }
              // 将数据以.json格式储存在data文件夹下
              fs.writeFile(`./data/${city}_${position}_${page}.json`, res.text, (err) => {
                if (err) throw err;
                // 写入数据完成后,两秒后再发送下一次请求
                setTimeout(() => {
                  num--;
                  console.log(`第${page}页写入成功`);
                  callback(null, "success");
                }, 2000);
              });
            }
          })
      }, (err, result) => {
        if (err) throw err;
        // 这个arguments是调用controlRequest函数的参数,可以区分是那种爬取(循环还是单个)
        if (arguments[2]) {
          ok = 1;
        }
        cb(null, ok)
      })
    },
    () => {
      if (ok) {
        setTimeout(function () {
          console.log(`${city}的${position}数据请求完成`);
          indexCallback(null);
        }, 5000);
      } else {
        console.log(`${city}的${position}数据请求完成`);
      }
      // exportExcel.exportExcel() // 导出为excel
    }

导出的json文件如下:

json文件导出为excel

将json文件导出为excel有多种方式,我使用的是node-xlsx这个node包,这个包需要将数据按照固定的格式传入,然后导出即可,所以我们首先做的就是先拼出其所需的数据格式:

function exportExcel() {
  let list = fs.readdirSync("./data")
  let dataArr = []
  list.forEach((item, index) => {
    let path = `./data/${item}`
    let obj = fs.readFileSync(path, "utf-8")
    let content = JSON.parse(obj).content.positionResult.result
    let arr = [["companyFullName", "createTime", "workYear", "education", "city", "positionName", "positionAdvantage", "companyLabelList", "salary"]]
    content.forEach((contentItem) => {
      arr.push([contentItem.companyFullName, contentItem.phone, contentItem.workYear, contentItem.education, contentItem.city, contentItem.positionName, contentItem.positionAdvantage, contentItem.companyLabelList.join(","), contentItem.salary])
    })
    dataArr[index] = {
      data: arr,
      name: path.split("./data/")[1] // 名字不能包含  / ? * [ ]
    }
  })

// 数据格式
// var data = [
//   {
//     name : "sheet1",
//     data : [
//       [
//         "ID",
//         "Name",
//         "Score"
//       ],
//       [
//         "1",
//         "Michael",
//         "99"
//
//       ],
//       [
//         "2",
//         "Jordan",
//         "98"
//       ]
//     ]
//   },
//   {
//     name : "sheet2",
//     data : [
//       [
//         "AA",
//         "BB"
//       ],
//       [
//         "23",
//         "24"
//       ]
//     ]
//   }
// ]

// 写xlsx
  var buffer = xlsx.build(dataArr)
  fs.writeFile("./result.xlsx", buffer, function (err)
    {
      if (err)
        throw err;
      console.log("Write to xls has finished");

// 读xlsx
//     var obj = xlsx.parse("./" + "resut.xls");
//     console.log(JSON.stringify(obj));
    }
  );
}

导出的excel文件如下,每一页的数据都是一个sheet,比较清晰明了:

我们可以很清楚的从中看出目前西安.net的招聘情况,之后也可以考虑用更形象的图表方式展示爬到的数据,应该会更加直观!

总结

其实整个爬虫过程并不复杂,注意就是注意的小点很多,比如async的各个方法的使用以及导出设置header等,总之,也是收获满满哒!

源码

gitbug地址: https://github.com/fighting12...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95927.html

相关文章

  • Python 爬虫-模拟登录知乎-爬取勾网职位信息

    摘要:本文代码地址爬取豆瓣电影爬取拉勾网职位信息模拟登陆知乎为什么没人给我点赞。职位名职位信息运行结果模拟登录知乎通过开发者工具,获取的数据。 我开通了公众号【智能制造专栏】,以后技术类文章会发在专栏。用Python写爬虫是很方便的,最近看了xlzd.me的文章,他的文章写的很到位,提供了很好的思路。因为他的文章部分代码省略了。下面是基于他的文章的三个代码片段:基于Python3,Pytho...

    joyvw 评论0 收藏0
  • 使用php 爬取勾网 的php 招聘信息~

    摘要:拉勾网的爬虫还是有一定的难度的所以我们今天就爬取试一下其实并没有太大的难度只要我们用好分析一下请求就会其实没有什么难度上代码亲测可用拉钩代码 拉勾网的爬虫还是有一定的难度的 所以我们今天就爬取试一下 其实并没有太大的难度 只要我们用好network 分析一下请求 就会其实没有什么难度 上代码 2019-05-22 亲测可用 拉钩代码

    CoderDock 评论0 收藏0
  • ?Echarts统计勾网招聘信息(scrapy 爬取

    摘要:因为本人在成都从事前端,所以这次爬取的关键词既是成都,前端。仅仅有这个是不够的,因为貌似拉勾网有反爬虫,没有好像得不到数据这个还待论证,至少我这边是。 前言 showImg(https://segmentfault.com/img/bV1g4S?w=700&h=490); 今天是2018的第一天,首先祝各位小伙伴元旦快乐!又到了新的一年,虽然离春节还有一段时间,但是程序狗打工不易啊,不...

    genefy 评论0 收藏0
  • ?Echarts统计勾网招聘信息(scrapy 爬取

    摘要:因为本人在成都从事前端,所以这次爬取的关键词既是成都,前端。仅仅有这个是不够的,因为貌似拉勾网有反爬虫,没有好像得不到数据这个还待论证,至少我这边是。 前言 showImg(https://segmentfault.com/img/bV1g4S?w=700&h=490); 今天是2018的第一天,首先祝各位小伙伴元旦快乐!又到了新的一年,虽然离春节还有一段时间,但是程序狗打工不易啊,不...

    Jingbin_ 评论0 收藏0
  • 区块链招聘信息爬取与分析

    摘要:最近在研究区块链,闲来无事抓取了拉勾网上条区块链相关的招聘信息。拉勾网的反爬虫做的还是比较好的,毕竟自己也知道这种做招聘信息聚合的网站很容易被爬,而且比起妹子图这种网站,开发的技术水平应该高不少。 最近在研究区块链,闲来无事抓取了拉勾网上450条区块链相关的招聘信息。过程及结果如下。 拉勾网爬取 首先是从拉勾网爬取数据,用的requests库。拉勾网的反爬虫做的还是比较好的,毕竟自己也...

    kelvinlee 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<