google試算表+Javascript爬蟲

Google試算表爬蟲:使用簡易Javascript定時抓取競爭對手價格(上)

不會python沒關係,Google試算+簡易javascript就能定時抓取競爭對手價格

最近接到一個新任務是定時抓取競爭對手產品價格,因為產品品項太多,每個禮拜都要去每個產品網頁確認價格是否有變動,真的是一件費心又費時的事。
於是開啟了寫簡單爬蟲的任務,也才發現,哇!Google試算表後面還可以寫code呢?
廢話不多說,直接開始吧!

靜態網頁vs動態生成網頁 抓取的方式不同

以抓取pchome的價格為例,pchome商店街和pchome 24H寫法就不同,商店街的價格是靜態生成的頁面,而Pchome 24H屬於動態生成的價格,所以首先就要先判斷他是哪一個類型的網站才可以決定用什麼方式來寫爬蟲。
那要怎麼判斷靜態還是動態呢?

Step1: 找出價格的程式碼


找一個產品頁面,對著你要pchome價格,右鍵給他點下去「檢查」 ,看他是under在哪一個class底下,把這串class複製起來,或是把整串div或是span複製起

對著價格點右鍵 > 檢查

Step 2: 網頁右鍵進入檢視原始碼,找是否有相同的程式碼


得到剛剛html之後,再回到商品頁右鍵「檢視網頁原始碼」,這時就會看到密密麻麻的網頁資訊啦! 這麼多要怎麼找? 「Ctrl +F」搜尋,直接貼上剛剛複製的那一段,也就是<span style=”font:bold 27px Verdana;color:#ff0000″>29900</span>

如果沒有,那就表示他是動態載入的資料,這裡就不適合你啦! 像是Pchome的24小時購物就是屬於動態生成的價格;如果有找到,恭喜你,繼續看下去,代表他是靜態的資料,而不是後來才抓進來的,方法會簡單很多。

判斷完靜態或動態的頁面後,接下來就到程式碼的部分,因為靜態相對來說比較簡單、比較好抓,所以這篇會先寫靜態的部分,在下一篇會再解釋動態生成的價格要怎麼抓

靜態網頁javascript 爬蟲

前置準備:
1. 新增三個工作分頁
主要是為了看在抓取價格的時候,較容易看哪個環節出問題。
2. 將產品新增對應網址
若欲抓取的產品有很多項,可以先以你喜歡的方式排版,並將對應的網址放在該列中,以我的例子來說,第一欄用來放pchome商店街該商品的網址,等於說讓爬蟲來去爬去爬這一欄所有產品的網址,而第二欄就是我自己方便看的產品名稱。

接下來就是主要程式碼的部分

Step1. 將抓下來的資料編碼成中文

這兩個function主要是在把抓下來的資料編碼成中文,讓中文可以正常顯示,而不是出現亂碼

function encode_big5( s ){
  //使用mathod map屬性時,初次編譯會出現 "TypeError: 無法讀取 undefined 的「map」屬      性",為正常現象,無需理會
  if (s.map) {            // 測試資料是否為陣列型態,假如是陣列型態就改為陣列輸出
    return s.map(encode_big5);
  } else{
    return Utilities.newBlob("").setDataFromString(s, "ISO-8859-1").getDataAsString("big5") ;
  }
}

function encode_utf8( s ){
  //使用mathod map屬性時,初次編譯會出現 "TypeError: 無法讀取 undefined 的「map」屬      性",為正常現象,無需理會
  if (s.map) {            // 測試資料是否為陣列型態,假如是陣列型態就改為陣列輸出
    return s.map(encode_utf8);
  } else{
    return Utilities.newBlob("").setDataFromString(s, "ISO-8859-1").getDataAsString("utf8") ;
  }
}

二、抓取價格

這裡就比較複雜了,先講解大致的流程…

Step 1. 將剛剛在「檢查」的程式碼中,對著你要的價格或是標題的那段程式碼,右鍵「Copy」> 「Copy XPath」,然後將複製整串路徑儲存在變數內(以下面例子來說,儲存在queryString_Price,而標題的路徑儲存在queryString_Name)

Step2. for迴圈將所有有資料(網址)的地方都爬過一遍
以範例來說,我要抓的資料及網址是從A3開始,也就是從第三列所以i的初始值是3,再來就是抓取網址的那一欄 .getRange(i,1)

*註: .getRange(a, b) 分別表示 a:第幾列/ b:第幾行*
也就是說若是在for迴圈打.getRange(i,1),爬蟲就會從第i列第一欄開始,再來第i+1列第一欄、第i+2列第一欄….這樣一直下去

 if( urlS1 != ""){
        var testS = '=IMPORTXML("' + urlS1 + '","'+ queryString_Num +'|'+ queryString_Name + '|'+ queryString_Price +'")';
        var testS2 = '=IMPORTXML("' + urlS2 + '","'+official_Private+'")';
        var P_Name = Tmpsheet.getRange(2,i).getValue();
        var P_Price = Tmpsheet.getRange(3,1).getValue();
        var O_Price = Tmpsheet.getRange(5, i).getValue();
        Utilities.sleep(777);
}

