Nodejs Crawler - Cover

上次介紹了網頁前端資料直爬的手法,但當資料量很大或是需要費時處理時,並不適合在網頁上做,否則會讓我們讀者的電腦當掉。因此,後端資料爬取仍不可或缺,今天我們就來看看如何使用 nodejs 在自己的電腦上寫爬蟲程式。

NODEJS

先假設各位已經會寫 Javascript 了但不一定有 nodejs 的經驗。 nodejs 是 Javascript 的直譯器,讓你可以在自己的電腦上用 Javascript 來撰寫程式。他提供了一套 API 讓你與本機電腦互動,API 都包裝成物件的形式,例如 fs 物件提供本機檔案存取的 API 、 JSON 物件則幫你做物件與字串間轉換 ( serialization & deserilzation ) 。基本上只要大致上了解這些 API 的用法,寫 Javascript 就很像是在瀏覽器端撰寫一樣,差別只是執行環境所提供給你的 API 不太一樣而已。

( 你可能會好奇,為什麼一定要用 Javascript 爬資料?小編自己其實傾向於前後端都使用 Javascript ,這樣程式碼只要有適當的包裝,就可以兩邊共用而不需要用兩種不同的語言寫一樣用途的程式碼了 )

大致上了解了 nodejs 以後,讓我們來安裝一下 nodejs 的執行環境吧!在 windows 環境上,只要到 https://nodejs.org/download/ 直接下載安裝檔來執行即可;而在 Linux 或 Mac 環境下,我建議參考這個網頁提供的作法,在終端機中鍵入下列指令(參考How To Install Using a PPA 段):

  curl -sL https://deb.nodesource.com/setup | sudo bash -
  sudo apt-get install nodejs
  sudo apt-get install build-essential

接著試著執行 node –version ,看看程式輸出結果是不是類似下列:

  v0.10.26

NPM

nodejs 除了提供一個 Javascript 的執行環境外,他也提供了一個管理套件的工具 npm ( nodejs package manager ),讓你很容易可以與他人交換各自擴充的 nodejs 套件。為了要撰寫爬蟲程式,我們要使用下列兩個擴充套件:

  • request - HTTP 客戶端輔助工具
  • cheerio - Server 端的 jQuery 實作

安裝套件的方式很簡單,執行下列指令即可:

npm install request cheerio

npm 會在你執行指令的目錄下建立 node_modules 資料夾來存放擴充套件,並且當你執行 Javascript 程式碼時, nodejs 會到這個目錄下尋找你用到的套件。使用的套件也相當的簡單,以下列程式碼引入 request 及 cheerio ,就可以用同名物件來存取他們的 API:

  var request = require("request");
  var cheerio = require("cheerio");

好啦,該準備的事情都做好了,讓我們來看看資料要怎麼爬。

REQUEST

request 這個模組可以幫你下載資料。使用方式:

  request({
    url: "你想抓的網址",
    method: "GET"
  }, function(e,r,b) { /* Callback 函式 */
    /* e: 錯誤代碼 */
    /* b: 傳回的資料內容 */
  });

隨便來個例子,假設你覺得 blog.infographics.tw 真是極品部落格,你想要隨時監控他的內容,那你就這樣寫

  request({
    url: "http://blog.infographics.tw",
    method: "GET"
  }, function(e,r,b) {
    if(!e) console.log(b);
  });

相當簡單吧!

CSS Selector

剛剛我們成功的把 blog.infographics.tw 首頁取下了,但他是 HTML 碼,並不是我們想像的「資料」。幸好 HTML 是相當結構化的格式,我們可以透過分析他的結構來取得我們要的資料。這時候就不得不提「CSS Selector」這個東西了。

CSS Selector 原本用於 CSS,用來在網頁指定一群標籤以便為他們套用 CSS 樣式。而這個「指定標籤」的能力也就可以讓我們來選出一群有特定規律的標籤,比方說 “<H3>" 、 “<div class=’title’><span>…</span></div>" 這樣的東西。

以 blog.infographics.tw 來說,若深入看網站首頁的源碼,你會發現文章以一個一個 LI 的形式包裝,這個 LI 有 item 這個類別,而標題則存在 LI 下的 H2 標籤中。若想取出所有的標題,可以用這樣的 Selector :「li.item h2」

