簡単に出来るサーバレスでのInstagramのスクレイピング

## 最初に
今回はinstagramのユーザ名を指定すると最新のフィードを取得出来るAPIを作っていきます。
私自身、初めて利用するものばかりだったため、誰でもコピペと設定で簡単に出来るよう書いたつもりです。
勉強用サンプルとしてInstagramを使っただけなので、日常的にスクレイピングするのは控えましょう。

## 各種ツールやサービス
・AWS
EC2（Amazon linux）
API Gateway
AWS Lambda

・各種バージョン
python3.6.2
phantomjs-2.1.1

## 環境設定（Python編）
Amazon linux上にPython3の環境を作ります。Vagrantでも問題無いです。
Lambdaのランタイムに合わせて3.6系を選択しています。
この辺りは様々なサイトで解説されているので、わからない場合はそちらを参照してください。

```
$ sudo yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel git
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv

$ pyenv --version
pyenv 1.1.3-33-g48aa0c4

$ pyenv install --list
$ pyenv install 3.6.2
$ pyenv versions
* system (set by /home/ec2-user/.pyenv/version)
  3.6.2

$ pyenv global 3.6.2
$ pyenv rehash
$ python --version
Python 3.6.2

$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
```

## 環境設定（準備編）
Lambdaのローカル開発環境や今回利用するモジュールを準備します。
今回はworkspaceディレクトリで作業を行っています。
Lambadにアップする際にはzipにするため、必ず作業ディレクトリ下で行って下さい。

```
$ mkdir workspace
$ cd workspace/

### Lambdaのローカル開発モジュール
$ pip install python-lambda-local

### zipに含めるためにモジュールをカレントディレクトリにインストール
$ pip install requests -t .
$ pip install html5lib -t .
$ pip install bs4 -t .

### Lambdaのeventに渡す値を設定。
### 今回は日本で一番人気のある方のアカウントを対象にします。
$ vi event.json
{
  "account": "watanabenaomi703"
}


```

## スクレイピング用プログラム
単純にInstagramのプロフィールページで「window._sharedData」変数にセットしているJSONを取得してフィードの部分だけ取得してきています。

``` lambda_function.py
# -*- coding: utf-8 -*-
import json
import requests
import re
from bs4 import BeautifulSoup

def lambda_handler(event, context):
    url = 'https://www.instagram.com/{}/'.format(event['account'])
    res = requests.get(url)
    soup = BeautifulSoup(res.content, 'html5lib')
    js = soup.find("script",text=re.compile("window._sharedData")).text
    data = js[js.find("{"):js.rfind("}")+1];
    return json.loads(data)['entry_data']['ProfilePage'][0]['user']['media']['nodes']
```

## テスト実施
このコマンドでテストを実施します。
実施してJSONデータが返ってきたら成功です。

```
$ python-lambda-local -f lambda_handler lambda_function.py event.json
```

テストが成功したら下記コマンドで圧縮します。
なぜか違うディレクトリで圧縮すると上手く動かなかったため注意して下さい。

```
$ zip -r ../workspace .
```


## Lambda設定
AWS Lambdaの画面にアクセスし、右上の「関数の作成」を行います。

![スクリーンショット 2017-09-15 15.02.45.png](https://qiita-image-store.s3.amazonaws.com/0/28985/3f81e3d1-df98-ba9a-6b31-214fb546cff0.png)

設計図はいろいろありますが、今回は右上の「一から作成」を行います。

![スクリーンショット 2017-09-15 15.02.59.png](https://qiita-image-store.s3.amazonaws.com/0/28985/62ca29fd-73a7-5654-1e8a-7b6a86b8c98c.png)

トリガーは設定せず「次へ」を押します。
![スクリーンショット 2017-09-15 15.03.13.png](https://qiita-image-store.s3.amazonaws.com/0/28985/a7381679-b637-4079-7c4c-ea5fd8c8514e.png)

今回は名前を「instagram-scraping」とし、ランタイムはPython3.6にします。
ハンドラは「ファイル名.関数名」です。
デフォルトは「index.handler」となっていますが、「lambda_function.lambda_handler」に直しましょう。

![スクリーンショット 2017-09-15 15.06.56.png](https://qiita-image-store.s3.amazonaws.com/0/28985/d8a91fe1-58e3-8c59-8434-133156c5efb6.png)

ZIPファイルをアップロードします。
![スクリーンショット 2017-09-15 17.09.41.png](https://qiita-image-store.s3.amazonaws.com/0/28985/df17ed54-8545-314b-66f0-c4f150c8fb8c.png)

タイムアウトを少し長くしましょう。
![スクリーンショット 2017-09-15 15.07.56.png](https://qiita-image-store.s3.amazonaws.com/0/28985/8ba12d14-ce08-61a7-e5b9-b3c67f15fb55.png)

アップロードが終わったらこのような画面になります。
![スクリーンショット 2017-09-15 15.11.59.png](https://qiita-image-store.s3.amazonaws.com/0/28985/44e9a78b-2114-3439-9a7e-29d12ac141ab.png)

テストを実行して成功するとこのように表示されます。
これでLambdaの準備は終わりです。
![スクリーンショット 2017-09-15 17.12.05.png](https://qiita-image-store.s3.amazonaws.com/0/28985/4608e5b1-eb56-1337-c337-de1192bc9db7.png)



## API Gateway設定
API Gatewayの画面に行き、新しいAPIを作成します。
名前は「instagram-scraping」で作成します。
![スクリーンショット 2017-09-15 16.17.06.png](https://qiita-image-store.s3.amazonaws.com/0/28985/25a2b34c-7495-d879-459e-6edfeda969f2.png)

上の「アクション」プルダウンからリソース（APIのpath）を作成してGETで受け付けるようにします。
今回は「/」でも問題ないですが、「instagram-scraping」で作っています。

![スクリーンショット 2017-09-15 16.19.27.png](https://qiita-image-store.s3.amazonaws.com/0/28985/359ac35d-7177-9812-cbba-f14147f119ce.png)

GETパラメータをLambdaに渡す準備をします。
作成したリソースの「統合リクエスト」を選択します。
![スクリーンショット 2017-09-15 17.16.34.png](https://qiita-image-store.s3.amazonaws.com/0/28985/744d7d6e-5572-4eee-dfa7-54d6b27de6e8.png)

一番下の本文マッピングテンプレートを画像のように設定します。

```
{
  "account": "$input.params('account')"
}
```

![スクリーンショット 2017-09-15 16.40.28.png](https://qiita-image-store.s3.amazonaws.com/0/28985/62743173-b386-6c95-d3c2-612231fa1114.png)

API Gatewayはデプロイしないとアクセスできないため、「アクション」プルダウンからデプロイを実施してください。
ステージ名はURLに含まれるため、公開しても問題ないよう「v1」とかでいいと思います。
これで準備完了です。

## APIにアクセス
こんな感じのURLが出来上がるので、ブラウザでもcurlでもいいのでアクセスしてみましょう。
JSONデータが返ってきたら完成です。
https://{固有の値}.execute-api.ap-northeast-1.amazonaws.com/{ステージ名}/instagram-scraping?account=watanabenaomi703

## 最後に
簡単にサーバレスでAPIが作成できましたが、ある程度規模が大きくなるとLambdaのコードの管理やAPIのリソース管理が煩雑になりそうですし、諸々面倒な部分は多いですが、他機能と絡まない独立しているAPIを作る場合は非常に便利なので、もし要件に合う場合は検討してもいいのではないかと感じました。

## 参考URL
http://qiita.com/akichim21/items/2714bcd6a56836cc9134