若是網址是存在的,就去抓取剛剛的XPath,也就是queryString_Name(要抓取的名字)/ queryString_Price(要抓取的價格),並儲存到testS這個變數裡面

再將testS放到工作表2的表單內

Tmpsheet.getRange(2, i).setValue(testS);

如果有成功,可以看到工作表二出現數字及亂碼,有亂碼的原因是因為抓下來的部分資料為中文,還沒給他轉成UTF8。

抓下來的原始XML資料
工作表2的資料

接下來確認有工作表2有出現抓下來的原始資料之後,將資料轉移到”工作表3″,下面的範例就是把工作表2很像亂碼的地方套上剛剛寫的function,轉成可辨別的中文,放到工作表3。

var P_Name = Tmpsheet.getRange(2,i).getValue();
var P_Price = Tmpsheet.getRange(3,1).getValue();
Tmpsheet3.getRange(i, 3).setValue(encode_big5(P_Name));
Tmpsheet3.getRange(i, 4).setValue(P_Price);

這裡有把工作表2的資料轉向(轉成直向)後到工作表3,讓工作表3的資料排列跟工作表一的排列是一樣的,這樣就可以直接從工作表1直接連結到工作表3的結果。
結果會長的像下圖:

程式的部分就在這裡結束了。

接下來工作表1除了網址跟標題之外其他都是空的阿!
Step3. 將工作表3的資料連到工作表1
看你的工作表3出現的價格在哪一行,在工作表1要放價格的地方點下去(以我的資料來說),在上面打上 =’工作表3′!D3,然後下一個產品接續=’工作表3′!D4…同理一直到最後一個品項,數字就會連結進來了。

完整的程式碼如下,在這個程式碼裡面,有另外去加一項是抓取他們官網的價格,你甚至可以透過這些不同平台的價格,來推算出他們的定價策略。

function getData() {
    var sheetName = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("工作表1");
    var Tmpsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("工作表2");
    var Tmpsheet3 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("工作表3");
    var datarange = sheetName.getDataRange();
    var numRows = datarange.getNumRows();
    var numColumns = datarange.getNumColumns();
    var nextColumn = numColumns + 1;
    var queryString_Num = "//*[@id='inside_content']/table/tr/td[2]/table[1]/tr/td[1]/table/tr[1]/td";
    var queryString_Name = "//h1[@style='font-size:24px;line-height:120%;font-family: Arial;font-weight: bold; padding:0; margin:0; border-bottom:3px solid #C9C9C9;']";
    var queryString_Price= "//span[@style='font:bold 27px Verdana;color:#ff0000']";
    var official_Private ="substring(//font[@class='goodsDescrcost'], 4)";	
    Tmpsheet.clearContents();//清除舊資料,可以不寫直接覆蓋
    Tmpsheet3.clearContents();        
      for (var i=3; i<=numRows; i++)
      {
       var urlS1 = sheetName.getRange(i,1).getValue();
       var urlS2 = sheetName.getRange(i,7).getValue();
       if( urlS1 != "")
       {
        var testS = '=IMPORTXML("' + urlS1 + '","'+ queryString_Num +'|'+ queryString_Name + '|'+ queryString_Price +'")';
        var testS2 = '=IMPORTXML("' + urlS2 + '","'+official_Private+'")';
        var P_Name = Tmpsheet.getRange(2,i).getValue();
        var P_Price = Tmpsheet.getRange(3,1).getValue();
        var O_Price = Tmpsheet.getRange(5, i).getValue();
        Utilities.sleep(777);
        Tmpsheet.getRange(1, i).setValue(new Date());
        Tmpsheet.getRange(2, i).setValue(testS);
        Tmpsheet.getRange(5, i).setValue(testS2);
        //transfer data to sheet3
        Tmpsheet3.getRange(i, 3).setValue(encode_big5(P_Name));
        Tmpsheet3.getRange(i, 4).setValue(P_Price);
        Tmpsheet3.getRange(i, 5).setValue(O_Price);
       }
      }
}

設定定時抓取

每次要看都要再執行一次function難免有些麻煩,好險Google試算表可以設定定期執行的時間區間。
打開google試算表後 > 工具 > 指令馬編輯器,進入之後可以看到一個時鐘的icon點進去就可以設置執行時間了

設定google試算表爬蟲定時執行

通常是一個禮拜定期更新一次,若設定是固定每周五更新一次,你就可以每個禮拜一再來確認,看價格有沒有變動。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *