きっかけは保存したい会話があったから
X(旧Twitter)のダイレクトメッセージには、大切な会話が残っています。グループでの企画会議や、友人との長いやりとり。これらを読みやすい形で保存したい場面があります。
X公式のアーカイブ機能を使えば、すべてのデータをダウンロードできます。しかし、ダウンロードしたファイルはJSON形式で、そのままでは読みにくいのが現状です。
この記事では、XのDMデータをWhatsAppのような読みやすいテキスト形式に変換する方法を紹介します。
Xのアーカイブ機能でデータを取得する
まずは、X公式のアーカイブ機能を使ってDMデータを取得します。
設定画面から「データのアーカイブをダウンロード」を選択し、パスワードを入力して確認します。数時間から1日程度で準備が完了し、メール通知が届きます。
ダウンロードしたZIPファイルを解凍すると、「direct-messages.js」というファイルが含まれています。これがDMの履歴データです。
ただし、このファイルはJavaScript形式で記述されており、人間が読むには適していません。JSON(JavaScript Object Notation)というデータ形式で構造化されていますが、複雑な階層構造になっています。
データの構造を理解する
direct-messages.jsファイルの中身は、次のような構造になっています。
window.YTD.direct_messages.part0 = [
{
"dmConversation": {
"conversationId": "会話のID",
"messages": [
{
"messageCreate": {
"senderId": "送信者のID",
"text": "メッセージの内容",
"createdAt": "2025-05-19T05:49:36.000Z"
}
}
]
}
}
]
Code language: JavaScript (javascript)
この構造を理解すると、必要な情報は次の3つであることがわかります。
- 送信者のID(senderId)
- メッセージの内容(text)
- 送信日時(createdAt)
Pythonスクリプトで変換する
Pythonを使って、この複雑なデータ構造を読みやすいテキストに変換します。
まず、必要なライブラリをインストールします。
pip install pytz
pytzは、時刻を日本時間に変換するために使用します。XのデータはUTC(世界標準時)で記録されているためです。
変換スクリプトの核となる部分は、JSONデータの読み込みです。JavaScript形式のファイルから、JSON部分だけを抽出する必要があります。
# JSファイルを読み込み
with codecs.open(js_file_path, 'r', 'utf-8', 'ignore') as f:
content = f.read()
# JSONデータを抽出
json_start = content.find('[')
json_data = content[json_start:]
dm_data = json.loads(json_data)
Code language: PHP (php)
この処理により、ファイルの先頭にある「window.YTD.direct_messages.part0 = 」の部分を取り除き、純粋なJSONデータだけを取得できます。
時刻の変換処理
XのタイムスタンプはISO 8601形式で記録されています。これを日本時間に変換し、読みやすい形式にします。
# ISO形式の日時をパース
dt = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
# JSTに変換
jst = pytz.timezone('Asia/Tokyo')
dt_jst = dt.astimezone(jst)
timestamp_str = dt_jst.strftime('%Y/%m/%d %H:%M:%S')
Code language: PHP (php)
この変換により、「2025-05-19T05:49:36.000Z」が「2025/05/19 14:49:36」のような日本時間の表記になります。
会話の選択機能
複数のDM会話がある場合、特定の会話だけを抽出したい場合があります。スクリプトでは、利用可能な会話を一覧表示し、ユーザーが選択できるようにしています。
# 会話リストを表示
for i, conversation in enumerate(dm_data):
conv_id = conversation['dmConversation']['conversationId']
messages = conversation['dmConversation']['messages']
print(f"{i+1}. 会話ID: {conv_id} ({len(messages)}メッセージ)")
Code language: PHP (php)
これにより、どの会話を変換するかを明確に選択できます。
出力形式の統一
最終的な出力は、WhatsAppのエクスポート形式に合わせています。
[2025/05/19 14:49:36] ユーザーID: メッセージの内容
Code language: CSS (css)
この形式は、日時、送信者、メッセージが一目で分かる構造になっています。
メディア添付がある場合は、メッセージの末尾に「[メディア添付: X件]」という表記を追加します。実際の画像や動画ファイルは別途保存されているためです。
完成したスクリプト
以下が、実際に使用できる完全なスクリプトです。
# -*- coding: utf-8 -*-
"""
X(Twitter)のdirect-messages.jsをWhatsApp風chat.txt形式に変換するスクリプト
Author: chiilabo
URL: chiilabo.jp
"""
import json
import codecs
from datetime import datetime
import pytz
def convert_x_dm_to_chat(js_file_path, output_file="x_chat_converted.txt", target_conversation_id=None):
"""
XのDMデータをchat.txt形式に変換
Args:
js_file_path: direct-messages.jsのパス
output_file: 出力ファイル名
target_conversation_id: 特定の会話IDを指定(Noneなら全会話)
"""
try:
# JSファイルを読み込み
with codecs.open(js_file_path, 'r', 'utf-8', 'ignore') as f:
content = f.read()
# JSONデータを抽出
json_start = content.find('[')
if json_start == -1:
print("JSONデータが見つかりません")
return
json_data = content[json_start:]
dm_data = json.loads(json_data)
print(f"読み込み完了: {len(dm_data)}個の会話を発見")
# 会話リストを表示
print("\n利用可能な会話:")
for i, conversation in enumerate(dm_data):
conv_id = conversation['dmConversation']['conversationId']
messages = conversation['dmConversation']['messages']
print(f"{i+1}. 会話ID: {conv_id} ({len(messages)}メッセージ)")
# 特定の会話を処理するか全体を処理するか
if target_conversation_id:
conversations_to_process = [conv for conv in dm_data
if conv['dmConversation']['conversationId'] == target_conversation_id]
if not conversations_to_process:
print(f"会話ID {target_conversation_id} が見つかりません")
return
else:
# 会話を選択
print("\n処理したい会話番号を入力してください(全て処理する場合は0):")
choice = input("番号: ").strip()
if choice == "0":
conversations_to_process = dm_data
else:
try:
index = int(choice) - 1
if 0 <= index < len(dm_data):
conversations_to_process = [dm_data[index]]
else:
print("無効な番号です")
return
except ValueError:
print("数字を入力してください")
return
# メッセージを抽出・変換
all_messages = []
for conversation in conversations_to_process:
conv_id = conversation['dmConversation']['conversationId']
messages = conversation['dmConversation']['messages']
for message in messages:
msg_create = message['messageCreate']
# 送信者ID
sender_id = msg_create['senderId']
# メッセージテキスト
text = msg_create.get('text', '')
# 時刻(ISO形式からJSTに変換)
created_at = msg_create['createdAt']
dt = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
jst = pytz.timezone('Asia/Tokyo')
dt_jst = dt.astimezone(jst)
timestamp_str = dt_jst.strftime('%Y/%m/%d %H:%M:%S')
# メディア添付の確認
if 'mediaUrls' in msg_create and msg_create['mediaUrls']:
text += f" [メディア添付: {len(msg_create['mediaUrls'])}件]"
all_messages.append({
'timestamp': dt_jst,
'timestamp_str': timestamp_str,
'sender_id': sender_id,
'text': text,
'conversation_id': conv_id
})
# 時系列順にソート
all_messages.sort(key=lambda x: x['timestamp'])
# chat.txt形式で出力
with open(output_file, 'w', encoding='utf-8') as f:
f.write(f"X(Twitter) DM変換結果\n")
f.write(f"変換日時: {datetime.now().strftime('%Y/%m/%d %H:%M:%S')}\n")
f.write(f"総メッセージ数: {len(all_messages)}\n")
f.write("=" * 50 + "\n")
for msg in all_messages:
f.write(f"[{msg['timestamp_str']}] {msg['sender_id']}: {msg['text']}\n")
print(f"\n変換完了: {len(all_messages)}件のメッセージを {output_file} に保存しました")
# 統計情報
senders = {}
for msg in all_messages:
sender = msg['sender_id']
senders[sender] = senders.get(sender, 0) + 1
print("\n参加者別メッセージ数:")
for sender, count in sorted(senders.items(), key=lambda x: x[1], reverse=True):
print(f" {sender}: {count}件")
except Exception as e:
print(f"エラーが発生しました: {e}")
import traceback
traceback.print_exc()
# 使用例
if __name__ == "__main__":
print("X DM → Chat形式変換ツール")
print("=" * 40)
js_file = input("direct-messages.jsのパスを入力してください: ").strip()
output_file = input("出力ファイル名を入力してください(デフォルト: x_chat.txt): ").strip()
if not output_file:
output_file = "x_chat.txt"
convert_x_dm_to_chat(js_file, output_file)
Code language: PHP (php)
実際の使用手順
スクリプトを使用する手順は次の通りです。
- PythonのIDLEまたはコマンドラインでスクリプトを実行します
- direct-messages.jsファイルのパスを入力します
- 利用可能な会話一覧が表示されるので、変換したい会話番号を選択します
- 出力ファイル名を指定します(省略可能)
- 変換が完了すると、指定したファイルに結果が保存されます
変換後のファイルは、テキストエディタで開けば、時系列順に整理されたメッセージが確認できます。
注意点とメリット
このスクリプトを使用する際の注意点があります。
アーカイブデータの再取得は30日間隔を空ける必要があります。また、削除されたメッセージはアーカイブに含まれません。
一方で、メリットも多くあります。オフライン環境でメッセージを確認でき、全文検索も可能です。バックアップとしても活用できます。
まとめ
XのDMアーカイブデータをPythonで処理することで、読みやすいテキスト形式に変換できます。JSON構造の理解、時刻変換、文字エンコーディング処理が主要な技術要素となります。変換されたテキストファイルは、会話の保存や検索に活用できる実用的な形式になります。