RubyによるTwitter全ツイート削除

はじめに

過去のツイートを削除することは抵抗はないのですが,前の職場に居た頃(2009年)から約8年間,細々と他愛のないツイートをしてきたこのTwitterのアカウントには愛着があります.

特に何か理由があるわけではありませんでしたが,過去のツイートを全部削除して,まっさらな気持ちでTwitterを利用したくなりました.

削除前は,全ツイート数は7,370ありました.
以前も3,200件ほど削除しているので,実際は1万ツイートを超えていました.
以前,なぜ3,200件しか削除できなかったのかは,後に記載しています

過去のツイートをまとめて削除する方法として,黒歴史ナントカとかありますが,自分以外の誰かに自分の過去の情報にアクセスされたり,変更を加えられたりするのはどうも嫌なのです.やたらめったら認証アプリ連携登録などしたくないですから...

となれば,自分でAPIを叩けば良いやということで,今回Rubyでスクリプトを書いてやってみたいと思います.

RubyによるTwitter API利用の準備

RubyでのTwitter APIとのインターフェースとして「twitter」Gemがありましたので,これを利用します.

https://github.com/sferik/twitter

例のごとく,本Gemは今回のアプリでのみ使用の予定ですので,Bundlerで管理.

$ mkdir twierase
$ cd twierase
$ bundle init
$ vi Gemfile
source "https://rubygems.org"

gem "twitter"
$ bundle install --path vendor/bundle

$ bundle exec irb
 irb(main):001:0> require "twitter"
 => true

これでTwitter Gemが利用できる状態になりました.

Twitterのアプリ登録

さて,Twitter APIはその利用にOAuth認証が必要なので,Twitterのアプリ登録が別途必要です.どのアプリと登録って,それはこれから自分で作るアプリのことです.

OAuth認証については,色々とググっていただければ詳しく説明してあるサイトがありますので,ここでは説明しませんが,これから作るアプリ(といっても大したものではありませんが...)とTwitter同士が色々とお話し合いをするために必要な機構のことです.

Twitterアプリ登録はここから行います.もちろんTwitterのアカウントが必要です.

Twitter Application Management

とにかく,アプリを動かす(Twitter APIを利用する)ためには,APIキーとアクセストークンの情報が必要なので,色々と登録設定作業をします.やり方については,ここのサイトが詳しく説明されていますので,こちらの手順で進められたら良いと思います.

Twitter REST APIの使い方 | Syncer

アプリ登録完了すれば,以下の情報が手に入ります.これがTwitter APIを利用する上で必要なハッシュコードです.

  • Consumer Key (API key)
  • Consumer Secret (API Secret)
  • Access Token
  • Access Token Secret

これらは絶対人に漏らさないようにしましょう.誰かに漏らせば,その人があなたの情報を取得できますし,あなたに成り替わり勝手にツイートしたりできてしまうからです.認証アプリ連携は,個人情報をそのアプリに登録することなく,TwitterやFacebookなどのサービスプロバイダ経由で認証できるという便利さがありますが,自分に成り代わって操作できるアクセストークンも渡してしまうという怖さもあります.

全ツイート削除スクリプト(失敗の巻)

では,全ツイート削除スクリプトをRubyで作っていきます.Twitter GemのGitHubのサイトに全ツイートを参照するexampleがありましたので,それを利用してみます.

https://github.com/sferik/twitter/blob/master/examples/AllTweets.md

これを利用して作成したスクリプトがこれです.

require "twitter";

# アプリ登録で取得した各ハッシュコードにて設定
client = Twitter::REST::Client.new do |config|
  config.consumer_key = "CONSUMER_KEY"
  config.consumer_secret = "CONSUMER_SECRET"
  config.access_token = "ACCESS_TOMEN"
  config.access_token_secret = "ACCESS_TOKEN_SECRET"
end

def collect_with_max_id(collection=[], max_id=nil, &block)
  response = yield(max_id)
  collection += response
  response.empty? ? collection.flatten : collect_with_max_id(collection, response.last.id - 1, &block)
end

def client.delete_all_tweets(user)
  collect_with_max_id do |max_id|
    options = {count: 200, include_rts: true}
    options[:max_id] = max_id unless max_id.nil?
    user_timeline(user, options).each do |stat|
      p stat
      destroy_status(stat.id)
    end
  end
end

client.delete_all_tweets(twitterID) # TwitterID = 自分のID

実行してみます.

$ bundle exec ruby twierase1.rb

タイムラインからID(Tweet ID)を取得して,それをdestroy_statusの引数に指定するだけです.

最新のツイートからどんどん削除されていきましたが,このスクリプトは3,200件までしか削除してくれません

メソッドuser_timelineは,APIのuser_timelineを呼び出していますが,このメソッドは最新の3,200件のツイートしか返してくれないようです.それ以前(過去)のツイートは返してくれません.

https://dev.twitter.com/rest/reference/get/statuses/user_timeline

そんなに簡単には上手くいきません...

とにかく,過去のツイートIDさえ取得できれば,

client.destroy_status(過去のツイートID)

で,削除は可能なので,後は過去の全てのツイートIDのリストさえ手に入れられればOKです.
そこで,Twitterの履歴データの取得です.

Twitterの全履歴データの取得

PCのWebブラウザにてTwitterにアクセスし,Twitterの設定画面から「全ツイート履歴をリクエストする」をクリックします.

そうすると登録されているメールアドレス宛に,履歴データのダウンロードURLが送られてきます.(普通ならね)

