ポンコツエンジニアのごじゃっぺ開発日記。

いろいろポンコツだけど、気にするな。プログラム&ロボット大好きなポンコツが日々の記録を残していきます。 自動で収入を得られるサービスやシステムを作ることが目標!!

GASから指定のURLにリクエストを投げて、スクレイピングしてみよう

この記事は GAS道場 Advent Calendar 2019 の5日目の記事です。 Google Apps Script(GAS)をこれから使おうという方向けのアドベントカレンダーになります。

今回は、個人的にGASを扱っていて面白いところを紹介したいと思います。

スクレイピング

タイトルの通り、指定したURLにリクエストを投げて、そのレスポンスを取得する、というものです。 では、さっそくリクエストを投げてみましょう。

function test() {
  var url = 'https://www.pnkts.net/';
  var data = UrlFetchApp.fetch(url).getContentText();
  Logger.log(data)
}

自分自身のブログに対してリクエストを投げています。 UrlFetchAppについては、以下のドキュメントを参考にしてみてください。

developers.google.com

さて、このリクエストの結果のLogger.logで出力されているのを確認してみましょう。

[19-12-02 08:24:49:264 PST] <!DOCTYPE html>
<html
  lang="ja"

data-admin-domain="//blog.hatena.ne.jp"
data-author="ponkotsu0605"
data-avail-langs="ja en"
data-blog="pnkts.hatenablog.com"
data-blog-host="pnkts.hatenablog.com"
data-blog-is-public="1"
data-blog-name="ポンコツエンジニアのごじゃっぺ開発日記。"
data-blog-owner="ponkotsu0605"
data-blog-uri="https://www.pnkts.net/"
data-blog-uuid="17680117126999583921"
data-blogs-uri-base="https://www.pnkts.net"
data-brand="pro"
data-data-layer="{&quot;hatenablog&quot;:{&quot;blog&quot;:{&quot;enable_ads&quot;:&quot;false&quot;,&quot;name&quot;:&quot;\u30dd\u30f3\u30b3\u30c4\u30a8\u30f3\u30b8\u30cb\u30a2\u306e\u3054\u3058\u3083\u3063\u307a\u958b\u767a\u65e5\u8a18\u3002&quot;,&quot;is_public&quot;:&quot;true&quot;,&quot;entry_show_footer_related_entries&quot;:&quot;true&quot;,&quot;uri&quot;:&quot;https://www.pnkts.net/&quot;,&quot;is_sleeping&quot;:&quot;false&quot;,&quot;enable_keyword_link&quot;:&quot;false&quot;,&quot;lang&quot;:&quot;ja&quot;,&quot;disable_ads&quot;:&quot;pro&quot;,&quot;content_seems_japanese&quot;:&quot;true&quot;,&quot;force_pc_view&quot;:&quot;true&quot;,&quot;owner_name&quot;:&quot;ponkotsu0605&quot;,&quot;is_responsive_view&quot;:&quot;true&quot;},&quot;pro&quot;:&quot;pro&quot;,&quot;admin&quot;:{},&quot;brand_tracking_category&quot;:&quot;pro&quot;,&quot;brand&quot;:&quot;pro&quot;,&quot;router_type&quot;:&quot;blogs&quot;,&quot;analytics&quot;:{&quot;non_sampling_property_id&quot;:&quot;&quot;,&quot;separated_property_id&quot;:&quot;UA-29716941-25&quot;,&quot;property_id&quot;:&quot;UA-117554025-1&quot;,&quot;brand_property_id&quot;:&quot;&quot;},&quot;permalink_entry&quot;:null,&quot;page_id&quot;:&quot;index&quot;}}"
data-device="pc"
data-dont-recommend-pro="false"
data-global-domain="https://hatenablog.com"
data-globalheader-color="b"
data-globalheader-type="pc"
data-has-touch-view="1"
data-hide-header="1"
data-no-suggest-touch-view="1"
data-page="index"
data-parts-domain="https://hatenablog-parts.com"
data-plus-available="1"
data-pro="true"
data-router-type="blogs"
data-sentry-dsn="https://03a33e4781a24cf2885099fed222b56d@sentry.io/1195218"
data-sentry-environment="production"
data-sentry-sample-rate="0.1"
data-static-domain="https://cdn.blog.st-hatena.com"
data-version="83eb03654f2157b2776372b0831f1039"

