twitterの全フォローユーザーが入っているリストをPythonで作成する

# なんでやろうと思ったか
twitterのHOMEのタイムラインは下記のような状態になっています。

- プロモーション（広告）が出てくる
- 時系列順とは限らない
- 時間差で数時間前のツイートが一番上に来たりする

２番目と３番目は要は同じことなんですが、なんにせよtwitter側で恣意的に並べられています。
個人的にこれは気持ち悪いですし、プロモーションもよく出てきてノイズが大きいのがストレスでした。

「リストだったらプロモーションも出てこないし時系列順になるよ！」ということを
教えてもらい、全フォローユーザーが入っているリストを作成しようとしたのですが、
twitterには一括でリストにユーザー登録する方法がなかったので、この記事をまとめました。

# 事前準備
twitterのAPIを使って処理するので、Token取得が必要です。
けっこう手間かかるので、この記事では割愛しますが、下記の記事に詳しいです。
<a href="https://www.itti.jp/web-direction/how-to-apply-for-twitter-api/
" target="_blank">https://www.itti.jp/web-direction/how-to-apply-for-twitter-api/
</a>

twitterでアプリ登録して、下記を発行してください。

- CONSUMER_KEY
- CONSUMER_SECRET
- ACCESS_TOKEN
- ACCESS_TOKEN_SECRET

# 処理
ソースコードはこちらに格納してます
<a href="https://github.com/wonohe/twitter_create_follow_list" target="_blank">https://github.com/wonohe/twitter_create_follow_list</a>

## 使い方
### tweepyのインストール
twitterのAPI操作にtweepyを使用しています。
下記にてインストール

```bash:bash
pip install -r requirements.txt
```

### config修正
`config.json.sample`を`config.json`に変更して、各キーを登録してください。

| キー | 説明 |
|:--|:--|
| SCREEN_NAME | 自分のtwitter表示名（例：@Starbucks_J） |
| CONSUMER_KEY | twitterアプリのCONSUMER_KEY |
| CONSUMER_SECRET | twitterアプリのCONSUMER_SECRET |
| ACCESS_TOKEN | twitterアプリのACCESS_TOKEN |
| ACCESS_TOKEN_SECRET | twitterアプリのACCESS_TOKEN_SECRET |

### 処理実行
cloneしたフォルダのルートにて下記実行

```bash:bash
python main.py
```

## 解説
コードは下記のようになっています。

```python:main.py
import datetime
import tweepy
import time
import json

# config
config = json.load(open('config.json', 'r'))

SCREEN_NAME = config['SCREEN_NAME']
CONSUMER_KEY = config['CONSUMER_KEY']
CONSUMER_SECRET = config['CONSUMER_SECRET']
ACCESS_TOKEN = config['ACCESS_TOKEN']
ACCESS_TOKEN_SECRET = config['ACCESS_TOKEN_SECRET']

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
api = tweepy.API(auth, wait_on_rate_limit=True, timeout=180, retry_delay=10, retry_count=5)

#create list
now = datetime.datetime.now()
list = api.create_list(name=f'all-{now.strftime("%Y%m%d%H%M%S")}',mode='public')
print(f'list.id:{list.id}')
print(f'list.name:{list.name}')

#add following users to list
cursor = -1
error_usr = []
while cursor != 0:
    itr = tweepy.Cursor(api.friends_ids, id=SCREEN_NAME, cursor=cursor).pages()
    try:
        for friends_ids in itr.next():
            try:
                user = api.get_user(friends_ids)
                print(user.screen_name)
                # 通信エラーユーザー対応。成功した場合は消す
                error_usr.append(user.screen_name)
                api.add_list_member(screen_name=user.screen_name, list_id=list.id, owner_screen_name=SCREEN_NAME)
                error_usr.pop()
            except tweepy.error.TweepError as e:
                print(e.reason)
    except ConnectionError as e:
        print(e)
    cursor = itr.next_cursor

print('通信エラー分対応')
# エラーがなくなるまで処理
while error_usr:
    for name in error_usr:
        try:
            print(name)
            api.add_list_member(screen_name=name, list_id=list.id, owner_screen_name=SCREEN_NAME)
            error_usr.remove(name)
        except tweepy.error.TweepError as e:
            print(e.reason)
```

基本的にはtweepyAPIを使ってリスト作成→ユーザー登録、をしているだけですが、
いくつか気をつけるポイントがありました。

### tweepyでAPIをコールする際の設定
twitterAPIをコールする際、回数制限があるので、tweepyで`wait_on_rate_limit`はTrueにしておきましょう。デフォルトではFalseになっています。

```
api = tweepy.API(auth, wait_on_rate_limit=True, timeout=180, retry_delay=10, retry_count=5)
```

また、回数制限以外でもエラーになる場合があったので、timeoutとリトライ回数も長めにしてあります。

### 不明な通信エラーに対する対応
私のフォロー人数は144名でそんなに多くはないと思うのですが、処理の中で失敗してしまうユーザーが必ず何名かいました。エラーになるユーザーはバラバラで、144名に対してだいたい３〜５ユーザー程度です。
エラーは下記になるのですが、

```console:error
Failed to send request: HTTPSConnectionPool(host='api.twitter.com', port=443): Max retries exceeded with url: /1.1/users/show.json?id=******* (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x10b1db410>: Failed to establish a new connection: [Errno 60] Operation timed out'))
```

connectionが張れなかった、というだけでよく分かりません。
この失敗した次のユーザーでは成功していたりしたので、回数制限でもなさそうです。
ググっても明確な原因が分からなかったので、エラー分だけリトライするようにしました。
ここでエラー分だけを配列に入れておいて、

```python:main.py
# 通信エラーユーザー対応。成功した場合は消す
error_usr.append(user.screen_name)
api.add_list_member(screen_name=user.screen_name, list_id=list.id, owner_screen_name=SCREEN_NAME)
error_usr.pop()
```

この部分でエラーがなくなるまで登録処理をしています。

```python:main.py
while error_usr:
    for name in error_usr:
        try:
            print(name)
            api.add_list_member(screen_name=name, list_id=list.id, owner_screen_name=SCREEN_NAME)
            error_usr.remove(name)
        except tweepy.error.TweepError as e:
            print(e.reason)
```

# まとめ
処理自体はそんなに複雑でもないのですが、twitterAPIが時々謎の挙動（突然エラーになる）など起きるのが少しややこしかったです。コード変更してないのに急に「その操作は許可してねえよ」って言われたりしたので、皆さんもテストするときは無茶しすぎないようにしましょうネ。

# その他
こちらの記事を参考にさせていただきました。ありがとうございました。
https://note.com/j1ntube/n/n5a417a68178b