機械学習用の Twitter データセットを作る
機械学習用の Twitter データセットとは、テキスト、日時、投稿者を含むツイートの集合です。Sentiment140(2009年の160万ツイート)のような公開アーカイブからダウンロードするか、API で新規に収集して用意します。
結論: 公開データセットは無料ですが数年前のもので、多くはツイート ID だけの配布なので、テキストを得るには再取得が必要です。新規データセットは、公式 X API だと1,000投稿あたり$5.00かかります。作るなら Sorsa API が最適です。POST /search-tweets でテーマ別のツイートを1,000件あたり$0.07から収集でき、100回分の無料リクエストで2,000ツイートの試験収集ができます。
- エンドポイント
POST /search-tweets- 1ページの件数
- 1リクエストあたり約20ツイート
- 料金
- 1,000件あたり$0.07から
#Twitter データセットの入手先
Twitter(X)のデータセットは、2つのどちらかから始まります。誰かがすでに公開しているコレクションか、API で自分で収集するツイートです。公開済みのものは無料で、数分で読み込めます。自分で集めれば、テーマ、言語、期間、ラベルを自分で選べるので、目的に合ったデータになります。2026年にツイートを取得するあらゆる方法は、Twitter のスクレイピング方法のガイドで比較しています。
有名な公開データセットの多くは2009年から2022年の間に集められたもので、当時の Twitter は研究者に API を無料で開放していました。現在ではツイートのテキストをまったく配布していないものもあります。
| データセット | 規模 | 収集時期 | 現在入手できるもの |
|---|---|---|---|
| Sentiment140 | 160万ツイート、テスト用498件 | 2009年 | 顔文字から付けた肯定・否定ラベル付きテキスト |
| TweetEval(Cardiff NLP) | 7つの分類タスク | 2020年のベンチマーク | 感情極性、感情、皮肉、ヘイト、攻撃的表現、絵文字、スタンスのテキストとラベル |
| COVID-19-TweetIDs | 10億ツイート超 | 2020年1月から | ツイート ID のみ |
| ArchiveTeam Twitter archive | 約160億レコード | 2011年から2022年 | 生の JSON、ラベルなし |
| SNAP Twitter7(Stanford) | 4億7,600万ツイート | 2009年 | なし:Twitter の要請で公開停止 |
Sentiment140
- 規模
- 160万ツイート、テスト用498件
- 収集時期
- 2009年
- 現在入手できるもの
- 顔文字から付けた肯定・否定ラベル付きテキスト
TweetEval(Cardiff NLP)
- 規模
- 7つの分類タスク
- 収集時期
- 2020年のベンチマーク
- 現在入手できるもの
- 感情極性、感情、皮肉、ヘイト、攻撃的表現、絵文字、スタンスのテキストとラベル
COVID-19-TweetIDs
- 規模
- 10億ツイート超
- 収集時期
- 2020年1月から
- 現在入手できるもの
- ツイート ID のみ
ArchiveTeam Twitter archive
- 規模
- 約160億レコード
- 収集時期
- 2011年から2022年
- 現在入手できるもの
- 生の JSON、ラベルなし
SNAP Twitter7(Stanford)
- 規模
- 4億7,600万ツイート
- 収集時期
- 2009年
- 現在入手できるもの
- なし:Twitter の要請で公開停止
汎用的な感情分析や感情分類のベースラインなら、ラベル付きの公開データセットのどれかが最も手早い出発点です。コミュニティが管理する awesome-twitter-data のリスト(新しいタブで開きます)には数百件が載っており、今もテキストを配布しているものがわかります。自社製品への反応、今年の出来事、アーカイブが扱わない言語、独自のラベル体系など、特定の目的があるなら、データセットは自分で作ります。
#公開 Twitter データセットの落とし穴
公開 Twitter データセットには4つの問題があります。古いこと、多くが ID だけであること、その ID が年々失われること、ラベルが他人の課題向けに作られていることです。
- 古いテキスト。 Sentiment140 のツイートはすべて140文字以内です。Twitter が上限を280文字に引き上げたのは2017年11月だからです。2009年の言葉で学習したモデルは、2026年のスラング、話題、投稿の長さに初めて出会うことになります。
- テキストではなく ID。 他者と共有するデータセットには、通常、投稿 ID とユーザー ID だけが含まれます。そのため、大規模な COVID-19 コレクションをはじめ、多くの研究用データセットは数字の羅列です。テキストを取り戻すには、ID を再取得(rehydrate)します。API で1件ずつ引き直す作業です。
- 欠落。 削除されたツイートや、投稿者が非公開にした、あるいは凍結されたツイートは、もう返ってきません。失われる行はランダムではないため、復元したデータセットは今もアクティブなアカウントに偏ります。
- 借り物のラベル。 Sentiment140 のラベルは、人がツイートを読んで付けたものではなく、顔文字から付けたものです。ベンチマークのラベルが答えているのはベンチマークの問いであって、自分の問いではありません。
今年の投稿を理解させたいモデルには、道が1つ残ります。1つのタスクのためにクリーニングとラベル付けをした、新規のデータセットです。
#Sorsa API で新規 Twitter データセットを作る方法
新規データセットなら、Twitter(X)API 兼スクレイピングツールである Sorsa API を使います。API キーは1つで、2006年まで遡る X の検索演算子が使え、1リクエストで約20ツイートを1,000件あたり$0.07から取得でき、全プランで毎秒20リクエストです。そのため、100万ツイートのテーマ別データセットは約42分で収集できます。すべてのツイートには投稿者の完全なプロフィールが追加料金なしで付き、自分の X アカウントには一切触れません。
データセットを何で定義するかに応じて、エンドポイントを選びます。
- テーマ、キーワード、期間:
POST /search-tweets(新しいタブで開きます)。 - 1アカウントの投稿:
POST /user-tweets(新しいタブで開きます)。詳しくはユーザーの全ツイートをダウンロードのガイドで解説しています。 - 厳選したアカウント群を1つの X リストとして読む:
GET /list-tweets(新しいタブで開きます)。 - 会話データ用のリプライスレッド:
POST /comments(新しいタブで開きます)。
-
キーを取得する。 サインアップして、ダッシュボードから API キーをコピーします。最初の100リクエストは無料で、カードは不要です。
-
クエリを書く。 検索演算子を使えば、料金を払う前にデータセットの形を決められます。
lang:enで言語を1つに絞り、-filter:retweetsでコピーを除外し、since:とuntil:で期間を固定します。まず、無料の Twitter 高度な検索ツールで手作業でクエリを試してください。 -
テストリクエストを送る。 1リクエストで約20ツイートと、次のページへのカーソルが返ります。
ターミナル curl -X POST "https://api.sorsa.io/v3/search-tweets" \ -H "ApiKey: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"order": "latest", "query": "crashing lang:en -filter:retweets since:2026-10-01"}'レスポンス200 OK { "tweets": [ { "id": "2115807747265175863", "full_text": "The new beta keeps crashing on launch for me", "created_at": "2026-10-29T14:07:31Z", "lang": "en", "likes_count": 12, "reply_count": 3, "retweet_count": 0, "conversation_id_str": "2115807747265175863", "in_reply_to_tweet_id": null, "is_reply": false, "made_with_ai": false, "user": { "id": "1710221540156513285", "username": "maya_trains_models", "followers_count": 412, "created_at": "2023-10-06T09:12:44Z", "verified": false } } ], "next_cursor": "DAADDAABCgABHV5mIq0VsAoKAAIdXmUz8Kqg" } -
ページネーションで取得し、生の JSONL として保存する。
next_cursorが空になるまで返ってきた値を送り直し、すべてのツイートを加工せずに1行1 JSON オブジェクトで書き出します。クリーニングはコピーに対して後から行うので、同じツイートを2度収集して料金を払うことはありません。Python import json import time import requests URL = "https://api.sorsa.io/v3/search-tweets" HEADERS = {"ApiKey": "YOUR_API_KEY"} QUERY = "crashing lang:en -filter:retweets since:2026-10-01" def collect(query, path, limit=50_000): body = {"query": query, "order": "latest"} saved = 0 with open(path, "a", encoding="utf-8") as out: while saved < limit: r = requests.post(URL, headers=HEADERS, json=body, timeout=30) if r.status_code == 429: # over 20 requests per second time.sleep(1) continue r.raise_for_status() # 403: no key, or no requests left page = r.json() for tweet in page.get("tweets", []): tweet["_query"] = query out.write(json.dumps(tweet, ensure_ascii=False) + "\n") saved += 1 if not page.get("next_cursor"): break body["next_cursor"] = page["next_cursor"] return saved print(collect(QUERY, "raw_tweets.jsonl"), "tweets saved")
クラスやテーマごとにクエリを1つずつ、それぞれ別のファイルに保存すると、ファイルごとの件数を見るだけでクラスが少なすぎないかがすぐにわかります。今年より前まで遡りたい場合も、同じエンドポイントで2006年以降のすべての公開ツイートに届きます。大規模なバックフィルでの日付分割については、Twitter の過去データのガイドをご覧ください。
#ツイート ID のデータセットを再取得する
公開済みの ID データセットを復元するには、Sorsa API の POST /tweet-info-bulk(新しいタブで開きます) にツイート ID を1リクエストあたり最大100件送り、返ってきたものを保存します。100万件の ID リストは、$14から、毎秒20リクエストで約8分です。同じ100万件の投稿を公式 X API で読むと、投稿読み取りだけで$5,000かかります。
import json
import requests
URL = "https://api.sorsa.io/v3/tweet-info-bulk"
HEADERS = {"ApiKey": "YOUR_API_KEY"}
with open("tweet_ids.txt", encoding="utf-8") as f:
ids = list(dict.fromkeys(x.strip() for x in f if x.strip()))
found = set()
with open("rehydrated.jsonl", "w", encoding="utf-8") as out:
for i in range(0, len(ids), 100):
body = {"tweet_links": ids[i:i + 100]}
r = requests.post(URL, headers=HEADERS, json=body, timeout=30)
r.raise_for_status()
for tweet in r.json().get("tweets", []):
found.add(tweet["id"])
out.write(json.dumps(tweet, ensure_ascii=False) + "\n")
missing = [x for x in ids if x not in found]
print(f"recovered {len(found)} of {len(ids)}; {len(missing)} gone")
データセットと一緒に、復元率も報告しましょう。何件中何件の ID が、いつ時点で返ってきたかです。同じ ID から1年の間を置いて復元した2つのデータセットは、別のデータセットです。結果を読む人は、どちらで学習したかを知る必要があります。
知っておきたいこと
ツイート ID は、最初から最後まで文字列として扱ってください。ID は19桁ですが、スプレッドシートが保持できる有効桁数は15桁、JavaScript の数値は約16桁です。そのため、Excel や
JSON.parseを通った ID は丸められて戻り、どのツイートにも一致しなくなります。ID ファイルは文字列として読み込み、引用符付きで保存してください。
#ツイートのクリーニングと重複排除
ツイートのデータセットは、4段階でクリーニングします。ID によるリツイートと完全重複の除去、類似テキストの統合、言語と投稿者による絞り込み、ハンドルとリンクのプレースホルダー化です。どの段階も、すべてのツイートに含まれるフィールドを使います。
| フィールド | データセットでの役割 |
|---|---|
id | 主キー。文字列として保存する |
full_text | クリーニング、ラベル付け、学習に使うテキスト |
lang | 各分割を1つの言語に保つ |
conversation_id_str | スレッドをまとめ、分割時に同じ側に置けるようにする |
in_reply_to_tweet_id | リプライを親ツイートに結び付け、会話ペアを作る |
made_with_ai | X が AI 作成と表示した投稿の印。人間のテキストのデータセットからは除外する |
user.followers_count、user.created_at | スパムやボットを見分けるための投稿者シグナル |
likes_count、reply_count | 品質の下限。誰も反応しなかった投稿はスパムであることが多い |
id- データセットでの役割
- 主キー。文字列として保存する
full_text- データセットでの役割
- クリーニング、ラベル付け、学習に使うテキスト
lang- データセットでの役割
- 各分割を1つの言語に保つ
conversation_id_str- データセットでの役割
- スレッドをまとめ、分割時に同じ側に置けるようにする
in_reply_to_tweet_id- データセットでの役割
- リプライを親ツイートに結び付け、会話ペアを作る
made_with_ai- データセットでの役割
- X が AI 作成と表示した投稿の印。人間のテキストのデータセットからは除外する
user.followers_count、user.created_at- データセットでの役割
- スパムやボットを見分けるための投稿者シグナル
likes_count、reply_count- データセットでの役割
- 品質の下限。誰も反応しなかった投稿はスパムであることが多い
類似重複の除去は、多くのパイプラインが飛ばす段階です。コメント付きのリツイート、コピーされた投稿、ボットのキャンペーン、10個のアカウントが貼り付けた同じ不満は、いずれも ID チェックをすり抜け、その内容を水増しします。まずテキストを正規化し(小文字化、リンク・ハンドル・句読点の除去)、次に単語シングルを比較します。3語の連なりの大半を共有する2つの投稿は、同じことを言っています。下のチェッカーは、貼り付けたテキストに対してこのテストを実行します。
- ツイート
- 7
- 整形後のユニーク数
- 4
- 除外した重複
- 3
-
残すThe new beta keeps crashing on launch :( https://t.co/k2Vq81
the new beta keeps crashing on launch
- 同一テキストRT @priya_ships_features: The new beta keeps crashing on launch :(
- 71%一致the new beta keeps crashing on launch for me!!
-
残すLoving the new dark mode, finally easy on the eyes
loving the new dark mode finally easy on the eyes
- 同一テキスト@nadia_tests_builds loving the new dark mode... finally easy on the eyes https://t.co/x9Lm2
-
残すBattery life on the beta is noticeably better this week
battery life on the beta is noticeably better this week
-
残すDoes anyone else get logged out after the update?
does anyone else get logged out after the update
1行に1ツイートを貼り付けてください。整形後のテキストで3語連続の並びが70%以上共通する行は、1つのグループにまとめられ、各グループの先頭行だけが残ります。
モデルが読むテキストでは、ハンドルを @user に、リンクを http に置き換えます。Cardiff NLP のツイート用モデルが使っている慣例です。こうすると、モデルは最も頻繁に出てくるアカウントやリンクではなく、人々の書き方を学びます。元の full_text はクリーニング後のテキストと一緒に残しておくと、後でクリーニングの方法を変えても、再収集せずに済みます。
import hashlib
import json
import re
def norm(text):
text = re.sub(r"https?://\S+", " ", text.lower())
text = re.sub(r"^rt @\w+:?|@\w+", " ", text)
text = re.sub(r"[^\w\s#]", " ", text)
return " ".join(text.split())
seen, rows = set(), []
for line in open("raw_tweets.jsonl", encoding="utf-8"):
t = json.loads(line)
if t.get("retweeted_status") or t.get("made_with_ai"):
continue
clean = norm(t["full_text"])
key = hashlib.sha1(clean.encode()).hexdigest()
if key in seen or len(clean.split()) < 4:
continue
seen.add(key)
rows.append(t)
ハッシュで見つかるのは、正規化後の完全なコピーです。数百万件規模であいまい一致を探すなら、MinHash と局所性鋭敏型ハッシュを使うと、すべてのペアを比較せずにシングルの比較ができます。
#データセットにラベルを付ける
ツイートのデータセットには、2層でラベルを付けます。人が手作業でラベルを付ける数百件の正解セットと、モデルが事前にラベル付けし、モデルが迷った箇所を人が確認する大量の残りです。正解セットは学習に使いません。大量のラベルが十分な品質かどうかを判断するためのテストです。
作業を始める前に、ラベルのルールを書き出します。各クラスの例と、判断が難しいケース(皮肉、質問、他人の意見の引用)の例を添えてください。2人が同じ100ツイートにラベルを付けて結果を比べれば、ルールの曖昧な箇所がわかります。各ラベルには、誰が(何が)付けたか(human、model、query)も保存しておくと、後から精度の低いものを外したり重みを変えたりできます。
検索クエリを弱いラベルとして使うこともできます。「keeps crashing」で集めたツイートは否定寄りです。ただし、使うのは最初の立ち上げだけにしてください。クエリによるラベルでは、モデルは感情ではなくクエリの言葉を学んでしまいます。
#リークなしで学習用とテスト用に分割する
ツイートのデータセットは、行ごとではなく、会話と時間で分割します。最新の数週間をテストセットとして取り分け、すべてのスレッドを境界の片側にまとめます。ランダムに分割すると、投稿とそのリプライ、あるいは同じテキストの2つのコピーが学習とテストの両方に入り、テストのスコアは能力ではなく記憶を測ることになります。
import hashlib
rows.sort(key=lambda t: t["created_at"])
cut = rows[int(len(rows) * 0.9)]["created_at"] # newest 10% = test
test = [t for t in rows if t["created_at"] >= cut]
threads = {t.get("conversation_id_str") for t in test}
rest = [t for t in rows if t["created_at"] < cut
and t.get("conversation_id_str") not in threads]
def bucket(t):
key = t.get("conversation_id_str") or t["id"]
return int(hashlib.md5(key.encode()).hexdigest(), 16) % 10
train = [t for t in rest if bucket(t) != 0]
valid = [t for t in rest if bucket(t) == 0]
Twitter のボット検出のように、投稿ではなくアカウントを対象にするタスクでは、代わりに user.id でグループ化します。こうすれば、同じアカウントが両側に現れません。
#モデルが読める形式で保存する
ツイートのデータセットは、1行1レコードの JSONL で保存し、すべてのレコードにツイート ID を入れます。JSONL はストリーミングや追記ができ、pandas、Hugging Face の datasets ライブラリ、ほとんどの学習スクリプトにそのまま読み込めます。各行に入れるフィールドは、用途によって決まります。
| 用途 | JSONL の各行に入れるもの |
|---|---|
| 分類器またはトピックモデル | id、クリーニング済みの text、label、label_source、lang、created_at |
| モデルやプロンプトの評価セット | id、text、期待される答え、検証するルール |
| 検索用の RAG インデックス | id、text、投稿 URL、created_at、投稿者 ID。回答が投稿を引用できるようにする |
分類器またはトピックモデル
- JSONL の各行に入れるもの
id、クリーニング済みのtext、label、label_source、lang、created_at
モデルやプロンプトの評価セット
- JSONL の各行に入れるもの
id、text、期待される答え、検証するルール
検索用の RAG インデックス
- JSONL の各行に入れるもの
id、text、投稿 URL、created_at、投稿者 ID。回答が投稿を引用できるようにする
{
"id": "2115807747265175863",
"text": "the new beta keeps crashing on launch for me",
"label": "negative",
"label_source": "human",
"lang": "en",
"created_at": "2026-10-29T14:07:31Z",
"split": "test"
}
ファイルにはデータセットカードを添えてください。クエリと期間、収集または再取得した日、復元率、ラベルの作り方、除外したもの、このデータセットを何に使うべきでないかを書きます。Hugging Face のデータセットカードも同じ構成で、次にこのデータで学習する人、たいていは半年後の自分が、そのすべてを必要とします。
#Twitter データセットの費用
100万ツイートのデータセットは、公式 X API では投稿読み取りだけで$5,000かかります。Sorsa API なら、検索で$70から、既知の ID を再取得するなら$14からです。件数を切り替えると、検索で新規収集した場合の合計を確認できます。
公式 X API
$5,000
100万 ツイートの場合
1投稿 $0.005
Sorsa API
$70から
100万 ツイートの場合
1,000件あたり$0.07から
公式 X API には、データセットの計算を変える制限が2つあります。1つ目は、従量課金プランの投稿読み取りが月300万件で止まることです。そのため1,000万ツイートのデータセットには Enterprise 契約が必要になり、X の料金表(新しいタブで開きます)でもこの上限を超える場合の案内があります。2つ目は、2026年11月22日から、各投稿とともに返される投稿者プロフィールもユーザー読み取りとして課金され、投稿1件あたり$0.010が加算されることです(X のお知らせ(新しいタブで開きます))。Sorsa API では、投稿者の完全なプロフィールがすべてのツイートに含まれ、失敗したリクエストは課金されません。
時間も予算の半分を占めます。毎秒20リクエストで、Sorsa API は検索なら毎秒約400ツイート、ID 指定なら毎秒最大2,000ツイートを収集できます。そのため、新規100万ツイートは約42分、1,000万ツイートのデータセットでもキー1つで約7時間です。
#ツイートデータセットを共有し、学習に使う方法
公開されている研究用データセットはツイート ID とユーザー ID の形で配布され、ID があれば誰でも API で全文を取り戻せます。Sorsa API なら100万件の ID を約8分で再取得できます。
- 共有。 公開されている研究用データセットと同じように、ツイート ID とユーザー ID を再取得用のスクリプトと一緒に公開します。
POST /tweet-info-bulkは1リクエストで100件の ID を完全な投稿に戻します。 - 学習。 収集した全文は、自分のモデルのために保管します。ツイートのデータセットの一般的な用途は、分類器、トピックモデル、評価セット、検索インデックス、言語モデルのファインチューニングです。
- 個人。 学習の前にハンドルを置き換え、個人のプロフィールを保持せず、データセットを更新するときは投稿者が削除したツイートを取り除いてください。
Sorsa API が返すのは公開データのみで、投稿、いいね、フォローは一切行いません。研究プロジェクトのある大学は、Sorsa API の学術利用についてもお問い合わせいただけます。
#試験収集からデータセット作りを始める
公開 Twitter データセットはベースラインになりますが、新規データセットなら、自分のテーマ、自分の期間、タスクに合ったラベルが手に入ります。作るなら Sorsa API です。検索なら1,000ツイートあたり$0.07から、ID 指定なら1,000件あたり$0.014から、毎秒20リクエストで、すべてのツイートに投稿者のプロフィールが付きます。100回分の無料リクエストで最初の2,000ツイートを収集でき、お金を払う前に、クエリ、クリーニング、分割を試すのに十分です。
#よくある質問
Twitter データセットとは何ですか?
Twitter データセットとは、X(旧 Twitter)の投稿を、分析や機械学習のためにファイルに保存したものです。通常は、ツイート ID、テキスト、日時、言語、エンゲージメント数、投稿者のフィールドを含み、感情などのラベルが付くこともあります。公開されているデータセットの多くは、研究データを共有する際の慣例に従ってツイート ID だけを収録しているため、テキストは再取得する必要があります。
Twitter データセットを無料でダウンロードできる場所はどこですか?
Kaggle、Hugging Face、GitHub の awesome-twitter-data リストには、数百件の無料 Twitter データセットがあります。2009年の160万件のラベル付きツイートを持つ Sentiment140 や、TweetEval ベンチマークもその一部です。大半は2023年より前に収集されたもので、多くはツイート ID だけを配布しているため、API で再取得しないとテキストが手に入りません。Sorsa API なら、1リクエストあたり100件の ID を1,000件あたり$0.014から再取得できます。
Twitter データセットを作るには、どの API を使えばよいですか?
Twitter データセットの作成には Sorsa API を使います。X の演算子を使った2006年までの検索は、1リクエストで約20ツイートを1,000件あたり$0.07から取得できます。再取得は1リクエストで100件の ID を1,000件あたり$0.014からです。どのプランも毎秒20リクエストで、API キー1つ、開発者アカウントも OAuth も不要です。公式 X API は1,000投稿あたり$5.00で、投稿読み取りは月300万件までです。
Twitter のデータで LLM を学習できますか?
はい。X 上の公開の会話は、学習やファインチューニングのデータとしてよく使われます。ツイートのデータセットは、感情やトピックの分類器、評価セット、アシスタントが実際の投稿を引用できるようにする検索拡張(RAG)のインデックスにも使われます。Sorsa API なら毎秒20リクエストで収集でき、検索では1リクエストで約20ツイートを1,000件あたり$0.07から、ID 指定では1リクエストで100件を1,000件あたり$0.014から取得できます。
100万ツイートの収集にはどのくらいかかりますか?
Sorsa API では、キー1つで毎秒20リクエストの場合、100万ツイートは検索で約42分、ツイート ID 指定なら約8分です。クリーニングと重複排除でその一部が減るので、試験収集で減る割合を測り、最終的に欲しい規模にその分を上乗せして集めてください。
#検証方法
2026年11月2日に、docs.x.com で X の従量課金の料金表を確認し、Sentiment140、TweetEval、SNAP の Twitter7 のデータセットページも調べました。このページの Sorsa API のエンドポイント、フィールド、ページサイズは、同日時点の公開 API リファレンスと一致しています。掲載したレスポンスはそのリファレンスのスキーマに沿ったもので、コードサンプルを API に対して実行してはいません。収集時間は、ページサイズと毎秒20リクエストというレート制限からの計算値です。