TypescriptとPythonでWebスクレイピング(ログイン必須)

## TL;DR
ログイン必須なWebサイトのデータ収集をしたく、以下で実装しました。
- スクレイピング: Typescript
- データ整形+アップロード(バックエンド): Python(+flask,googledriveapi)
- 実行: jsDelivr,Chrome開発者ツール

TypescriptはChromeの開発者ツールとブックマークレット機能を使用して実行しています。

## スクレイピング実装
1. Chromeの開発者ツール(console)で実行するため、Typesriptはまず以下の形式で記載します。
```
;(async () => {
    ...
})()
```

2. サイト内でAPIをリクエスト(POST)するとデータを取得できたので、今回はJavascriptのfetchを使用しました。
```
await fetch(baseUrl, {
        method: 'POST',
        headers: {
            'Accept': '*/*',
            'Connection': 'keep-alive',
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF_8',
            'Cookie': document.cookie,
        },
        body: body,
        credentials: 'include',
        referrerPolicy: 'strict-origin-when-cross-origin'
    });
```
### ポイント
- クッキーの取得
    - *document.cookie* でcookieを取得できます。サイトによりますが、ここではcookieをAPIのヘッダに入れることにより、リクエストが通るパターンでした。
- APIの内容について
    - 基本的には開発者ツールのElementとNetworkタブでどのようなリクエストを送っているのかが見えます。実際にボタンを押したりしてAPIリクエスト内容を確認し、それと同じことをコードで行えば基本的にはAPIが通ります。

## データのアップロード
ここはPythonで行います。ローカルサーバを立て、Typescript側から呼び出すことで実現します。
1. APIサーバはFlaskで立てました。Flaskについては他の記事がネット上にうじゃうじゃあるのでここでは記載しません。
2. 今回はgoogledriveへアップロードを行うため、Pythonのpydriveライブラリを使用して認証とアップロードを行います。(実態はgoogledriveapiを使用しているだけです)
- 認証とアップロードAPIの変数化
```
from pydrive2.auth import GoogleAuth
from pydrive2.drive import GoogleDrive
gauth = GoogleAuth() # 認証
gauth.LocalWebserverAuth()
drive = GoogleDrive(gauth) # 変数化
```
- アップロード
```
def fileUpload(data, dirId, filename):
    file1 = drive.CreateFile({'title': filename, 
                            'mimeType': 'text/csv',
                            'parents': [{'kind': 'drive#fileLink', 'id': dirId}]})
    file1.SetContentString(data)
    file1.Upload() # ファイルアップロード
    print('title: %s, id: %s' % (file1['title'], file1['id']))

```

## 実行
1.Javascriptへの変換
chromeのconsoleではTypescriptは実行できず、Javascriptへ変換してからでないと実行できません。
変換に関してはtsconfig.jsonを作成し、gulpを用いて変換しています。

tsconfig例:
```
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Projects */

    /* Language and Environment */
    "target": "es2016",

    /* Modules */
    "module": "umd",
    "rootDir": "./src",

    /* JavaScript Support */

    /* Emit */

    /* Interop Constraints */
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,

    /* Type Checking */
    "strict": true,

    /* Completeness */
    "skipLibCheck": true
  }
}
```

gulp例:
```
var gulp = require('gulp')
var ts = require('gulp-typescript')
var uglify = require('gulp-uglify')
var replace = require('gulp-replace')
const tsProject = ts.createProject(`tsconfig.json`)
const srcDir = `src`
const destDir = `dist`

export default () => {
  return gulp
    .src(`${srcDir}/*.ts`)
    .pipe(tsProject())
    .pipe(
      uglify({
        mangle: true,
        compress: true,
      })
    )
    .pipe(replace(/^(.*)$/, `javascript:$1`))
    .pipe(gulp.dest(destDir))
}
```

2.Githubへアップロード・設定
Githubの公開リポジトリへアップロードします。その後、jsDelivrから読み取るためにタグの設定を行います。

3.jsDelivrでの設定
使い方は [こちら](https://kamoqq.info/post/cdn-jsdelivr/) を参照してください。

4.ブックマークへの追加
jsDelivrに配置したJavascriptファイルを読み取り実行する設定を行います。
下記例の `Path` の箇所には、jsDelivrに配置したJavascriptファイルのPath(URL)を記載します。

```例
javascript:"use strict";var script = document.createElement('script'); script.src = '<Path>';document.body.appendChild(script);
```

5.consoleでの実行
対象webサイトへログイン、Chromeの開発者ツールを開き、登録したブックマークを実行します。
consoleへ実行結果が表示されていきます。

## まとめ
今回は公開のAPIが用意されていないWebサイトだったため、自分でスクリプトを実装し、Webサイト内からのみ叩けるAPIを叩いてデータを取得してみました。
他にも色々と方法はあるかとは思いますが、参考になれば幸いです。