1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Google Apps Scriptで動的サイトのHTMLを取得する方法

Posted at

はじめに

Google Apps Script(GAS)は、スプレッドシートと連携した自動化処理に非常に便利です。中でも「Webページの情報を自動で取得し、スプレッドシートに転記したい」というニーズは非常に多いです。

一方でGAS単体ではクライアント側でレンダリングされる動的なサイトのHTML要素を取得することができません。
そこでこの記事では、クライアント側でレンダリングされる動的なWebサイトのスクレイピング方法を解説します。

この記事で触れないこと

  • GASの操作
  • Cheerioの使い方
  • スクレイピングの実装方法

GASでのスクレイピングにCheerioを使う

GAS単体ではDOMの解析ができませんが、Cheerioというライブラリを使うことで、Node.js風にjQueryライクなDOM操作が可能になります。
GASとCheerioを組み合わせると、取得したデータをスプレッドシートに転記する処理も簡単に書けます。業務の自動化や定期的なデータ収集に最適です。

しかし、Cheerioには限界がある

便利なCheerioですが、 JavaScriptで描画されるコンテンツ(いわゆる動的サイト) には対応できません。これは次のような理由によるものです:

  • UrlFetchAppで取得できるHTMLは、 JavaScriptの実行前の状態
  • つまり、 画面上に表示されている内容と取得できるHTMLが異なる
  • Cheerioはあくまで「静的なHTML」に対する処理を行うライブラリ

具体的な例

たとえば、SPA(Single Page Application)で作られたECサイトの商品リストや、チャートライブラリで動的に描画されるグラフ情報などは、Cheerioでは取得できません。

解決方法

このような「JavaScriptで描画されたあとのHTML」を取得するには、ヘッドレスブラウザを使った描画処理が必要になります。そこで登場するのがPhantomJSCloudです。

PhantomJSCloudとは?

PhantomJSCloudは、クラウド上でヘッドレスブラウザを実行し、描画済みのHTMLを取得できるAPIサービスです。
PhantomJSCloudを使い以下の流れでスクレイピングが実行可能になります。

  1. PhantomJSを呼び出しHTMLを描画
  2. 描画されたHTMLをGASに返す
  3. Cheerioでスクレイピング実行

実装編

PhantomJSCloudのAPIキーを取得する

PhantomJSCloudのページにアクセスし、APIキーを取得します。
Sign up nowのボタンから登録します。
Screenshot 2025-04-26 at 11.47.04.png

登録が完了するとAPIキーが表示され、以降PhantomJSCloudのエンドポイントへリクエストを送るために必要なためコピーして控えておきます。

Screenshot 2025-04-26 at 13.25.19.png

エンドポイントへのリクエストの書き方

GET

http(s)://PhantomJsCloud.com/api/browser/v2/[先ほど発行したAPIキー]/?request=リクエストのJSONオブジェクト

POST

http(s)://PhantomJsCloud.com/api/browser/v2/[先ほど発行したAPIキー]/

リクエストのJSONオブジェクトの書き方

プロパティはたくさんありますが、とりあえず最低限必要なもののみ紹介します。

  • url(必須)
    • 操作したい対象のページURL
  • render
    • レスポンスのレンダリングタイプ。HTMLとすればHTMLが戻される
  • outPutAsJson
    *
  • overseerScript
    • 対象のページでクリックやフォーム入力などブラウザ操作するスクリプトを渡す
    • 詳細はこちらを参考
    • Puppeteerのスクリプトの80%をサポートしている

参考

GETリクエストを送ってページを取得してみる

サクッとQiitaのトップページのHTMLを取得してみます。
HTMLが返されるので結果をCheerioに渡してあげればスクレイピングが可能です。

function myFunction() {
  const apikey = "apiキー";
  const options = {
    url: "https://qiita.com/",
    renderType: "HTML",
    outputAsJson: true,
  }
  // encodeURIComponentでエンコードする必要があるためエンコード
  const requestJson = encodeURIComponent(JSON.stringify(options));
  const endpoint = `https://phantomJsCloud.com/api/browser/v2/${apikey}/?request=${requestJson}`
  const response = UrlFetchApp.fetch(endpoint).getContentText();
  const html = JSON.parse(response)['content']['data'];

  console.log(html);
}

ログイン処理が必要な場合

ログインが必要な場合にはPOSTリクエストを送り、overseeerScriptにログインフォームを操作するスクリプトを渡すことで実装が可能になります。

今回はPhantomJSCloudにあるサンプルのログインページにユーザー名・パスワードを入力してログインしてみます。

function myFunction() {
  const apikey = "apiキー";
  const endpoint = `https://phantomJsCloud.com/api/browser/v2/${apikey}/`
  const payload = {
    url: "https://phantomjscloud.com/examples/corpus/automation.html",
    renderType: "HTML",
    outputAsJson: true,
    overseerScript: 'await page.waitForSelector("input[name=id]"); \
      await page.type("input[name=id]", user, {delay:100}); \
      await page.type("input[name=pass]", pass, {delay:100}); \
      page.click("input[type=button][value=Login]"); \
      await waitForNavigation();',
  }

  const options = {
    method: "POST",
    payload: JSON.stringify(payload)
  }
  
  const response = UrlFetchApp.fetch(endpoint, options);
  const html = JSON.parse(response)["content"]["data"];

  console.log(html)
}

overseerScript内で使っているメソッドのドキュメントはこちらになります。
このスクリプトを実行するとログイン後の画面のHTMLが返されるのでスクレイピングが可能になります。
またこれ以外にログイン後の画面でボタン操作がしたいなどあればoverseerScript内に追加のコードを書くことで実現可能です。

最後に

GASでのスクレイピング方法の情報は多くありますが、動的サイトにログイン処理をしてスクレイピングする情報が少なかったので苦労しました。
この情報がどなたかの参考になれば幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?