拡張機能なしで、ツイートを一括削除
Chromeのコンソールに貼り付けるだけの無料スクリプトで、古いX投稿を片づけましょう。まずシミュレーション、最近のツイートは保護、削除は上限つきのバッチ処理。拡張機能もサードパーティのサービスも不要で、データがブラウザの外に出ることは一切ありません。
DRY_RUN = true)で提供されています。何にも触れずに、削除される予定のツイートを一覧表示するだけです。先にXのアーカイブをダウンロードし、シミュレーションを確認して、それからだけ実際の削除に切り替えてください。ウェブ画面の自動操作はXの利用規約で想定されていません。控えめに、自己責任でお進めください。
手順
- プロフィールを開く:
x.com/anata_no_usernameにアクセスし、ポストタブを開いて、アカウントにログインしておきます。 - コンソールを開く:
F12(MacではCmd+Option+J)を押し、Console タブをクリックします。 - スクリプトを設定する:下のコードをコピーし、
anata_no_usernameをあなたのXのユーザー名(@なし)に置き換え、KEEP_RECENT(残す最近のツイートの数)を調整します。必要なら、絶対に削除しないツイートのIDをKEEP_STATUS_IDSに並べます。 - シミュレーションする:スクリプトをコンソールに貼り付け、Enterで実行します。
DRY_RUN = true(既定の設定)では、何も削除せずに、削除される予定のツイート一覧を表示します。 - 削除する:シミュレーションの内容でよければ、
DRY_RUNをfalseにして再実行します。スクリプトは1回の実行で最大40件を削除し、6〜14秒の待機をはさみます。 - 繰り返す:残りをバッチで処理するために、必要な回数だけスクリプトを再実行します。
スクリプト
(() => {
// ============ 設定 ============
const HANDLE = "anata_no_username"; // あなたのXのユーザー名、@なし
const KEEP_RECENT = 15; // 最新の15件のツイート(一番上)は絶対に削除しない
const KEEP_STATUS_IDS = [ // 追加の保護(任意):絶対に削除しないツイートのID
"1234567890123456789",
];
let DRY_RUN = true; // true = シミュレーション(一覧表示のみ)。false = 実際に削除。
const MIN_DELAY_MS = 6000;
const MAX_DELAY_MS = 14000;
const MAX_DELETIONS = 40; // 1回の実行あたりの上限(続けるには再実行)
// ================================
const me = HANDLE.toLowerCase();
const KEEP = new Set(KEEP_STATUS_IDS.map(String));
const protectedRecent = new Set();
const RX_DELETE = /(削除|delete|supprimer)/i;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const rand = (a, b) => Math.floor(a + Math.random() * (b - a));
const skipped = new Set();
if (!location.pathname.toLowerCase().startsWith("/" + me)) {
console.warn(`%c[停止] https://x.com/${HANDLE}(ポストタブ)へ移動してから再実行してください。`,
"color:#e0245e;font-weight:bold");
return;
}
const articles = () => [...document.querySelectorAll('article[data-testid="tweet"]')];
const idOf = (art) => {
const href = [...art.querySelectorAll('a[href*="/status/"]')]
.map((x) => x.getAttribute("href"))
.find((h) => new RegExp(`^/${HANDLE}/status/\\d+`, "i").test(h));
if (!href) return null;
const m = href.match(/status\/(\d+)/);
return m ? m[1] : null;
};
// 最新のKEEP_RECENT件(一番上のツイート)を読み込んで保護する。
async function protectRecent() {
const order = [], seen = new Set();
let noNew = 0;
window.scrollTo(0, 0);
await sleep(1200);
while (order.length < KEEP_RECENT && noNew < 6) {
let added = 0;
for (const art of articles()) {
const id = idOf(art);
if (id && !seen.has(id)) { seen.add(id); order.push(id); added++; }
}
noNew = added ? 0 : noNew + 1;
if (order.length >= KEEP_RECENT) break;
const cs = articles();
(cs[cs.length - 1] || document.body).scrollIntoView({ block: "end" });
await sleep(1400);
}
for (const id of order.slice(0, KEEP_RECENT)) protectedRecent.add(id);
return order.slice(0, KEEP_RECENT);
}
const eligible = (art) => {
const id = idOf(art);
if (!id) return null;
if (protectedRecent.has(id)) return null; // 最新のうちの1件
if (KEEP.has(id)) return null; // 追加で保護されたID
if (skipped.has(id)) return null;
return id;
};
async function deleteOne(art, id) {
const caret = art.querySelector('[data-testid="caret"]');
if (!caret) { skipped.add(id); return false; }
caret.click();
await sleep(700);
const del = [...document.querySelectorAll('[role="menuitem"]')]
.find((i) => RX_DELETE.test((i.innerText || "").trim()));
if (!del) { document.body.click(); skipped.add(id); return false; }
del.click();
await sleep(700);
const confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
if (!confirmBtn) { skipped.add(id); return false; }
confirmBtn.click();
await sleep(1500);
return true;
}
async function dryScan() {
const seen = new Set();
let noNew = 0;
console.log("%c[シミュレーション] 何も削除されません。カウント中...", "color:#1d9bf0;font-weight:bold");
while (noNew < 4) {
let added = 0;
for (const art of articles()) {
const id = eligible(art);
if (id && !seen.has(id)) { seen.add(id); added++; }
}
noNew = added ? 0 : noNew + 1;
const cs = articles();
(cs[cs.length - 1] || document.body).scrollIntoView({ block: "end" });
await sleep(1200);
}
console.log(`%c[シミュレーション] ${seen.size}件のツイートが削除される予定です(最新のものは保護されています)。`,
"color:#1d9bf0;font-weight:bold");
console.log([...seen].slice(0, 60));
}
async function run() {
let count = 0, guard = 0, noHit = 0, started = false;
while (count < MAX_DELETIONS && guard++ < 1500) {
const pair = articles().map((a) => [a, eligible(a)]).find(([, id]) => id);
if (!pair) {
if ((started && noHit >= 6) || noHit >= 40) break;
const cs = articles();
(cs[cs.length - 1] || document.body).scrollIntoView({ block: "end" });
await sleep(1500);
noHit++;
continue;
}
started = true; noHit = 0;
const [node, id] = pair;
node.scrollIntoView({ block: "center" });
await sleep(600);
const ok = await deleteOne(node, id);
if (ok) count++;
console.log(`${ok ? "削除" : "スキップ"} ${id} (合計: ${count}/${MAX_DELETIONS})`);
await sleep(rand(MIN_DELAY_MS, MAX_DELAY_MS));
}
console.log(`%c[完了] 今回は${count}件削除しました。残りがあればスクリプトを再実行してください。`,
"color:#00ba7c;font-weight:bold");
}
(async () => {
console.log(`最新の${KEEP_RECENT}件のツイートを読み込んで保護しています...`);
const recent = await protectRecent();
if (recent.length < KEEP_RECENT) {
console.warn(`%c[停止] ${recent.length}件しか読み込めませんでした(Xが制限をかけています)。ページを再読み込み(F5)してもう一度お試しください。`,
"color:#e0245e;font-weight:bold");
return;
}
console.log("%c保護済み(最新):", "color:#00ba7c;font-weight:bold", recent);
if (DRY_RUN) { await dryScan(); return; }
await run();
})();
})();
スクリプトがあなたを守るしくみ
4つの安全装置が組み込まれています。まず既定でシミュレーション。DRY_RUN が true のあいだは何も削除されず、対象となるツイートの一覧が表示されるだけです。次に最近のツイートの保護。スクリプトはまず最新のツイート(既定は15件)を記憶し、それらには決して触れません。さらにホワイトリスト。KEEP_STATUS_IDS に入れたIDはどれも削除されず、固定した投稿やお知らせに便利です。最後に慎重なペース。各削除の間に6〜14秒のランダムな待機をはさみ、40件で自動的に停止して、Xの制限を招かないようにします。
大掃除の前に、大切なものはアーカイブしておきましょう。メールアドレス・URL抽出ツールはツイートのエクスポートからすべてのリンクを取り出し、文字数カウンターは何年分の投稿がどれだけの分量だったかを教えてくれ、テキスト整形ツールはアーカイブのコピペをきれいに整えます。これからのツイートには、Unicode装飾文字ツールや大文字小文字変換ツールがお役に立ちます。ツール集のすべてのツールは、このガイドと同じく、プライバシーポリシーの考え方に沿って、あなたのブラウザ内だけで動作します。
よくある質問
削除は取り消せますか?
いいえ。削除したツイートは完全に消え、Xにはゴミ箱のような機能はありません。だからこそスクリプトはシミュレーションモード(DRY_RUN = true)で提供されています。まずはそのまま実行して削除される予定のツイート一覧を確認し、内容に納得できてからだけ false に切り替えてください。事前にXのアーカイブをダウンロードしておくこともおすすめします(設定、次にアカウント、次にデータのアーカイブをダウンロード)。
他人のツイートを削除できてしまいますか?
いいえ。Xは自分の投稿にしか削除ボタンを表示しません。スクリプトはログイン中のアカウントの、あなた自身のプロフィールページ上でしか動作しません。他人の返信やサードパーティのコンテンツには一切触れません。
なぜ削除は40件が上限で、待機時間もランダムなのですか?
プラットフォームに負担をかけないためです。削除が速すぎたり大量すぎたりすると、Xの制限がかかったり、アカウントが一時的にロックされたりすることがあります。スクリプトは各削除の間に6〜14秒待機し、40件で停止します。続きを処理するには、もう一度実行してください。
これはXで認められていますか?
自分のコンテンツを削除することはあなたの権利です。一方で、ウェブ画面の自動操作はXの利用規約で想定されていません。このスクリプトは控えめに、自己責任でお使いください。公式な代替手段は手動での削除か、APIを利用する専用サービスです。
特定のツイートだけを守るには?
2つの保護が併用できます。KEEP_RECENT は最近のツイートを自動的に残し(既定は15件)、KEEP_STATUS_IDS には絶対に削除しないツイートのIDのリストを指定できます。ツイートのIDは、アドレスの末尾にある大きな数字です。x.com/username/status/1234567890123456789。