ホームページ  >  記事  >  ウェブフロントエンド  >  ノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。

ノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。

不言
不言オリジナル
2018-07-07 17:55:392050ブラウズ

この記事では、Lagou.com のデータをクロールして Excel ファイルにエクスポートするためのノードを紹介します。これを必要な友人に共有します。

私はノードを学習しています。 js の前に断続的に書いていますが、今日は Lagou.com を使って練習し、データから最近の採用市場について学びましょう!私はノードには比較的慣れたばかりですが、皆さんと一緒に学び、進歩していきたいと思っています。

1. 概要

最初に具体的なニーズを明確にする必要があります:

    ノード インデックスの都市の位置を通じて関連情報をクロールできます
  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),然后查看控制台,找到相关的请求,如图:

ノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。

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

通过superagent请求数据

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

ノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。

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

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

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

ノードは Lagou.com データをクロールし、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ノード インデックスを入力することもできますstart 事前定義された都市と位置の配列を直接クロールし、ループしてさまざまな都市のさまざまな求人情報をクロールします

最終的なクロール結果をローカルの ./data ディレクトリに保存します 次へ
    li>
  1. 対応する Excel ファイルを生成し、ローカルに保存します

🎜 2. クローラーによって使用される関連モジュール🎜
    🎜 🎜fs: に使用されます。システムファイルとディレクトリの読み取りと書き込み🎜🎜🎜async: プロセス制御🎜🎜🎜superagent: クライアントリクエストプロキシモジュール🎜🎜🎜node-xlsx: ファイルを特定の形式で Excel にエクスポート🎜
🎜 クローラーの 3 つの主なステップ: 🎜

プロジェクトを初期化する

🎜 新しいプロジェクト ディレクトリを作成する🎜🎜 適切なディスク ディレクトリ -crwl-lagou にプロジェクト ディレクトリ ノードを作成する🎜🎜プロジェクトを初期化する🎜🎜🎜🎜🎜node-crwl-lagouフォルダーに入る🎜🎜🎜npm initを実行し、package.jsonファイルを初期化する🎜🎜🎜インストール依存関係パッケージ🎜 🎜🎜🎜🎜npm install async🎜🎜🎜npm install superagent🎜🎜🎜npm install node-xlsx🎜🎜

コマンドライン入力の処理

🎜コマンドラインに入力された内容については、process.argv を使用して取得できます。配列内の各項目はユーザーが入力した内容です。
nodeindexregionalpositionnodeindexstart を区別する最も簡単な方法は、process.argv の長さを判断することです。長さが 4 の場合は、それを呼び出します。クローラーのメイン プログラムはデータをクロールします。長さが 3 の場合は、事前定義された都市と位置の配列を介して URL を結合し、async.mapSeries を使用してループ内でメイン プログラムを呼び出す必要があります。コマンド分析用のホームページ コードは次のとおりです: 🎜
(cb) => {
      for (let i=0;Math.ceil(i<totalpage>🎜 事前定義された都市と位置の配列は次のとおりです: 🎜<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
    }
🎜 次のステップは、クローラーのメイン プログラム部分の分析です。 🎜

ページを分析してリクエスト アドレスを見つけます

🎜まず、Lagou.com のホームページを開き、クエリ情報 (ノードなど) を入力し、次に示すように、コンソールを確認して関連するリクエストを見つけます。画像内: 🎜🎜2217504720-5b3c89dd656e7_articlex[ 1].png🎜🎜これは、投稿リクエスト https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false が必要なものです。さまざまなデータは、次の方法で取得できます。 3 つのリクエスト パラメータと簡単な分析が可能です。パラメータ first は、現在のページが最初のページであるかどうかをマークします。true は「はい」を意味し、false は「いいえ」を意味します。は現在のページ番号です。パラメータ kd code> はクエリ入力の内容です。 🎜<h3>スーパーエージェントを介したデータのリクエスト</h3>🎜 まず第一に、プログラム全体が非同期であることを明確にする必要があり、async.series を使用してプログラムを順番に呼び出す必要があります。 <br>分析によって返された応答を表示します: 🎜🎜<img src="https://img.php.cn//upload/image/561/170/373/1530957314268879.png" title="1530957314268879.png " alt="ノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。">🎜🎜content.positionResult.totalCount が必要なページの総数であることがわかります。<br>スーパーエージェントを使用して投稿リクエストを直接呼び出し、コンソールには次の情報が表示されます: 🎜<pre class="brush:php;toolbar:false">function exportExcel() {   let list = fs.readdirSync('./data')   let dataArr = []   list.forEach((item, index) =&gt; {     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) =&gt; {       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(&quot;./&quot; + &quot;resut.xls&quot;); //     console.log(JSON.stringify(obj));     }   ); }</pre>🎜 これは実際には、リクエスト ヘッダーを追加するだけです: 🎜🎜<img src="https://img.php.cn%20//upload/image/571/168/824/1530957321762012.png" title="1530957321762012.png" alt="ノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。">🎜 🎜次に、スーパーエージェントを使用して投稿リクエストを呼び出します。メインコードは次のとおりです:🎜rrreee🎜 総ページ数を取得した後、<code>総ページ数/15 のループを通じて pn パラメーターを取得できます。すべての URL を生成して url に保存するには:🎜rrreee🎜 すべての URL を使用すると、データを取得するたびにスーパーエージェントの post メソッドを使用してすべての URL を要求し続けることは難しくありません。 、データ ディレクトリに json ファイルを作成し、返されたデータを書き込みます。これは簡単そうに見えますが、注意すべき点が 2 つあります: 🎜🎜🎜🎜 同時リクエストが多すぎて IP がブロックされるのを防ぐには、URL をループするときに、async.mapLimit メソッドを使用して同時実行性を制御する必要があります。から 3 まで、各リクエストの後に 2 つのリクエストを渡す必要があります。数秒以内に次のリクエストを送信します🎜。
  • 在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文件如下:
    ノードは Lagou.com データをクロールし、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,比较清晰明了:
    ノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。

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

    总结

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

    源码

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

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

    相关推荐:

    以上がノードは Lagou.com データをクロールし、Excel ファイルにエクスポートします。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。