はじめに

Twitterデータの tweet.js を読み込んで全ツイート履歴を表示するツール「tweet.js loader」の紹介 で作った tweet.js loader の技術面について整理した。

TL;DR

  • 主に埋め込み用ツイートの表示で苦労した話をまとめた
  • Vue + GitHub Pages の設定例についても少しまとめた

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 使用しているもの
    2. ポイントや苦労した箇所など
      1. ツイートの埋め込み
    3. Vue CLI + GitHub Pages
      1. ビルド結果出力先の設定
      2. publicPath の設定
    4. Vue Router + GitHub Pages
  5. まとめ
  6. その他・メモ
  7. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15
BuildVersion: 19A602

$ node -v
v12.7.0

$ npm -v
6.10.3

$ vue -V
3.10.0

$ npm v vue
vue@2.6.10 | MIT | deps: none | versions: 250

$ npm v vue-router
vue-router@3.1.3 | MIT | deps: none | versions: 65

詳細

リポジトリ: 17number/tweet-js-loader

使用しているもの

レベル感とか粒度とか適当だけど、以下を使用して作成。

より細かく見たい人は package.json を参照。

ポイントや苦労した箇所など

ツイートの埋め込み

埋め込み用コード

ツイートの埋め込み自体は Twitter Publish で、URL を指定すると HTML コードが取得できる。

↑は https://twitter.com/kantei/status/1183296748027969539 を指定し生成された HTML コードを埋め込んだもので、コードとしては以下のようなものが生成される。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<blockquote class="twitter-tweet">
<p lang="ja" dir="ltr">
【お知らせ】
<a href="https://twitter.com/kantei_hisai?ref_src=twsrc%5Etfw">
@kantei_hisai
</a>
(被災者応援情報)では、各省庁や地方自治体等の発信する情報をリツイート等で発信しています。ぜひご活用ください。
<a href="https://t.co/KHnlLLGL80">
pic.twitter.com/KHnlLLGL80
</a>
</p>
&mdash; 首相官邸 (@kantei)
<a href="https://twitter.com/kantei/status/1183296748027969539?ref_src=twsrc%5Etfw">
October 13, 2019
</a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

途中に挟まっているテキスト(【お知らせ】 など)は、ツイートが消された時でも最低限の情報は表示できるようにというもの。なので、埋め込みに必要な情報だけに極限まで削ると以下だけで良い。

1
2
3
4
<blockquote class="twitter-tweet">
<a href="https://twitter.com/kantei/status/1183296748027969539"></a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

https://platform.twitter.com/widgets.jsscript タグで読み込んだ時(= JS 実行時)に、<blockquote class="twitter-tweet"> で囲まれた部分を解析して、<twitter-widget> というタグに置き換えている模様。

1
2
3
<twitter-widget class="twitter-tweet twitter-tweet-rendered" id="twitter-widget-0" style="..." data-tweet-id="1183296748027969539">
<!-- ここにツイートのデータ -->
</twitter-widget>
widgets.js の挙動

前セクションで、<blockquote class="twitter-tweet">widgets.js の読み込み完了時に置き換わることが分かったが、大量に <blockquote class="twitter-tweet"> を生成後に widgets.js を読み込むとエラーとなった。

Developer Tools の Network タブで確認したところ、414 URI Too Long

どうやら https://cdn.syndication.twimg.com/tweets.json に対して情報を要求しているが、クエリパラメータとして Tweet ID を指定しているため埋め込みが多すぎると「URI が長すぎるよ!」とエラーになる模様。(以下のように idstweet_id をカンマ(%2C)区切りで指定)

1
https://cdn.syndication.twimg.com/tweets.json?callback=__twttr.callbacks.cb0&ids=1160544024694054912%2C1185383050273669120&lang=en&suppress_response_codes=true&theme=light&tz=GMT%2B0900`

ここは Vue Router を導入して、ページネーションで表示件数を制限することで対応した。(ここでも苦労したが、その話は に記載)

なお、axioshttps://cdn.syndication.twimg.com/tweets.json を直接叩くことも試みたが、CORS の設定であっさりと弾かれたのでまぁまぁ粘ったけど断念した。

screen_name の省略

ここであらためて Twitterデータ から取得した tweet.js の構造を見てみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
window.YTD.tweet.part0 = [ {
"tweet": {
"retweeted" : false,
"source" : "<a href=\"https://twitter.com/hogehoge\" rel=\"nofollow\">hogehoge</a>",
"entities" : {
"hashtags" : [ ],
"symbols" : [ ],
"user_mentions" : [ {
"name" : "アカウント名",
"screen_name" : "hogehoge",
"indices" : [ "3", "16" ],
"id_str" : "1234567890",
"id" : "1234567890"
} ],
"urls" : [ {
"url" : "https://t.co/ABCDEF",
"expanded_url" : "https://google.co.jp",
"display_url" : "https://google.co.jp",
"indices" : [ "64", "87" ]
} ]
},
"display_text_range" : [ "0", "87" ],
"favorite_count" : "0",
"id_str" : "1234567890",
"truncated" : false,
"retweet_count" : "0",
"id" : "1234567890",
"possibly_sensitive" : false,
"created_at" : "Sun Jan 1 12:34:56 +0000 2019",
"favorited" : false,
"full_text" : "RT @hogehoge: My Tweet\nhttps://t.co/ABCDEF",
"lang" : "ja"
}
},

分かりづらいかもしれないが、実はツイート主の screen_name はどこにも含まれていない。

ツイート主(の screen_name)を特定しようと思うと、Twitter API - GET collections/show に Tweet ID(https://twitter.com/screen_name/status/**1234** の最後の数値部分。↑だと id とか id_str の部分)を指定して実行するしか無さそうだが、それはやりたくない(面倒だし、API Rate Limit の話もあるし)。

そこで screen_name を使わない方向で検討、以前は https://twitter.com/-/status/1234 みたいに、- を使えば screen_name を指定しなくても良かったがいつの間にか使えなくなっていた。

当然同じ疑問を持つ人が居て解決策まで提示してくれていた。

現在は https://twitter.com/i/status/1234 のように - ではなく i を指定すれば良い模様。ということで、twitter.js のデータを元に以下の HTML を生成し、最後に https://platform.twitter.com/widgets.js を読み込むこととした。

1
2
3
4
5
6
7
8
<blockquote class="twitter-tweet">
<p lang="ja" dir="ltr">
<a href="https://twitter.com/kantei_hisai"></a>
</p>
<a href="https://twitter.com/kantei/status/1183296748027969539"></a>
</blockquote>
...
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
ページ遷移時に埋め込みツイートを削除

ここまででツイートの埋め込み表示が実現できたが、Vue Router によるページ遷移後に埋め込みツイートが切り替わってくれなかった。

Tweet.vue<blockquote class="twitter-tweet"> の部分を描画しているのだが、widgets.js<twitter-widget> に置き換わってしまうため、ページ遷移後に描画対象の HTML 要素が無くて上手くいっていないようだった。

そこで無理やりではあるが、ナビゲーションガードbeforeRouteUpdate を使って以下の処理を行っている。

  • <twitter-widget> の削除
  • 読み込み済の widgets.js の削除

その後、Tweet.vueupdated フック内で以下を実施。

  • <blockquote> 要素が無ければ insertAdjacentHTML で追加

最後に Home.vueupdated フック内で以下を実施。

  • widgets.js の読み込み

かなり無理やり実現している(もっとうまくできそう)感があるが、Vue.js にそこまで詳しいわけじゃないのでいったん良しとする。

最終的にこうなった
  • n(= 60 にした)件ずつ埋め込みツイートの元ネタ(<blockquote class="twitter-tweet">)を埋め込み
  • ページ遷移したら、以下を実施することで各ページごとの埋め込みツイート表示を実現
    • 現在の埋め込みツイート(<twitter-widget>)を削除して
    • 埋め込みツイート用のスクリプト(widgets.js)を削除して
    • 埋め込みツイートの元ネタ(<blockquote>)を生成して
    • 埋め込みツイート用のスクリプト(widgets.js)を読み込む

Vue CLI + GitHub Pages

この辺の話は別記事(Vue CLI で作った Vue Router 利用プロジェクトを GitHub Pages で公開する方法)にもまとめた。

ビルド結果出力先の設定

docs ディレクトリを GitHub Pages の対象に設定できるので、vue.config.jsoutputDir オプションを追加して docs にビルド結果を出力するように変更。あと filenameHashing も合わせてオフにした。

1
2
3
4
 module.exports = {
+ outputDir: 'docs',
+ filenameHashing: false,
}

publicPath の設定

(設定やディレクトリ構成次第ではあるが) docs/js/hoge.jshttps://username.github.io/repository/js/hoge.js に配置される。

だが、デフォルトの設定だと https://username.github.io/js/hoge.js を読みにいくような HTML が出力されるので、vue.config.jspublicPath の設定を追加する。

1
2
3
4
5
 module.exports = {
+ publicPath: '/repository', // 今回の自分のケースだと '/tweet-js-loader' を設定
outputDir: 'docs',
filenameHashing: false,
}

Vue Router + GitHub Pages

Vue Routerhttps://username.github.io/repository/ というリポジトリ名ありの URL で利用する場合、new VueRouter するときに base オプションを指定する。

1
2
3
4
5
6
7
8
9
10
new Router({
mode: 'history',
base: 'repository/', // 今回の自分のケースだと 'tweet-js-loader/' を設定
routes: [
{
path: '/',
component: Home,
},
],
});

config/dev.env.jsconfig/prod.env.js を用意して、base: process.env.BASE_URL, としても良い。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// config/dev.env.js
module.exports = merge(prodEnv, {
NODE_ENV: 'development',
BASE_URL: ''
})

// config/prod.env.js
module.exports = {
NODE_ENV: 'production',
BASE_URL: 'repository/'
}

// VueRouter の設定
new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
component: Home,
},
],
});

vue-routerのgithub-pages用設定 - Qiita を参考に ROOT_BASE を使ったら、何故か上手くいかなくてハマった。ググっても同事例が出てこなかったので、自分の環境だけかもしれない。

まとめ

  • 主に埋め込み用ツイートの表示で苦労した話をまとめた
  • Vue + GitHub Pages の設定例についても少しまとめた

その他・メモ

tweet.js loader 使ってみて。

紹介記事: Twitterデータの tweet.js を読み込んで全ツイート履歴を表示するツール「tweet.js loader」の紹介

参考文献

関連記事