Initial Site

Initial Site

Initial Site

【難しくない】GASでwebスクレイピングして正規表現でデータを集める

  • ヨシモト  2018/11/02 17:18
dance_shoot_dance

営業部 ヨシモトです。
 
以前に、スプレッドシートを使って簡単なスクレイピングをしてみようという記事を書きました。
この記事ではスプレッドシートの関数(主にIMPORTXML関数)だけを使って簡単なスクレイピングをしてみよう、という内容でした。
 
今回はこれに近いことをGoogle Apps Script(以下、GAS)でやってみようという内容です。
 
スプレッドシート関数だけの場合はできることもある程度限定されますが、今回の内容は仕組みがわかれば広く応用が利きますのでぜひ覚えてみましょう。
 

 

今回おこなうこと

今回は、いまご覧いただいてる弊社のトップページに掲載されている記事の「タイトル」と「記事のURL」を取得してスプレッドシートに書き込みます。
 
 

どうやってやるか

topSS
 
このページに掲載されている記事のタイトルと、そのURLを取得します。
ちょっと厄介なのが、このページでは記事のタイトルの文字数が一定以上あると全て表示されていないことです。
とすれば、取得した記事URLのページから記事タイトルを取得することになります。
 
大まかな手順は、下記の通りです。
 


トップページの情報を全て取得する

その中から、記事のURLを正規表現を使って取得する

取得した記事URLにアクセスし、正規表現を使って記事のタイトルを取得する

これをトップぺージに掲載されている記事数分繰り返す

取得したURLとタイトルをスプレッドシートに書き込み


 
さらに嚙み砕くと基本的には、
 
正規表現でマッチさせたものを配列に格納する←これを繰り返す
 
となります。

 

以下にコードを記載します。

 

コード