itemscope
itemtype="http://schema.org/Blog"

  data-initial-state="{}"

  >
  <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
  
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />

長いので途中で切っちゃいましたが、ここからもわかるように、HTMLのソースを取得することができています。すなわち、レスポンスボディを文字列として扱うことができるということです。

スクレイピングでYahoo検索してみよう

ということは、Yahooにクエリを投げたら検索できるってことですね!

function search(word) {
  for (var n = 1; n <= 30; n++) {
    try {
      var searchUrl = "https://search.yahoo.co.jp/search?vs=twitter.com&p=" + encodeURI(word)
      var response = UrlFetchApp.fetch(searchUrl);
      // リンクだけを検索対象とする
      var myRegexp = /<a href=\"([a-zA-Z0-9_\.:\/]*?)\">/g;
      var elems = response.getContentText().match(myRegexp);
      var result = []
      myRegexp = /href=\"(.*?)\"/;
      for(var i in elems) {
        var title = elems[i]
        var url = title.match(myRegexp)[1];
        
        // twitterサイトだけに絞る
        if (!url.match(/https:\/\/twitter.com\/.*/)) continue;
        
        // 以下のURLが含まれたら除外する
        if(url.match(/\/status\//)) continue;
        if(url.match(/\/moments\//)) continue;
        if(url.match(/\/statuses\//)) continue;
        if(url.match(/\/hashtag\//)) continue;
        if(url.match(/\/search\//)) continue;
        
        result.push(url)
      }
      return result;
    } catch(e) {
      Logger.log('NG count: ' + n);
    }
  }
}

ということで、こんなプログラムを書いてみました。

URLの指定

リクエストする先のURLは、https://search.yahoo.co.jp/search?vs=twitter.com&p=" + encodeURI(word) ということで、Yahoo検索にクエリパラメータで検索ワードをつけたものです。そして、 vs=twitter.com とつけることで、Twitterのサイトを対象として検索することができます。 f:id:ponkotsu0605:20191203013636p:plain

検索結果のリンクの取得

レスポンスのボディから、aタグになっているものを取得します。そのaタグに書いてあるリンクを取得という流れになっています。 そうすることで、検索結果のリンクを取得できます。

Twitterのアカウントを取得

そして、statusやmomentsなど、ツイートやハッシュタグとかTwitterの検索結果などを取り除くことで、Twitterのアカウントだけを取得することができます。

実際に動かしてみる

では、わかりやすくポケモンで検索してみましょう。

function main() {
  Logger.log(search('ポケモン'))
}

上で実装したメソッドを呼んで、その戻り値をログに出力しているだけです。 これの結果は以下のように出力されました。

[19-12-03 01:26:44:534 JST] [https://twitter.com/pokemon_cojp, https://twitter.com/poke_times, https://twitter.com/anipoke_pr]

このように、3つのTwiitterアカウントを見つけることができました。 このようにすることで、例えばスプレッドシートに検索したい文字列を並べて、GASで各ワードを検索しつつ、スプレッドシートに記録する、なんてことも簡単に作れてしまいます。

さいごに

このように、GASとスプレッドシートとスクレイピングを組み合わせることで、もしかしたら人による手動の検索を代わりにGASのボットが調査してくれるようになり、仕事が何倍も捗り、速く終わらせることができるんじゃないかなと思います。 自分も、だいぶ仕事を楽にしたりしていますので。もちろん、外部のAPIを叩くのもこのようにすることでできますね。