2019年7月15日

使用 Google Apps Script 爬網頁資料,解析 HTML 及操作 DOM 的技巧

使用 Google Apps Script 爬網頁資料,解析 HTML 及操作 DOM 的技巧

Wayne Fu 0 A+
前陣子接到的需求,要使用爬蟲程式撈特定網站資料回來,那麼利用 Google 試算表是不錯的選擇,除了每個儲存格的容量最多有 50000 個字元,還可用 Google Apps Script(以下簡稱 GAS) 執行爬蟲程式、處理各種細節。

不過撈完網頁內仍後,如何解析 HTML 架構、操作頁面上的 HTML 元素都有不少難題要解決,因此本篇記錄一下 GAS 的操作心得與技巧。

(圖片出處: pixabay.com)


一、解析 HTML


1. 爬網頁原始碼

由於操作平台是 GAS,需要先瞭解爬網頁資料的語法:

var response = UrlFetchApp.fetch("https://www.wfublog.com/"),
content = response.getContentText();

以上語法可很簡單取得 WFU BLOG 的網頁 HTML 內容字串。


2. 操作 XmlService

GAS 用來解析 HTML 語法的官方文件如下:


官方也附了簡單的範例,使用 XmlService.parse() 就能解析網頁原始碼。

但先說結論,這是沒有用的,因為 XmlService 只能解析結構完整的 XML 文件,而 HTML 語法結構並沒有 XML 那麼嚴謹,所以不是每個網頁都能解析。

可參考這篇「XML文件的結構」,每個元素都要有開始及結束標記,例如:

<link></link>

但 HTML 文件以下標記方式(開始即結束)是允許的:

<link /> <input />

那麼一個正常的網頁內容,使用 XmlService.parse() 解析都會報錯,這就非常麻煩了。

硬要處理的話只能先用正規表示式,一一修改 link、input 等等各種不合 XML 規範的結束標記,才能使用 XmlService.parse()。


3. GAS 舊的 XML 工具

找到這篇比較好的解法「What is the best way to parse html in google apps script」,提到 GAS 舊版本的 XML 這個工具可以解析 HTML,參考語法如下:

var page = UrlFetchApp.fetch(contestURL);
var doc = Xml.parse(page, true);
var bodyHtml = doc.html.body.toXmlString();
doc = XmlService.parse(bodyHtml);
var root = doc.getRootElement();

先用 Xml 解析 HTML 後,再轉成 XML 字串,就能使用 XmlService 來解析內容了。

需要注意的是,XML 這個工具已被 Google 棄用(deprecated),隨時有可能失效。



二、操作 DOM


如果是前端的話,用 jQuery 操作取得的 HTML 內容非常簡單方便,但可惜的是 GAS 無法使用 jQuery,而且官方提供的工具還非常難用,比前端的 HTML API 還爛,所以國外有網友自行做了幾個方便的函數,可參考「Parsing HTML」。

這個頁面主要模擬 HTML API,做出了三個最常用的 DOM 操作工具:

  • getElementById
  • getElementsByClassName
  • getElementsByTagName


範例程式碼如下,複製到自己的 GAS 即可執行:

function getElementById(element, idToFind) {
var descendants = element.getDescendants();
for(i in descendants) {
var elt = descendants[i].asElement();
if( elt !=null) {
var id = elt.getAttribute('id');
if( id !=null && id.getValue()== idToFind) return elt;
}
}
}

function getElementsByClassName(element, classToFind) {
var data = [];
var descendants = element.getDescendants();
descendants.push(element);
for(i in descendants) {
var elt = descendants[i].asElement();
if(elt != null) {
var classes = elt.getAttribute('class');
if(classes != null) {
classes = classes.getValue();
if(classes == classToFind) data.push(elt);
else {
classes = classes.split(' ');
for(j in classes) {
if(classes[j] == classToFind) {
data.push(elt);
break;
}
}
}
}
}
}
return data;
}

function getElementsByTagName(element, tagName) {
var data = [];
var descendants = element.getDescendants();
for(i in descendants) {
var elt = descendants[i].asElement();
if( elt !=null && elt.getName()== tagName) data.push(elt);
}
return data;
}




三、常用功能


另外也提供一些 GAS 我常會用到的幾個 DOM 操作工具:

1. 刪除元素

function removeElement(element) {
element.getParentElement().removeContent(element);
}


2. 屬性值

取得圖片網址 IMG 的 src:

imgElement.getAttribute("src").getValue()

設定圖片網址 IMG 的 src:

imgElement.setAttribute("src", "圖片網址");


3. 字串

取得整個元素的 HTML 字串:

XmlService.getCompactFormat().format(element);

取得元素的文字:

element.getText();

設定元素的文字:

element.setText("字串內容");


更多 Google Apps Script 相關技巧:
0 0
如這篇文章對你有幫助,歡迎「分享」到 FB、「追蹤」粉絲團、「訂閱」最新文章

2 則留言:

  1. 解析 html 也許可以試試 npm 的模組, 像 jsdom 或 cheerio 。 但要在 app script 上執行要靠 webpack 甚至 babel 。

    回覆刪除
    回覆
    1. 謝謝提供方向,查了一下,在 apps script 可以想辦法載入 node.js 的一些模組,看起來會方便一些。

      刪除

張貼留言注意事項:

◎ 勾選「通知我」可收到後續回覆的mail!
◎ 請在相關文章留言,與文章無關的主題可至「Blogger 社團」提問。
◎ 提問若無法提供足夠的資訊供判斷,可能會被無視。建議先參考這篇「Blogger 提問技巧及注意事項」。
◎ CSS 相關問題非免費諮詢,建議使用「Chrome 開發人員工具」尋找答案。
◎ 手機版相關問題請參考「Blogger 行動版範本的特質」→「三、行動版範本不一定能執行網頁版工具」。
◎ 非官方範本問題、或貴站為商業網站,請參考「Blogger 免費諮詢 + 付費諮詢
◎ 若是使用官方 RWD 範本,請參考「Blogger 推出全新自適應 RWD 官方範本及佈景主題」→ 不建議對範本進行修改!
◎ 若留言要輸入語法,"<"、">"這兩個符號請用其他符號代替,否則語法會消失!
◎ 為了過濾垃圾留言,所有留言不會即時發佈,請稍待片刻。
◎ 本站「已關閉自刪留言功能」。

TOP