首頁 > web前端 > js教程 > 主體

node爬取拉勾網資料並匯出為excel文件

不言
發布: 2018-07-07 17:55:39
原創
1998 人瀏覽過

這篇文章主要介紹了關於node爬取拉勾網數據並導出為excel文件,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

前言

之前斷斷續續學習了node.js,今天就拿拉勾網練練手,順便透過資料了解了解最近的招募行情哈! node方面算是萌新一個吧,希望可以和大家共同學習和進步。

一、概要

我們首先需要明確具體的需求:

  1. 可以透過node index 城市職位來爬取相關資訊

  2. 也可以輸入node index start直接爬取我們預先定義好的城市和職位數組,循環爬取不同城市的不同職位資訊

  3. 將最終爬取的結果儲存在本地的./data目錄下

  4. 產生對應的excel文件,並儲存到本地

二、爬蟲用到的相關模組

  • #fs: 用來對系統檔案及目錄進行讀寫操作

  • #async:流程控制

  • superagent:客戶端請求代理模組

  • node-xlsx:將一定格式的檔案匯出為excel

三、爬蟲主要步驟:

初始化專案

新專案目錄

在適當的磁碟目錄下建立項目目錄node-crwl-lagou

初始化項目

  1. #進入node-crwl-lagou資料夾下

  2. 執行npm init,初始化package.json檔案

安裝依賴套件

  1. npm install async

  2. npm install superagent

  3. 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 預先定義好的城市和職位數組如下:<p></p><pre class="brush:php;toolbar:false">{
    "city": ["北京","上海","广州","深圳","杭州","南京","成都","西安","武汉","重庆"],
    "position": ["前端","java","php","ios","android","c++","python",".NET"]
}
登入後複製
接下來就是爬蟲主程式部分的分析了。

分析頁面,找到請求位址

首先我們打開拉勾網首頁,輸入查詢資訊(例如node),然後查看控制台,找到相關的請求,如圖:

node爬取拉勾網資料並匯出為excel文件

這個post請求

https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false就是我們所需要的,透過三個請求參數來取得不同的數據,簡單的分析就可得知:參數first是標註目前是否是第一頁,true為是,false為否;參數pn是當前的頁碼;參數kd是查詢輸入的內容。

透過superagent請求資料

首先需要明確得是,整個程式是異步的,我們需要用async.series來依序呼叫。

查看分析回傳的response:

node爬取拉勾網資料並匯出為excel文件

可以看到content.positionResult.totalCount就是我們所需要的總頁數

我們用superagent直接呼叫post請求,控制台會提示以下資訊:

{'success': False, 'msg': '您操作太频繁,请稍后再访问', 'clientIp': '122.xxx.xxx.xxx'}
登入後複製
這其實是反爬蟲策略之一,我們只需要給其新增一個請求頭即可,請求頭的取得方式很簡單,如下:

node爬取拉勾網資料並匯出為excel文件

然後在用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<totalpage>有了所有的url,在想爬到所有的資料就不是難事了,繼續用superagent的post方法循環請求所有的url,每一次取得到資料後,在data目錄下建立json文件,將傳回的資料寫入。這裡看似簡單,但是有兩點要注意:<p></p>
<ol class=" list-paddingleft-2">
<li>為了防止並發請求太多而導致被封IP:循環url時候需要使用async.mapLimit方法控制並發為3,每次請求完都要過兩秒在發送下一次的請求<p></p>
</li>
<li><p>在async.mapLimit的第四个参数中,需要通过判断调用主函数的第三个参数是否存在来区分一下是那种命令输入,因为对于<code>node index start</code>这个命令,我们使用得是async.mapSeries,每次调用主函数都传递了<code>(city, position, callback)</code>,所以如果是<code>node index start</code>的话,需要在每次获取数据完后将null传递回去,否则无法进行下一次循环</p></li>
</ol>
<p>主要代码如下:</p>
<pre class="brush:php;toolbar:false">// 控制并发为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文件如下:
node爬取拉勾網資料並匯出為excel文件

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,比较清晰明了:
node爬取拉勾網資料並匯出為excel文件

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

总结

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

源码

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

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

以上是node爬取拉勾網資料並匯出為excel文件的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!