僕が作ったわけではないのですが、Google Apps ScriptによるスクレイピングというYoutubeの動画から写経して、Google App Scriptのスクレイピングコードを勉強します。
できることとしては、Ted talksの全動画のタイトル、スピーカーをすべてGoogle Spread Sheetに取り出すことができるというものです。(詳しくは動画を見てください。)
Google App Scriptを使って、スクレイピングするメリット
そもそも、Python使ってGoogle Apps Scriptかけばええでねぇかって話なので、なぜGoogle Apps Scriptを使うのかって話から。
- サーバーを準備せずとも、定期実行が簡単
普通にPythonなどで定期実行プログラムを組むと、どうしてもサーバー立てたりする必要があります。ちょっとしたスクレイピングをする定期実行プログラムを組むために、そこまではしたくない!みたいなときに超絶使えるのが、Google Apps Scriptです。
Googleがサーバーを用意してくれるので、定期実行するにしても、サーバーを自前で用意する必要がありません。まずcronが超簡単に使えるので、簡単な定期実行プログラムを組みたいのであれば、Python用意してサーバー立ててDBに保存したいみたいな場合でも、スプレッドシートで完結するので、非常に楽です。
- JavaScriptで書ける
Google Apps Scriptは、基本的にはJavascriptで書けるので、学習コストが低いです。
また、特別にLinuxがいじれたり、DBの構築も必要がありません。Javascriptしか書けないような人でも、かけてしまうと思います。ただし、すこし正規表現の知識がないと、スクレピングは難しいかもしれません。
- データの共有が簡単
スプレッドシートを共有するだけで、データの共有ができるので、さくっとスクレピングしてきたデータを共有するなんてことができます。
https://www.youtube.com/watch?v=xkk7OxsfTpQ
やろうとしていること
僕が作ったわけではないのですが、Google Apps ScriptによるスクレイピングというYoutubeの動画から写経して、Google App Scriptのスクレイピングコードを勉強します1. データのスクレイピング
Ted日本語ページにアクセスし、ページャーの最後までの以下の値を取得します。
- タイトル名
- スピーカー名
- 作成日時
該当するページのHTMLはこんな感じ。
<div class='media__message'> <h4 class='h12 talk-link__speaker'>Justin Baldoni</h4> <h4 class='h9 m5'> <a class=' ga-link' data-ga-context='talks' href='/talks/justin_baldoni_why_i_m_done_trying_to_be_man_enough?language=ja' lang='ja'> 「男らしく」在らんとすることをやめた理由 </a> </h4> <div class='meta'> <span class='meta__item'> Posted <span class='meta__val'> Dec 2017 </span> </span> <span class='meta__row'> Rated <span class='meta__val'> Inspiring, Courageous </span> </span> </div> </div>
タイトル名が
<a class=' ga-link' data-ga-context='talks' href='/talks/justin_baldoni_why_i_m_done_trying_to_be_man_enough?language=ja' lang='ja'> 「男らしく」在らんとすることをやめた理由 </a>
スピーカー名が
<h4 class='h12 talk-link__speaker'>Justin Baldoni</h4>
作成日時が
<span class='meta__val'> Dec 2017 </span>
に該当します。
検索結果のページ番号が、URLのクエリに含まれています。
https://www.ted.com/talks?language=ja&page=2
であれば、page=2、つまり2番目のページが取得できます。
https://www.ted.com/talks?language=ja&page=3
であれば、同様に、3番目のページが取得できます。
なので、N番目のページが取得できるようにするには、最後のページの番号を取得し、一つずつ数字をあげていけば全部のページにアクセスできます。
2. スプレッドシートに書き込み
スクレイピングしたデータを、スプレッドシートに書き込みます。
3. 実際のソースコード
function MainFrame() { var url = "https://www.ted.com/talks?language=ja"; //ted日本語のURL var request = UrlFetchApp.fetch(url) //http responseの取得 var content = request.getContentText(); //最終ページの数字を取得 var step1 = content.match(/page=\d*/g) var pages = []; for (var i=0; i<=step1.length-1;i++){ pages.push(step1[i].replace(/page=/,"")); } var maxnum = Math.max.apply({},pages); //空の配列を作る var Speakers = []; var Titles = []; var Dates = []; //スクレイピング実行部分 for (var i=1;i<=maxnum;i++){ var url = "https://www.ted.com/talks?language=ja" + "&page=" + i; var request = UrlFetchApp.fetch(url); var content = request.getContentText(); var speakers = SpeakersArray(content); var titles = TitlesArray(content); var dates = DateArray(content); for(var n=0;n<=speakers.length-1;n++){ Speakers.push(speakers[n]) } for(var n=0;n<=titles.length-1;n++){ Titles.push(titles[n]) } for(var n=0;n<=dates.length-1;n++){ Dates.push(dates[n]) } } //2次元配列を作成 var Data = []; for(var n=0;n<=Speakers.length-1;i++){ var data = []; data.push(Speakers[n]); data.push(Titles[n]); data.push(Dates[n]); Data.push(data); } //スプレッドシートのid var id= "14OEIoSZ0RodyHgi3krhHuLDQd8k37CDy2Mm8Csrhsdk" var File = SpreadsheetApp.openById(id); var Sheet = File.getSheets(); Sheet[0].getRange(1,1,Speakers.length,3).setValues(Data); } function SpeakersArray(x){ /*実際のHTML <h4 class='h12 talk-link__speaker'>Justin Baldoni</h4> */ //スピーカー名を配列で返す var step1 = x.match(/<h4\sclass='h12\stalk-link__speaker'>.*<\/h4>/g); var extract = [] for (var i=0;i<=step1.length-1;i++){ extract.push(step1[i] .replace(/<h4\sclass='h12\stalk-link__speaker'>/g,"") .replace(/<\/h4>/g,"") .replace(/\n/g)); } return extract; } function TitlesArray(x){ //スピーチタイトルを配列で返す /*実際のHTML <a class=' ga-link' data-ga-context='talks' href='/talks/justin_baldoni_why_i_m_done_trying_to_be_man_enough?language=ja' lang='ja'> 「男らしく」在らんとすることをやめた理由 </a> */ var step1 = x.match(/<a\sclass='\sga-link'\sdata-ga-context='talks'\shref='\/talks\/.*\?language=ja'\slang='ja'>[\w\W]*?<\/a>/g,"") var extract = [] for (var i=0;i<=step1.length-1;i++){ extract.push(step1[i] .replace(/<a\sclass='\sga-link'\sdata-ga-context='talks'\shref='\/talks\/.*\?language=ja'\slang='ja'>/g,'') .replace(/<\/a>/g,"") .replace(/\n/g,"")); } return extract; } function DateArray(x){ //日付を配列で返す /*実際のHTML <span class='meta__val'>Dec 2017</span> */ var step1 = x.match(/<span\sclass='meta__val'>\n*[A-z]{3}\s\d{4}\n*<\/span>/g,"") var extract = [] for (var i=0;i<=step1.length-1;i++){ extract.push(step1[i] .replace(/<span\sclass='meta__val'>/g,"") .replace(/<\/span>/g,"") .replace(/\n/g,"")) } return extract; }
実行すると、スプレッドシートにデータがすべてスクレイピングされ、スプレッドシートに反映されるようになります。ただし、私の環境ではメモリ不足になり確認できなかったので、ほかのタイミングで再現してみたいと思います。
関連資料
UrlFetchApp.fetch()
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app
getContentText() : Gets the content of an HTTP response encoded as a string.
https://developers.google.com/apps-script/reference/url-fetch/http-response
match(): 正規表現を取得するメソッド
https://tonari-it.com/gas-regular-expression/
Math.max.apply:配列の最大値を取得するメソッド
https://qiita.com/YusukeHirao/items/e848f5de40beaa52e002
— Dai Kawai@RubyPython (@never_be_a_pm) February 8, 2018
— Dai Kawai@RubyPython (@never_be_a_pm) February 8, 2018
#GAS gフラグは、正規表現で取得したデータを、配列で複数取得できるようにするコマンド。gフラグなしだと、検索した結果の一つだけしかとれない。 / “【JavaScript入門】matchメソッドから正規表現をマスターする! …” https://t.co/9weRBJNFGW— Dai Kawai@RubyPython (@never_be_a_pm) February 8, 2018