Instagram のあるアカウントの Follower をクロールする

# 概要
Instagram のあるアカウントの全てのフォロワー情報をクロールする.
公式の Instagram API には自分のフォロワーリストを取得する API はあるが, 他人のフォロワーリストを取得する API はない.
ので今回は任意のアカウントのフォロワー情報をクロールするプログラムを作成した.

# 前提条件
このプログラムを使用するにあたっての前提条件
1. Instagram のアカウントをもっている
2. 調べたいアカウントの User ID を知っている
3. 自身の Query ID なるものを知っている

2 と 3 についてはその調べ方を後述する.

# プログラム
```crawl_followers.py

import requests
import json

# Your Account info
def get_user_info():
    return {
            "username": "your_account",
            "password": "your_password"
            }

# HTTP Headers to login
def login_http_headers():
    ua = "".join(["Mozilla/5.0 (Windows NT 6.1; WOW64) ",
                  "AppleWebKit/537.36 (KHTML, like Gecko) ",
                  "Chrome/56.0.2924.87 Safari/537.36"])
    return {
            "user-agent": ua,
            "referer":"https://www.instagram.com/",
            "x-csrftoken":"null",
            "cookie":"sessionid=null; csrftoken=null"
            }

# login session
def logined_session():
    session = requests.Session()
    login_headers = login_http_headers()
    user_info = get_user_info()
    login_url = "https://www.instagram.com/accounts/login/ajax/"
    session.post(login_url, data=user_info, headers=login_headers)
    return session

# a fetch (max 3000 followers)
def fetch_followers(session, user_id, query_id, after=None):
    variables = {
        "id": user_id,
        "first": 3000,
    }
    if after:
        variables["after"] = after

    followers_url = "".join(["https://www.instagram.com/graphql/query/?",
                             "query_id=" + query_id + "&",
                             "variables=" + json.dumps(variables)])
    # HTTP Request
    followers = session.get(followers_url)
    dic = json.loads(followers.text)
    edge_followed_by = dic["data"]["user"]["edge_followed_by"]

    count = edge_followed_by["count"] # number of followers
    after = edge_followed_by["page_info"]["end_cursor"] # next pagination
    has_next = edge_followed_by["page_info"]["has_next_page"]
    return {
            "count": count,
            "after": after,
            "has_next":  has_next,
            "followers": edge_followed_by["edges"]
            }

def fetch_all_followers(session, user_id, query_id):
    after     = None # pagination
    followers = []  

    while(True):
        fetched_followers = fetch_followers(session, user_id, query_id, after)
        followers += fetched_followers["followers"]

        if fetched_followers["has_next"]:
            after = fetched_followers["after"]
        else:
            return {
                    "count": fetched_followers["count"],
                    "followers": followers
                    }

def main(user_id, query_id):
    session = logined_session()
    return fetch_all_followers(session, user_id, query_id)

if __name__ == '__main__':
    user_id  = "3425874156" # user id to search 
    query_id = "" # your query id
    main(user_id, query_id)
```

# 調べたいアカウントの User ID を知る
あるアカウントの user id を知るのは割と簡単. 普通に Instagram の GUI から確認できる.
例: taylorswift の user id を知る
手順 1: chrome を開く
手順 2: developer tool を開く
手順 3: Network の tab に移動
手順 4: https://www.instagram.com/taylorswift/ にアクセス
手順 5: ↓にある /query をクリック<img width="985" alt="スクリーンショット 2017-04-11 20.26.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/169628/0236aa43-d828-d575-bbbf-94b5c2062bb8.png">

手順 6: Response の tab に移動し, "owner" という key を探す
<img width="885" alt="スクリーンショット 2017-04-11 20.28.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/169628/93011e8f-be24-03ae-e902-8b287b9de863.png">

手順 7: "owner" の中の "id" という key の横にある文字列が User ID である

# 自身の query id を知る
手順 1: chrome を開く
手順 2: https://www.instagram.com/ から login
手順 3: https://www.instagram.com/taylorswift/ にアクセス
手順 4: developer tool を開く
手順 5: Network を開く
手順 6: taylor の Followers をクリック
手順 7: ↓の中のリクエストの ?query_id の部分が自身の query id である (隠している部分に実際はある)
<img width="1171" alt="query_id.png" src="https://qiita-image-store.s3.amazonaws.com/0/169628/6709e6fd-6cd8-4016-ccaf-cf86d4bbba2e.png">


## Query ID についてわかったこと
・ ログインし直しても常に同じ
・ アカウントを変えたら違うものになる
・ Instagram の公式 API からはそれらしきものが取得できない
・ 結論, 正体はよくわからないが, アカウントと 1 対 1 の関係にある何かしらの ID

## 以上なり