Skip to content

Instantly share code, notes, and snippets.

@ssig33
Last active June 8, 2024 16:13
Show Gist options
  • Save ssig33/e0b16eae6eaeba90fce6353fb7cf998a to your computer and use it in GitHub Desktop.
Save ssig33/e0b16eae6eaeba90fce6353fb7cf998a to your computer and use it in GitHub Desktop.
tweetdeck スクレイピング

Twitter のストリーミング API が滅亡した今、 tweetdeck をスクレイピング(?)するのが一番簡単に twitter の更新情報をリアルタイムに取得する方法だと思います。

ここではその方法を説明します。

1. puppeteer

puppeteer は極めて強力なソフトウェアで、本物の Chrome を使って簡単に Web サイトの操作を自動化したりスクレイピングしたりできます。

今回のような用途で特筆すべき点は、 puppeteer は Web アプリケーションの Ajax リクエストのレスポンスを横取りできる点です。以下に例を示します

// puppeteer の起動
// これを書いている人は Docker で起動しているので root ユーザーでの実行となるのでそのためにいくつか設定をしている
// 一般ユーザー権限で実行する場合は no-sandbox とかは不要
const browser = await puppeteer.launch({headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox', '--enable-logging']});
const page = await browser.newPage();
await page.goto("https://twitter.com/login?hide_message=true&redirect_after_login=https%3A%2F%2Ftweetdeck.twitter.com%2F%3Fvia_twitter_login%3Dtrue", {waitUntil: "domcontentloaded"});
// Twitter のログインページにはフォームがいくつもありどれに入れたらいいのかよく分かってないので面倒なので全部に入力してます
await page.evaluate(()=> document.querySelectorAll("input[name='session[username_or_email]']").forEach((n)=> n.value = 'ユーザー名'));
await page.evaluate(()=> document.querySelectorAll("input[name='session[password]']").forEach((n)=> n.value = 'パスワード'));
await page.evaluate(()=>{
// ここで 2 秒待っているのは生活の知恵みたいなもんです、こうするとうまくいく
setTimeout(()=>{
document.querySelectorAll("button.submit").forEach((n)=> n.click())
}, 2000);
});

これだけで Ajax リクエストのレスポンスをのぞき見することができます。

2. 実践

tweetdeck がどういう作りになっているかというと、高速に API をポーリングしているだけで、 Web Socket のようなめんどくさい仕組みを使っていません。

大量にコネクションを持続させるよりは通信量としては無駄があってもとにかくリクエストされまくるほうが(いちいちリソースを捨てられる分)まだましみたいな判断があるのだと思います。

この作りになっていることは非常に有利です。

2-1. tweetdeck へのログイン

puppeteer で tweetdeck にログインするには以下のようにします

// puppeteer の起動
// これを書いている人は Docker で起動しているので root ユーザーでの実行となるのでそのためにいくつか設定をしている
// 一般ユーザー権限で実行する場合は no-sandbox とかは不要
const browser = await puppeteer.launch({headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox', '--enable-logging']});
const page = await browser.newPage();
await page.goto("https://twitter.com/login?hide_message=true&redirect_after_login=https%3A%2F%2Ftweetdeck.twitter.com%2F%3Fvia_twitter_login%3Dtrue", {waitUntil: "domcontentloaded"});
// Twitter のログインページにはフォームがいくつもありどれに入れたらいいのかよく分かってないので面倒なので全部に入力してます
await page.evaluate(()=> document.querySelectorAll("input[name='session[username_or_email]']").forEach((n)=> n.value = 'ユーザー名'));
await page.evaluate(()=> document.querySelectorAll("input[name='session[password]']").forEach((n)=> n.value = 'パスワード'));
await page.evaluate(()=>{
// ここで 2 秒待っているのは生活の知恵みたいなもんです、こうするとうまくいく
setTimeout(()=>{
document.querySelectorAll("button.submit").forEach((n)=> n.click())
}, 2000);
});

2-2. API レスポンスを横取り

以下のようにします

page.on("response", async (res)=>{
const url = res.url();
// home timeline だけ処理するようにします
if(!url.match(/api\.twitter\.com/)){ return }
if(!url.match(/home_timeline.json/)){ return }
if((await res.text()) === ""){ return true }
try{
console.log('tweet received');
const data = await res.json();
// あとはこの JSON を煮るなり焼くなりするだけ
// ぼくは Slack の Incoming Webhook に送り付けて Twitter IRC Gateway みたいな環境を再現しています
data_wo_syori_suru_nanrakano_funcion(data);
} catch(e) {
console.log(url);
console.log(e);
console.log(await res.text());
}
})
@nakagawa1131
Copy link

こんにちは、コメント失礼します。
tweetdeckがなくなり、x proになった現在でもリアルタイムで新規投稿を取得することは可能なのでしょうか?

@ssig33
Copy link
Author

ssig33 commented Jun 8, 2024

@nakagawa1131 残念ながら X Pro になってだいぶ実装が変わっています。ですが似たようなアプローチで取得することは可能だと思います(ブラウザのコンソールからいろいろ眺めてると雰囲気分かる)。

ただ僕はいまはTwitterの自動化とか発言の取得とかは https://github.com/fa0311/twitter-openapi-typescript/tree/master/twitter-openapi-typescript-generated こちらを使っています。

@nakagawa1131
Copy link

@ssig33 ご丁寧にありがとうございます。
特定のキーワードが含まれたポストをリアルタイムで取得しようとしているのですが、現状では中々難しいんですね。
twitter-openapi-typescriptの方も見てみます。
ありがとうございます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment