Google Apps Script でスクレイピングがうまく出来ない場合にチェックするポイント

必要に駆られて行った調査をまとめました。
間違いや良い代替策等ありましたらコメント下さい。




## 要約

- 世のウェブサイトは Valid でないことが多いので `IMPORTXML()` では値が取れないことが多い。
- 同じく `XmlService.parse()` も Valid であることが前提なのでパースエラーが起こりやすい。
  - 正規表現で値を取得する。
- JavaScript で動的にコンテンツを生成しているサイトでは `UrlFetchApp` でコンテンツを取得できない。
  - PhantomJS Cloud のようなサービスと組み合わせる必要がある。




## 事例

### `IMPORTXML()` で値が取れない。`#N/A` と表示される。

`IMPORTXML()` はスクレイピング対象が Valid でありことが求められる。
HTML エラーが存在するサイトでは機能しない。

> * [IMPORTXML - ドキュメント エディタ ヘルプ](https://support.google.com/docs/answer/3093342?hl=ja&ref_topic=9199554)


### `IMPORTXML()` で取得した値が文字化けしている。

スクレイピングの対象が UTF-8 でないサイトである。
`IMPORTXML()` は諦めてオリジナルの関数を作成し、`getContentText()` で `charset` を指定する。

> * [Class HTTPResponse  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/reference/url-fetch/http-response#getContentText(String))

```javascript
// The code below logs the HTML code of the Google home page with the UTF-8 charset.
var response = UrlFetchApp.fetch("http://www.google.com/");
Logger.log(response.getContentText("UTF-8"));
```


### `XmlService.parse()` で値が取れない。Parse Error が発生する。

`XmlService.parse()` も Valid であることが前提なのでパースエラーが起こりやすい。
`XmlService.parse()` を利用せずに正規表現で値を取得する。

> * [Class XmlService  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/reference/xml-service/xml-service#parse(String))
> * [Google Apps Scriptでスクレイピングする方法 - Qiita](https://qiita.com/inaling/items/23248b7290d8140f53fc)

[タイトルを取得する例：](https://www.d-wood.com/blog/2019/07/26_11365.html)

```JavaScript:Code.js
function getTitle(url) {
  var _options = {
    method: "GET"
  };

  try {
    var _response = UrlFetchApp.fetch(url, _options);
    var _regexp = /<title>(.*?)<\/title>/;
    var _title = _response.getContentText().match(_regexp);

    return _title[1];
  } catch (e) {
    return e;
  }
}
```


### `UrlFetchApp` で取得した値に目的のコンテンツが含まれていない。

`UrlFetchApp` は JavaScript 実行前の HTML が返される。

JavaScript で動的にコンテンツを生成している可能性がある。
`curl` 等で取得できるソースを確認する。

PhantomJS Cloud のようなサービスと組み合わせる必要がある
（著者は利用したことがありません）
。

> * [Class UrlFetchApp  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app)
> * [JavaScript実行後のHTMLをGASで取得する - Qiita](https://qiita.com/icchi_h/items/4d560d9b451fc483a25e)
> * [PhantomJS Cloud API Service - It just works!](https://phantomjscloud.com/)

[PhantomJsCloud を利用してスクレイピングする例：](https://www.d-wood.com/blog/2019/08/02_11423.html)

```JavaScript:Code.js
function getResource(target_url) {
  var _options = {
    url: target_url,
    renderType: "html",
    outputAsJson: true
  };
  var _payload = encodeURIComponent(JSON.stringify(_options));
  var _request =
    "https://phantomjscloud.com/api/browser/v2/" +
    APIKEY +
    "/?request=" +
    _payload;
  var _response = UrlFetchApp.fetch(_request).getContentText();

  return _response;
}
```




## 補遺

- スクリプトに実行権限を許可するダイアログでは「詳細」をクリックする。
- Web エディタ上では JavaScript ES5 を利用する。
  - しっかり書く場合は [google/clasp](https://github.com/google/clasp) を利用すると良さそう。

> - [clasp 入門 | deadwood](https://www.d-wood.com/blog/2019/07/30_11378.html)
> - [Using TypeScript in a Clasp Project | deadwood](https://www.d-wood.com/blog/2019/07/31_11417.html)