どうもこたにんです。
本日は単発技術記事です。
WEBスクレイピングしたいと思ったことはありませんか?
ということで今回は、Google Apps Script (GAS) でWEBスクレイピングをしてみましょう。
はい、目次どん!
今回やること
GASでWEBサイトのHTMLをパースして任意の値を定期的に取得する。
Parserライブラリ
GAS上でパースしてくれるよさげなライブラリParserというのがあるようです。
ソースコードも公開されております、すごいシンプルなつくり。
せっかくなのでソースコード見ながらリファレンスを作ってみましょうか。
.data(content)
引数contentを持ったParserインスタンスを生成します。
contentは文字列を持たせておきます。
GASでHTML GETしてそれをまま使うやり方。
var html = UrlFetchApp.fetch('https://hogehoge/').getContentText();
var parser = Parser.data(html);
ここで格納したdataがパースの対象になっていきます。
.from(pattern, [offset])
引数patternで指定したところから文字列を抜き出すようにパターンをセットします。
offsetを指定するとオフセットをセットできます、デフォルトは0。
.to(pattern, [offset])
引数patternで指定したところまで文字列を抜き出すようにパターンをセットします。
こちらも同じくoffsetを指定するとオフセットをセットできます、デフォルトは0。
.offset(index)
.data()で格納している文字列にオフセットを指定しておくことができます。
基本的に指定しなくても問題ないですが、文字列が長すぎるときに無駄な走査をなくせそう。
.setDirection(way)
走査の順番を指定できるみたいです。
通常、from→toの順番で文字列を抜き出しますが、これを指定すると逆になります。
ちょっといろいろ考えたけど有用となる場面が思いつかず。。
ちなみに指定するwayは "from" 以外の文字列であればなんだってOKです。
.setLog()
ログ出力モードです、デバッグのときに便利。
各種パラメータの内容、from,to で文字列が見つかったindex値などを出力できます。
.build()
実際に文字列を抽出する実行メソッドです。
上にあるいくつかのメソッドを組み合わせ、ようやく実行されます。
例えば、とあるul-li構造のHTMLタグがあったとします。
<ul>
<li>りんご</li>
<li>うなぎ</li>
<li>おもち</li>
</ul>
ここからliタグのみを抜き出すとどうなるか。
var html = '<ul><li>りんご</li><li>うなぎ</li><li>おもち</li></ul>';
var data = Parser.data(html).from('<li>').to('</li>').build();
// 返却値: りんご
.build()で返却されるのは文字列ですので、この場合は最初に見つかった りんご が返却されます。
うなぎとおもちは、残念でした。
.iterate()
これは文字列のリストを抽出するメソッドです。
先ほどと同じHTMLをiterate()で抽出してみます。
var html = '<ul><li>りんご</li><li>うなぎ</li><li>おもち</li></ul>';
var data = Parser.data(html).from('<li>').to('</li>').iterate();
// 返却値: [りんご, うなぎ, おもち]
.iterate()で返却されるのは文字列の配列になります、なんか便利そう。
ここまでがParserの各メソッドの紹介でした。
すごく簡単にHTML内の任意の値を取り出せそうです、使ってみよう。
DOMのパースといいつつ、文字列抽出なライブラリなので、実はHTML以外にも使えそう。
データクリーニングなどに応用が効くかもしれないですね。
次は、このParserライブラリを実際にGAS上にインポートしていく方法です。
GAS上にライブラリをインポートする
GASのメニューから、リソース→ライブラリ... を選択します
ライブラリが突然の「図書館」になってて素敵だなと感じつつ、ライブラリのキーを追加します。
以下のキーをコピーしてテキストエリアに乗っけて追加。
M1lugvAXKKtUxn_vdAG9JZleS6DrsjUUV
追加されたらバージョンを指定して保存します、これでライブラリのインポートおわり。
スクリプトを書く
今回は、とあるサイトの記事一覧のタイトルを取得する、というシナリオで考えます。
そのとあるサイトの記事一覧のHTML階層が以下のようになっているとします。
:
<ul class="entries">
<li class="entry">
<div class="title">記事タイトル1</div>
<div class="category">技術</div>
</li>
<li class="entry">
<div class="title">記事タイトル2</div>
<div class="category">日記</div>
</li>
<li class="entry">
<div class="title">記事タイトル3</div>
<div class="category">技術</div>
</li>
</ul>
:
entriesの中に、entryが複数ある。
entryの中に、タイトルとカテゴリがある。
ここから、タイトルたちだけをスマートに抜き出しましょう。
var data = Parser.data(html).from('<div class="title">').to('</div>').iterate();
はい、これだけですね。
これでdataに配列でタイトル一覧が入ってきます。
あとはその配列をお好きに調理する。
では、技術カテゴリのタイトルたちだけを抜き出しましょう。
var entries = Parser.data(html).from('<li class="entry">').to('</li>').iterate();
var titles = [];
for each(var entry in entries) {
var category = Parser.data(entry).from('<div class="category">').to('</div>').build();
if (category.indexOf("技術") === -1) {
continue;
}
var title = Parser.data(entry).from('<div class="title">').to('</div>').build();
titles.push(title);
}
entryのlistを作っておいて、for eachで回す。
Parser.data() に指定する文字列はHTML全文でなくても、文字列ならなんでもアリです。
なので今回は1つのentry内の文字列を対象にさらに走査しています。
これで欲しい値が欲しい感じで取得できましたね。
あとは、取得したデータをどう扱うのかは、実際に作りたいもの次第ですね。
どこかにデータをためるのか、WebHookするのか、何かをPostするのか。
それ次第で、後続処理を好きなように書き加えていきましょう。
トリガーを作る
作ったスクリプトを定期的に実行させるトリガーを作りましょう。
G Suits Developer Hub のトリガー一覧にアクセスします。
GASのこの時計アイコンからいけます。
右下にトリガー追加ボタンがあるのでそれをクリックして、トリガーの設定を行います。
- イベントのソースを選択:時間主導型
- 時間ベースのトリガーのタイプを選択:特定の日時/分/時間/日付/週/月 から
時間ベースのトリガータイプによって、詳細の設定が変わってきます。
例えば、分ベースにすると、1/5/10/15/30分おきから選べる、というしくみ。
お好きな間隔で指定しましょう。
すべての指定が完了し、保存すればこれであとは勝手に動きます。
動いているかどうかは、今作ったトリガーの詳細を開けば閲覧できます。
このようなかたちで、エラー率や実行数などがわかります。
まとめ
以上で、GASでWEBサイトのHTMLをパースして任意の値を定期的に取得する、ができました。
すごく簡単に作れちゃいますね。
ただ、悪用は気をつけてください。
サイトに優しいスクレイピングを、ネチケット大事。