function scrapSample() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1');
  var getUrl = 'http://www.initialsite.com/page/1';
  var content = UrlFetchApp.fetch(getUrl).getContentText('UTF-8');
  
  var itemRegexp = new RegExp(/<a href=.*?" class="c\d{1,2}">/g);
  var item = content.match(itemRegexp);
  var items = [];
  
  for (var i = 0; i < item.length; i++) {
    var itemURL = item[i]
      .replace('<a href=','')
      .replace(/"/g,'')
      .replace(/class=c\d{1,2}>/,'');
    items.push([itemURL]);
       }
  
  var titleRegexp = new RegExp(/<title>.*?<\/title>/);
  var titles = [];
  
  for (var j = 0; j < items.length; j++) {
    var itemCont = UrlFetchApp.fetch(items[j]).getContentText('UTF-8');
    var title = itemCont.match(titleRegexp)[0]
      .replace(/<\/*title>/g,'')
      .replace(' | 株式会社InitialSite','');
    titles.push([title]);
       }
  sheet.getRange(2, 1, titles.length, 1).setValues(titles);
  sheet.getRange(2, 2, titles.length, 1).setValues(items);
}

 

解説

コンテンツの取得

  var getUrl = 'http://www.initialsite.com/page/1';
  var content = UrlFetchApp.fetch(getUrl).getContentText('UTF-8');

 
UrlFetchApp.fetch() でトップページのURLへアクセスします。
 
これに対してgetContentText() することでトップページのテキストデータが取得できます。

参考

Class UrlFetchApp | Apps Script | Google Developers

 
取得したものをLogger.log(content ) で見てみるとこんな感じになってます。
 
contentSS
 
次はここで取得したcontent から正規表現を使って記事URLを抜き出していきます。
 

正規表現で記事URLを取得

  var itemRegexp = new RegExp(/<a href=.*?" class="n\d{1,2}">/g);
  var item = content.match(itemRegexp);
  var items = [];
  
  for (var i = 0; i < item.length; i++) {
    var itemURL = item[i]
      .replace('<a href=','')
      .replace(/"/g,'')
      .replace(/class=n\d{1,2}>/,'');
    items.push([itemURL]);
       }

new RegExp で記事URLの正規表現をitemRegexp として作成します。
トップページのソースを見てみると(Chromeブラウザなら「Ctrl+U」で見られます)。
各記事のURLはclassがn02,n03などのaタグなのがわかります。
 
これの正規表現を/<a href=.*?” class=”n\d{1,2}”>/として作成します。
n\d{1,2}は「nと1回以上、2回以下の数字」となり、
/正規表現/g のようにオプションにgを選択して、一致するものを全てをマッチさせるようにしています。
(n\d{2}でも大丈夫です)

 

※ 2020/03/30 追記 クラス名が変更になっていたので修正いたしました。

new RegExp で記事URLの正規表現をitemRegexp として作成します。
トップページのソースを見てみると(Chromeブラウザなら「Ctrl+U」で見られます)。
各記事のURLはclassがc02,c03などのaタグなのがわかります。
 
これの正規表現を/<a href=.*?” class=”c\d{1,2}”>/として作成します。
c\d{1,2}は「cと1回以上、2回以下の数字」となり、
/正規表現/g のようにオプションにgを選択して、一致するものを全てをマッチさせるようにしています。
 
(c\d{2}でも大丈夫です)

参考

RegExp – JavaScript | MDN

正規表現の基本 – Qiita

 
var item = content.match(itemRegexp)で、
content のうち、この条件にマッチした部分をitem に配列として格納します。
item に格納したURLはaタグやclass名がついたままなのでreplace() でURLの文字列だけにしたものをitemURL へ代入します。
replace() にも正規表現が使えますのでこれを利用してタグ部分を空にします。
 

参考

String.prototype.replace() – JavaScript | MDN

 
items へpush() して二次元配列として格納します。
二次元配列に格納するのは、最後にスプレッドシートへsetValues() で書き込むためです。
(setValues は二次元配列でないと書き込めません)
これをfor文でitem の数だけ繰り返します。
 

この場合の二次元配列について

var items = []でitems という空の配列を用意しておきます。
items.push(itemURL) とすると一次元配列として格納されますが、
items.push([itemURL]) とすれば二次元配列として格納できます。

 

各記事URLへアクセスしてタイトルを取得する

  var titleRegexp = new RegExp(/<title>.*?<\/title>/);
  var titles = [];
  
  for (var j = 0; j < items.length; j++) {
    var itemCont = UrlFetchApp.fetch(items[j]).getContentText('UTF-8');
    var title = itemCont.match(titleRegexp)[0]
      .replace(/<\/*title>/g,'')
      .replace(' | 株式会社InitialSite','');
    titles.push([title]);
       }

 
記事ページではtitleタグを取得すれば記事タイトルが得られますので、titleタグの正規表現をtitleRegexp として作成しておきます。
UrlFetchApp.fetch() で先ほどitems に格納した記事URLへアクセスし、取得したテキストデータをitemCont へ代入します。
 
itemCont からtitleRegexp にマッチしたタイトルからタグをreplace()で取り除きtitleへ格納します。記事タイトルの後ろにサイト名(会社名)も併記されますので同じく取り除きます。
そして、こちらも記事URLと同じようにtitles へpush()して二次元配列として格納し、これをfor文でitemsの数だけ繰り返します。
 
ちなみにLogger.log(titles)  で見てみるとこんな感じです。
 
titlesSS

スプレッドシートへ書き込む

  sheet.getRange(2, 1, titles.length, 1).setValues(titles);
  sheet.getRange(2, 2, titles.length, 1).setValues(items);

 
最後にsheet.getRange() で範囲を指定し、setValues() 二次元配列に格納してある記事タイトルと記事URLを書き込みます。
 
これで完成です!
 
titleAndUrlSS
 
ちなみにトップページのURLは http://www.initialsite.com ですが、
 http://www.initialsite.com/page/1 としてあるのは、
最後のページ数を取得しておけば、/page/1 の1へ変数を代入し、for文で全ページ分取得できるようにするためです。
(今回はしてません。)
 
今回ご紹介した方法を応用して他のサイトでもスクレイピングできますし、
UrlFetchApp.fetch()へパラメータを渡せばログインが必要なサイトでも応用できるようになります。
ぜひいろいろ応用してみましょう。

 

TwitterWikipediaなど、スクレイピングが禁止されているサイトもありますので、他人・他社が運営するサイトの場合は必ず利用規約を読んでから行ってください。

また、スクレイピングの禁止が明示されていないサイトであっても、短時間で大量のリクエストを送ること等は管理者や利用者に迷惑がかかる場合もありますのでご注意ください。

 

今回参考にさせていただいたサイトはこちらです。
ありがとうございました。

Class UrlFetchApp | Apps Script | Google Developers

RegExp – JavaScript | MDN

String.prototype.replace() – JavaScript | MDN

正規表現の基本 – Qiita

 

その他、スプレッドシートやGASに関する記事はこちらからどうぞ。

Google Apps Script はじめました

GASを使ってGmailから本文の一部を抜き出す

スプレッドシート独自の便利な関数

QUERY関数の便利な使い方

スプレッドシートを使って簡単なスクレイピングをしてみよう

【GAS】正規表現を使ってGmailの本文から文章を抜き出す

【簡単】GASでスプレッドシートの送信リストからメールを送る

【GAS】正規表現を使って複数行の文章をGmailから抜き出す

【GAS】Gmailの受信トレイにあるメールの添付ファイルを自動でGoogleドライブへ保存する

【難しくない】GASでwebスクレイピングして正規表現でデータを集める

【GAS】OCRを使った画像の文字取得を自動化する

【GAS】お手軽なOCRの自動化をスプレッドシートで扱いやすくする

ReactとFirebase(Cloud Firestore)を使って独り言WEBアプリをつくる


この記事の作者

アバター
ヨシモト


総記事本数:17

この記事へのコメント

    • 通りすがりの さん
      2018年11月29日 12:07 PM
    • 返信

    わかりやすい解説ありがとうございました。

    • 林 伸夫 さん
      2020年3月29日 12:25 PM
    • 返信

    頼りになる記事をありがとうございます。
    早速やってみました。コードはコピペです。10行目の
    for (var i = 0; i < item.length; i++)
    の部分で次のようなエラーが出て、先に進めません。なにか、サイトの変更などがあったのでしょうか?
    [20-03-28 20:17:22:025 PDT] TypeError: Cannot read property 'length' of null
    at scrapSample(コード:10:28)

      • ヨシモト さん
        2020年3月30日 5:30 PM

      林 伸夫さん
      コメントありがとうございます。
       
      > 早速やってみました。コードはコピペです。10行目の
      > for (var i = 0; i の部分で次のようなエラーが出て、先に進めません。なにか、サイトの変更などがあったのでしょうか?
       
      確認してみたところ、記事へのリンクタグのクラス名が変更になっておりましたので内容を修正いたしました。
      下記をご確認ください。
      http://www.initialsite.com/w01/14311#subTitle2

コメントをどうぞ

知識の記事

  1. gahag-0075639565
    サブスクリプションの整理をしよう
  2. complete
    ReactとFirebase(Cloud Firestore)を使って独り言WEBアプリをつくる
  3. E5DxwzVgVHYHr411581668296_1581668370
    わたしは公家ではありませんでした(多分)
  4. PAK86_smonitatocode20140517_TP_V
    All in One SEO Packの必要性

おすすめ記事

  1. ウィルス画像
    横浜のブラック企業 Initial Site(イニシャルサイト)から『コロナハラスメント』の内部告…
  2. E5DxwzVgVHYHr411581668296_1581668370
    わたしは公家ではありませんでした(多分)
  3. 1b8493ba94ff68762824c3c7274b3128-e1499313795652
    イオンの保存容器はジップロック超えた!?※主観的視点