Pythonで黒歴史クリーナーを作ってみた。


Twitterやってる人なら一度は耳にした事があるであろう「[黒歴史クリーナー](https://kurorekishi.me/cleaner/getstarted)」
僕も黒歴史ツイートとか消し去りたいので作ってみました。

向き合わなきゃいけないのは分かってても、
消したい過去もある。

#手順

プログラム実行する前にやる事があるので、まずは準備から。
前提として、`Python3`の実行環境が整っているものとします。
それ以外に、やらなきゃいけないのは以下です。

- 各種キー情報の取得([参考記事](https://qiita.com/bakira/items/00743d10ec42993f85eb))
	- `Consumer Key (API Key)`
	- `Consumer Secret (API Secret)`
	- `Access Token`
	- `Access Token Secret`
- データ取得([参考記事](https://www.howtonote.jp/twitter/helpful/index1.html))
	- 過去全てのツイートデータ
- Pythonの外部ライブラリ取得
	- `pip install pandas`
	- `pip install requests`
	- `pip install requests-oauthlib`

#実際のコード

```py3:deleteTweets.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
# 実行コマンド
> python deleteTweets.py {%Y} {%m} {%account_id}

#パラメータ説明
%Y: 処理終了年(未設定の場合は最初から最後までが対象)
%m: 処理終了月(未設定の場合は最初から最後までが対象)
%account_id: 対象アカウントID(特定の相手とのやりとりを消去したい場合に設定)
"""

import sys
import pandas as pd
from requests_oauthlib import OAuth1Session
from time import sleep

# 取得してきた各種キー情報を設定 ※※キー情報は他の人に教えちゃダメだよ！悪用されるよ！※※
twitter = OAuth1Session(
    client_key="XXXXXXXXXX",
    client_secret="XXXXXXXXXX",
    resource_owner_key="XXXXXXXXXX",
    resource_owner_secret="XXXXXXXXXX"
)

# ファイル食って、列['timestamp']をdatetime型にしておく
df = pd.read_csv("twieeted/tweets.csv")
df['timestamp'] = pd.to_datetime(df['timestamp'])

# 削除済みIDを読んで、DataFrameから消しておく
f = open("deleted_ids.txt")
deleted_ids = str(f.read()).split(",")
f.close()
print("started len(deleted_ids): {}".format(len(deleted_ids)))
for deleted_id in deleted_ids:
    if "" != deleted_id:
        df = df[df["tweet_id"] != int(deleted_id)]
deleted_ids = []

# 処理開始年月〜処理終了年月まで処理する
# - 処理終了年月が設定されてない場合は最後まで
start_y = int(min(df['timestamp']).strftime("%Y"))
start_m = int(min(df['timestamp']).strftime("%m"))
max_y   = int(sys.argv[1]) if 2 <= len(sys.argv) else 0
max_m   = int(sys.argv[2]) if 3 <= len(sys.argv) else 0
try:
    while True:
        # 処理対象年月のツイートのみに絞る(元オブジェクトに影響が及ばないように別オブジェクトに作成)
        tweets = df[df["timestamp"].dt.year == start_y]
        tweets = tweets[tweets["timestamp"].dt.month == start_m]
        if len(tweets) <= 0:
            print("bye:)")
            break
        print("processing... {}/{}".format(start_y, start_m))


        """
        XXX ここは毎回変えないといけない。正規表現部分だけ外出しすればいいんだろうけど、面倒臭い。ごめん。
        設定例：
          先頭が"RT"で始まるツイート(=RT):
            "^RT.*"
          先頭が"RT"で始まらないツイート(=RT以外)
            "^(?!RT).*"
          先頭が"@"で始まるツイート(=リプライ)
            "^@.*"
          先頭が"@"で始まらないツイート(=リプライ以外)
            "^(?!@).*"
        """
        # 指定された正規表現に従って、ツイートを絞り込み
        tweets = tweets[tweets["text"].str.contains("^(?!RT @).*$")]  # 中の正規表現を変えて使ってね。ごめんね。
        if 3 <= len(sys.argv):
			tweets = tweets[tweets["text"].str.contains(sys.argv[3])]


        # 1秒ごとにTwitterAPIを呼び出して削除する
        # - 削除に失敗(!=200)の場合は、削除済みリストに入れないよう留意
        # - すぐに削除せず、どういうツイートが取れるのか見たい時は、sleep以降をコメントアウトすると良い
        if 0 < len(tweets):
            for index, row in tweets.iterrows():
                print("{} - {} : {}".format(row["timestamp"], row["tweet_id"], row["text"]))
                sleep(1)
                res = twitter.post("https://api.twitter.com/1.1/statuses/destroy/{}.json".format(int(row["tweet_id"])))
                if res.status_code == 200 or res.status_code == 404:
                    deleted_ids.append(row["tweet_id"])
                if res.status_code != 200:
                    print("       - [{}]: {}".format(res.status_code, res.json()))

        # 処理終了年月になった場合はbye
        if start_m == 12:
            start_y += 1
            start_m = 1
        else:
            start_m += 1
        if start_y == max_y and start_m == max_m:
            print("bye:)")
            break

finally:
    # 削除対象IDをファイルに書いて終了
    # (元CSVをpandas.to_csv()で書き込んだら、データ型が変わってしまって再利用できなかったので苦肉の策。ごめん。)
    print("finished len(deleted_ids): {}".format(len(deleted_ids)))
    if 0 < len(deleted_ids):
        f = open("deleted_ids.txt", "a")
        f.write("{}".format(deleted_ids)
                .replace("[", "")
                .replace("]", "")
                .replace("'", "")
                .replace(" ", ""))
        f.write(",")
        f.close()

```

ソースコードコメント多くてすみません。
あれこれ手探りで、やっつけで作ったので、「何でこうしたの？」って部分が多くなったので、書いといた方が親切かなと思って書いてます。。。

----

というか、過去ツイートの黒歴史がうじゃうじゃ出てきて気持ち悪い...
いや、ツイートしたのは自分なんだけど。