這邊不另外說明 CSS Selector ,因為他的內容量其實已經可以再寫成另外一篇文章了,有興趣的朋友可以參考 W3C School 的說明

CHEERIO

如果你有用過 jQuery ,你大概對 CSS Selector 不陌生,要透過 CSS Selector 取得對應的標籤(比方說前面講的標題),你只需要:

  $("li.item h2")

那麼問題來了,在本機電腦上並沒有 jQuery 可以用,也沒有瀏覽器幫你建好 DOM Tree。該怎麼辦呢?剛好這邊有個 Server 端的 jQuery 實作叫 Cheerio ,那就來用用看吧!

Cheerio 的使用方式也是相當簡單,把整個 HTML 扔給他,他就會送你一個「$」,就可以拿來跑 CSS Selector 了:

  $ = cheerio.load(blogHTMLString);
  titles = $("li.item h2");

接下來你大概也知道怎麼辦了,利用 jQuery 的 text 函式取得標籤的內容:

  var result = [];
  for(i=0;i<titles.length;i++) { result.push($(titles[i]).text()); }

JSON

標題都取得以後,我們可以利用 JSON 跟 fs API 來儲存結果:

  fs.writeFileSync("result.json",JSON.stringify(result));

於是最終爬下來的資料就放在 result.json 裡了。

WRAP UP

讓我們來看看完整的程式碼大概會長什麼樣子吧!

  var request = require("request");
  var fs = require("fs");
  var cheerio = require("cheerio");
  request({
    url: "http://blog.infographics.tw",
    method: "GET"
  }, function(e,r,b) {
    if(e || !b) { return; }
    var $ = cheerio.load(b);
    var result = [];
    var titles = $("li.item h2");
    for(var i=0;i<titles.length;i++) {
      result.push($(titles[i]).text());
    }
    fs.writeFileSync("result.json", JSON.stringify(result));
  });

執行結果大概會有點像下面這個樣子:

  [
    "誰說前端爬不了資料?使用 crossorigin.me",
    "看過圖表社交平台嗎? - 使用 plot.ly 製作圖表",
    "統計圖表製作 - 使用 RAW","只有圖表卻想要資料?讓 D3 Deconstructor 幫你!",
    "千萬不要犯,7 個常見的圖表錯誤",
    "統計圖表製作 – 使用 d3-generator","統計圖表製作 - 使用 infogr.am",
    "復航事件、人失誤與設計心理學","歷年台灣航空事故圖表",
    "六個訣竅,改進你的視覺化作品"
  ]

到這邊為止,你已經知道怎麼從一個網頁上取得資料了,那麼接著要做的也就不困難了 - 把預計要抓的網址建成一個列表,然後逐項的去做上面這樣的處理。相信不會太困難才對,就教給你自己來做嘗試囉!

結語

這篇文章大致上介紹了網路爬蟲最基本的做法,一些簡單的爬蟲透過這樣的原理便可以製作。然而,網頁爬蟲程式其實要應付的問題不光只是技術的基本問題,如何應變各種不同的狀況,則是更進階的問題,例如:

  • throttling - 有些網站碰到大量的存取時,會逐漸降低同來源的優先序,直到最後完全不再處理,使得爬蟲需要花非常久的執行時間來執行。
  • cookie verification - 有些網站需要我們確認後才能進入,亦或者是我們要的資料藏在特定的步驟之後。這時候我們必須再額外對不同網址呼叫 request ,並紀錄回傳的 cookie 值
  • agent verification - Headers in HTTP Request 會帶入瀏覽器的版本資訊,有些網站會以此做依據判斷我們是否是爬蟲程式。request 可以帶入自訂的 Header ,所以很容易可以騙過網站
  • reCAPTCHA - 常見的網站認證碼欄,一張圖片要你填入看到的數字或字母,正確送出後才讓你讀取。因為程式要正確的認出圖片中的資訊並不容易,這往往很不容易對付。

不過,畢竟我們的主題還是關於資料視覺化 / 資訊圖表,撰寫網路爬蟲的議題也許點到為止就好了,所以就到此打住,有興趣的朋友可以再針對這裡所提到的這些問題各別去深入研究囉!


Written by infographics.tw

6 Comments

TWNCB

請問,如果網站的內容是透過js動態載入的話,同樣的方式也爬得到後來才動態載入的資料嗎?
謝謝

Reply

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *