Denoで10年間の過去ツイートをPixelaグラフに取り込む
@kawarimidollのアカウントはいつの間にか登録から10年以上が経過していました。
(後から登録した他のサイトと違ってTwitterだけKawarimiDoll
でパスカルケース…)
これを見て、実際自分がどれくらい使っているのか確認したくなりした。
今回はPixelaを使って草を生やして確認したいと思います。
作業記録(Zenn Scrap)はこちら
Pixelaとは
GitHubの草のごとく、なんでもピクセルグラフにして記録を取れるツールです。
基本的にAPI経由でしか操作できないところが特徴的で、開発者向けのツールという感じがします。
唐突に宣伝
そういえば「草っぽい文字画像を作るツール」を以前作っていました。
当時はZennのアカウントを持っていなかったのでQiitaに解説があります。
なお、これもDeno 🦕 で動いています。
Pixelaにユーザー登録する
さて、本題に戻ります。
まずPixelaにユーザー登録が必要です。
Create User APIからユーザー作成を行ってください。
規約の同意等に関する項目があるのでコード例は控えます。
ユーザー登録ができると、https://pixe.la/@<username>
にユーザーページがつくられます。
グラフを作成する
グラフの作成を行います。
Create Graph APIを使います。
❯ curl -X POST https://pixe.la/v1/users/kawarimidoll/graphs -H 'X-USER-TOKEN:xxxxx' -d '{"id":"tweets","name":"tweets","unit":"tweets","type":"int","color":"sora","timezone":"Asia/Tokyo"}'
{"message":"Success.","isSuccess":true}
Twitterなので青系のグラフにしてみました。
ブラウザからhttps://pixe.la/v1/users/<username>/graphs/<graphID>.html
にアクセスすると、テーブルを確認できます。
空っぽのグラフが作成できました。
データを取得する
過去のツイート数はtwilogのステータスページから確認します。 ここ見ればPixela使わなくて良いじゃんとか言わない
ページのソースを見てみると、script
タグにデータが埋め込まれ、画面の表示に使われているようです。
ar_data
にツイート数が、ar_lbl
に日付が格納されており、日次データはインデックス1となっています。
ということで、以下のコードで取得できます。
import { ky } from "./deps.ts";
const username = "kawarimidoll";
const html = await ky(`https://twilog.org/${username}/stats`).text();
const tweets = html.match(/ar_data\[1\]\s*=\s*\[([^\]]+)\];/);
const dates = html.match(/ar_lbl\[1\]\s*=\s*\[([^\]]+)\];/);
if (!tweets || !dates || !tweets[1] || !dates[1]) {
console.warn("fetch failed");
Deno.exit(1);
}
const getLength = 365;
const tweetsArray = tweets[1].split(",").slice(-getLength);
const datesArray = dates[1].split(",").slice(-getLength);
console.log(tweetsArray);
console.log(datesArray);
なおky
については以下の記事で解説しています。
ひとまず1年分、365件を取り出してみました。古いものから並んでいるので、slice(-365)
で後ろから抽出します。
❯ deno run --allow-net fetch_twilog.ts
[
"3", "14", "7", "4", "67", "2", "1", "0", "1", "2", "1",
"73", "2", "2", "1", "1", "2", "3", "98", "3", "1", "0",
"1", "4", "2", "49", "0", "1", "7", "2", "2", "4", "82",
"1", "0", "7", "5", "2", "4", "74", "0", "2", "2", "5",
"0", "3", "78", "7", "1", "0", "0", "1", "2", "85", "3",
"3", "3", "1", "1", "5", "80", "2", "4", "15", "10", "4",
"2", "38", "2", "1", "6", "12", "6", "5", "81", "0", "0",
"4", "1", "2", "2", "89", "3", "2", "2", "4", "6", "5",
"88", "5", "1", "4", "1", "2", "0", "56", "3", "2", "6",
"8",
... 265 more items
]
[
"'200624'", "'200625'", "'200626'", "'200627'", "'200628'",
"'200629'", "'200630'", "'200701'", "'200702'", "'200703'",
"'200704'", "'200705'", "'200706'", "'200707'", "'200708'",
"'200709'", "'200710'", "'200711'", "'200712'", "'200713'",
"'200714'", "'200715'", "'200716'", "'200717'", "'200718'",
"'200719'", "'200720'", "'200721'", "'200722'", "'200723'",
"'200724'", "'200725'", "'200726'", "'200727'", "'200728'",
"'200729'", "'200730'", "'200731'", "'200801'", "'200802'",
"'200803'", "'200804'", "'200805'", "'200806'", "'200807'",
"'200808'", "'200809'", "'200810'", "'200811'", "'200812'",
"'200813'", "'200814'", "'200815'", "'200816'", "'200817'",
"'200818'", "'200819'", "'200820'", "'200821'", "'200822'",
"'200823'", "'200824'", "'200825'", "'200826'", "'200827'",
"'200828'", "'200829'", "'200830'", "'200831'", "'200901'",
"'200902'", "'200903'", "'200904'", "'200905'", "'200906'",
"'200907'", "'200908'", "'200909'", "'200910'", "'200911'",
"'200912'", "'200913'", "'200914'", "'200915'", "'200916'",
"'200917'", "'200918'", "'200919'", "'200920'", "'200921'",
"'200922'", "'200923'", "'200924'", "'200925'", "'200926'",
"'200927'", "'200928'", "'200929'", "'200930'", "'201001'",
... 265 more items
]
良さげです。
日付を整形する
草を生やすには、PixelaのUpdate Pixel APIを使います。
ここで、twilogから取得した日付は'yyMMdd'
という形式になっていますが、APIのエンドポイントに合わせるため、/v1/users/<username>/graphs/<graphID>/<yyyyMMdd>
の形式に変更する必要があります。
日付の整形といっても、世紀をまたぐことは想定されないので、頭に20
をつければOKです。
import { ky } from "./deps.ts";
- const username = "kawarimidoll";
+ const twitterUsername = "kawarimidoll";
+ const pixelaUsername = "kawarimidoll";
+ const pixelaGraphId = "tweets";
- const html = await ky(`https://twilog.org/${username}/stats`).text();
+ const html = await ky(`https://twilog.org/${twitterUsername}/stats`).text();
const tweets = html.match(/ar_data\[1\]\s*=\s*\[([^\]]+)\];/);
const dates = html.match(/ar_lbl\[1\]\s*=\s*\[([^\]]+)\];/);
if (!tweets || !dates || !tweets[1] || !dates[1]) {
console.warn("fetch failed");
Deno.exit(1);
}
const getLength = 365;
const tweetsArray = tweets[1].split(",").slice(-getLength);
const datesArray = dates[1].split(",").slice(-getLength);
- console.log(tweetsArray);
- console.log(datesArray);
+ for (let i = 0; i < tweetsArray.length; i++) {
+ const url =
+ `https://pixe.la/v1/users/${pixelaUsername}/graphs/${pixelaGraphId}/20${
+ datesArray[i].replaceAll("'", "")
+ }`;
+ const quantity = tweetsArray[i];
+ console.log(url, quantity);
+ }
❯ deno run --allow-net fetch_twilog.ts
https://pixe.la/v1/users/kawarimidoll/graphs/tweets/20200624 3
https://pixe.la/v1/users/kawarimidoll/graphs/tweets/20200625 14
https://pixe.la/v1/users/kawarimidoll/graphs/tweets/20200626 7
https://pixe.la/v1/users/kawarimidoll/graphs/tweets/20200627 4
# (略)
OKですね。
後はこれに順次リクエストをかければ草が生えていきます。
草を生やす
ではリクエストを実行していきます。
※以下は全件について実行するコードですが、自分が最初に試したときはテストで数件から行いました。
一度にリクエストをかけると攻撃になりかねないと思い、ループごとに2秒のインターバルを置きました。
import { ky } from "./deps.ts";
+ import { PIXELA_TOKEN } from "./env.ts";
const twitterUsername = "kawarimidoll";
const pixelaUsername = "kawarimidoll";
const pixelaGraphId = "tweets";
const html = await ky(`https://twilog.org/${twitterUsername}/stats`).text();
const tweets = html.match(/ar_data\[1\]\s*=\s*\[([^\]]+)\];/);
const dates = html.match(/ar_lbl\[1\]\s*=\s*\[([^\]]+)\];/);
if (!tweets || !dates || !tweets[1] || !dates[1]) {
console.warn("fetch failed");
Deno.exit(1);
}
- const getLength = 365;
- const tweetsArray = tweets[1].split(",").slice(-getLength);
- const datesArray = dates[1].split(",").slice(-getLength);
+ const tweetsArray = tweets[1].split(",");
+ const datesArray = dates[1].split(",");
for (let i = 0; i < tweetsArray.length; i++) {
const url =
`https://pixe.la/v1/users/${pixelaUsername}/graphs/${pixelaGraphId}/20${
datesArray[i].replaceAll("'", "")
}`;
- const quantity = tweetsArray[i];
- console.log(url, quantity);
+ const json = { quantity: tweetsArray[i] };
+ console.log(url, json);
+ console.log(
+ await ky.put(url, { headers: { "X-USER-TOKEN": PIXELA_TOKEN }, json })
+ .json(),
+ );
+ // wait 2 sec...
+ await new Promise((resolve) => setTimeout(resolve, 2000));
}
なお、トークンをenv.ts
から読み込んでいますが、ここの作りに関しては以下の記事にまとめています。
実行します。
❯ deno run --allow-net --allow-read --allow-env fetch_twilog.ts
https://pixe.la/v1/users/kawarimidoll/graphs/tweets/20210621 { quantity: "16" }
{ message: "Success.", isSuccess: true }
https://pixe.la/v1/users/kawarimidoll/graphs/tweets/20210622 { quantity: "23" }
{ message: "Success.", isSuccess: true }
(略)
だーーーーー……
10年プラス半年で3800件程度のデータがありました。1件ごとに2秒のインターバルが入るので…それなりに時間がかかります。
全期間適用できました。
Freeユーザーは1年以上前の草は見られないのですが、データとしては存在しています。
非常に個人的なグラフ表示結果の分析
しばらくニチアサ実況アカウントだったので日曜だけツイートが多く、他の日はほとんどつぶやいていませんでした。
キラメイジャーが完結したのを機にテレビを手放したので、3月くらいから一気にツイートが減りました。
5月末にvim-jpのslackに参入したことをきっかけにツイッターにinする時間も増え、一気に色が濃くなっています。
これ以降も同じ処理を行えばグラフを更新できますが、もちろん毎回10年のデータを取得する必要はありません。
たとえばデイリーバッチで毎日実行するなら、最新1日ぶんずつ実行すれば良いことになります。
おわりに
Twilogからデータを取得してPixelaへ投げるという処理を実装してみました。
DenoとKyのおかげで必要最低限の記述で済んでいます。
GitHubの草もそうですが、活動が可視化されるとやる気が出ますね。
他のものもPixelaに繋げられないか、いろいろ考えようと思います。
参考
Discussion