ツイートの量によってはメールが返ってくるのに時間がかかるらしく,しばらく待っていたのですが,待てど暮らせど一向にメールは返ってきませんでした.Twitter APIで変な事(3,200件一挙に削除)したので,Twitter様がお怒りになられて私には履歴データは渡せないということなのかと心配になりましたが,登録メールアドレスを別のものに変更したら,あっさりと,今度はすぐにメールが返ってきました

履歴データをダウンロードしますと,その中に「tweets.csv」ファイルがあります.これに過去全てのデータが詰まっています.

"tweet_id","in_reply_to_status_id","in_reply_to_user_id","timestamp","source","text","retweeted_status_id","retweeted_status_user_id","retweeted_status_timestamp",”expanded_urls"
"831509734859476992","","","2016-01-31 14:25:44 +0000","<a rel="">Twitter for iPhone</a>","松任谷由実さんは、「VOYAGER」〜「Delight Slight Light KISS」までをよく聴いていた世代です。","","","","" "831506150231199745","","","2016-01-30 14:11:29 +0000","<a rel="">Twitter for iPhone</a>","CASIOPEAは、「Jive Jive」〜「SUN SUN」、THE SQUAREは、「ADVENTURES」〜「S・P・O・R・T・S 」、それぞれをよく聴いていた世代です。","","","",”"
"831500067508924418","826930841490067456","107390505","2017-02-14 13:47:19 +0000","<a rel="">Twitter for iPhone</a>","@tanimurayumi うちの奥さんは電気関係に疎く、電球取り替えるのも独りでできないから、私が先立ってもこういうのがあれば安心です(^-^)","","","",”"
"831498158119153664","","","2016-01-29 13:39:44 +0000","<a rel="">Twitter for iPhone</a>","飲んで帰った後、家でもシメで飲むから二日酔い になるんだな(-_-;)","","","",”"
:

この行頭の数字の羅列がツイートIDです.

https://twitter.com/ユーザID/status/ツイートID

を指定する事で,その特定のツイートを確認することができます.

このCSVファイルは,タイムスタンプや実際のツイートの内容,後変なところで改行されていたりするので,sedコマンドで整形します.

$ sed 's/^"\([0-9]*\)",.*$/\1/g' tweets.csv | sed -e 1d | sed -e '/[^0-9]/d' | sed -e '/^$/d' > tweets_id.dat

もっと正規表現をスマートに記述できれば良いのですが,やっつけでパイプを使いまくっています.

sedコマンドがない場合は,ExcelでCSVファイルを開いて適当に加工します.

その結果こんな感じでツイートIDだけのデータファイルができあがります.

831708491228143616
831637953642237953
831509734859476992
831506150231199745
831502090237202432
831500067508924418
:

後は,このファイルから1行1行ツイートIDを読み込んで,destroy_statusを実行すればOKです.

APIの制限

しかし,一抹の不安に駆られます...

Twitter様に,一気に7,000件以上も連続してAPIを送りつけても怒られないだろうか....

当然,サーバに負担がかかるから制限はあります.

API Rate Limits — Twitter Developers

これを読むと,GETリクエストについては,15分間ごとに15回コールまでと,15分間ごとに180回コールまで,とあります.

果たして,destroy_statusメソッドが,どちらかの制限に該当するかということですが,Gemのドキュメントを見てみると,「Rate Limited? No」と書いてあります.

Method: Twitter::REST::Tweets#destroy_status — Documentation for sferik/twitter (master)

本当かなと思って,呼び出している元のAPIのリファレンスを見てみると,「Rate limited? Yes」となっています...

POST statuses/destroy/:id — Twitter Developers

POSTについての制限は色々調べても良くわかりませんが,ブラックリストに載るのは嫌なので,ここは15分に160回くらいのリクエストで行ってみます.

改めて,全ツイート削除スクリプト(成功編)

require "twitter"

client = Twitter::REST::Client.new do |config|
  config.consumer_key = "CONSUMER_KEY"
  config.consumer_secret = "CONSUMER_SECRET"
  config.access_token = "ACCESS_TOMEN"
  config.access_token_secret = "ACCESS_TOKEN_SECRET"
end

max = File.read('tweets_id.dat').count("\n") # 総行数(総ツイート数)を取得

File.open('tweets_id.dat', 'r') do |f|
  ct = 0
  f.each_line do |line|
    twid = line.chomp
    printf("[%04d]%s ", max - f.lineno, twid)
    begin
      printf("%s", client.destroy_status(twid))
    rescue => e
      printf("FAIL: %s", e.message)
    end
    printf("\n")
    ct += 1
    sleep(930) if ct % 160 == 0 # 160回コマンドを発行ごとに930秒間休止
  end
end

15分間=900秒ですが,30秒ほど余裕をみています.関係ないかもしれないけど...

それでは意を決して実行.

$ bundle exec ruby twierase2.rb
[7150]831708491228143616 FAIL: No status found with that ID.
[7149]831637953642237953 FAIL: No status found with that ID.
[7148]831509734859476992 FAIL: No status found with that ID.
[7147]831506150231199745 FAIL: No status found with that ID.
[7146]831502090237202432 FAIL: No status found with that ID.
[7145]831500067508924418 [#<Twitter::Tweet id=831500067508924418>]
[7144]831498158119153664 [#<Twitter::Tweet id=831498158119153664>]
[7143]831481114875174913 [#<Twitter::Tweet id=831481114875174913>]
:

削除していて既に存在しないツイートについては,FAILを表示させています.

7,150件を,15分30秒間ごとに160件ずつ削除するということは,全て削除するには約11時間半もかかることになります.

間欠実行は,プロセス資源上,本当はcronか何かで制御したほうがいいと思いますが,とりあえずやっつけでやってしまいました.

結局,夜実行させて,翌朝には履歴を取った以降の分(200件程度)のツイートだけになっていました.

これにてコンプリート!

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中