Skip to content

Instantly share code, notes, and snippets.

@ssig33
Last active November 16, 2021 05:23
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
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());
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment