generated at
Twitterの画像をScrapboxに引用するスクリプトを作る
(のりしろ)
ChatGPTでTwitterのツイートを取得する実験をやったがうまくいかなかった
自分で作ることにした

できたもの
連続したツイートには対応していないのでTwitter to Scrapbox GPTの利用も検討してください基素
どうやって対応したの?……ってtwitterのwebページからscrapingしたのかtakker
Scrapboxから実行することにこだわらないほうが簡単で便利かも?わからん。
使い方がよくわからなかったwogikaze
同意tkgshn
https://scrapbox.io/api/code/takker/GM_fetch/scrapbox.user.js TamperMonkeyにいれて
https://scrapbox.io/api/code/motoso/Tweet%E3%82%92%E5%8F%96%E3%82%8A%E8%BE%BC%E3%82%80Popup_menu_v2/script.js をUserScriptに追加すればいいのか?
そうです基素

---
できるまでのログ

Twitterの画像をScrapboxに引用するスクリプトを作る
https://pic.twitter.com/RFED10Ssn6 から画像に変換することはできなそう
上の例なら https://twitter.com/motoso/status/1637434235534589952/photo/1 にリダイレクトされるだけ
https://twitter.com/motoso/status/1637434235534589952/photo/1 のレスポンスから再現できるか?
ブラウザアクセスをすると document.querySelector("#id__a06mgrsipeq > div > div > div > div > div > a > div > div.r-1p0dtai.r-1pi2tsx.r-1d2f490.r-u8s1d.r-ipm5af.r-13qz1uu > div > img") にある
ただし #id__a06mgrsipeq はアクセスごとに毎回変わる
https://twitter.com/motoso/status/1637434235534589952 にブラウザアクセスしてレスポンスを持ってきたらなんとかできそうだなぁ
Denoだとこんな感じでHTMLから復元できるなぁ
ts
import { load } from "https://esm.sh/cheerio@1.0.0-rc.12"; import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts"; const ACCOUNT_NAME = "motoso"; const ID = '1637434235534589952'; async function getLatestTweet() { const browser = await puppeteer.launch(); // TwitterのプロフィールページのURLを構築する const url = `https://twitter.com/${ACCOUNT_NAME}/status/${ID}`; const page = await browser.newPage(); // UAを設定しないとサポートされていないブラウザ扱いになる // https://stackoverflow.com/questions/67010016/twitter-blocked-in-headless-chrome-puppeteer page.setUserAgent( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36" ) await page.goto(url); // ツイート表示まで待つ。要素表示まで待つに変更したい await new Promise(r => setTimeout(r, 2000)); const html = await page.content(); await browser.close(); // Cheerioを使用して、HTMLからツイートのテキストを抽出する const $ = load(html); const lines :string[] = []; const tweetTextElement = $('#react-root > div > div > div.css-1dbjc4n.r-18u37iz.r-13qz1uu.r-417010 > main > div > div > div > div > div > section > div > div > div:nth-child(1) > div > div > article > div > div > div:nth-child(3) > div:nth-child(2)') const tweetText = tweetTextElement.text() lines.push(tweetText) // 最初のidが毎回変わるみたい const imageElement = $('[id^="id__"] > div > div > div > div > div > a > div > div.r-1p0dtai.r-1pi2tsx.r-1d2f490.r-u8s1d.r-ipm5af.r-13qz1uu > div > img') const imageURl = imageElement[0].attribs.src // TODO: 画像をGyazoに送りつける lines.push(imageURl) return(lines.join('\n')) } console.log(await getLatestTweet());
こういうのをUserScriptから呼び出せるかな?
流石に重そう
API serverを立てて呼び出せば作れるtakker
いい方法はあるだろうか
deno、プロジェクトを作るのを考えずにコードを書いたらすぐ動くのいいな〜

方法は2つtakker
puppeteer経由でweb scrapingして取得する
基素さんの試したやつ
twitter.comの内部APIを借りて取得する
けしからんことしていいならこれが確実
けしからんこと=robots.txtの禁止事項を無視する
現状見る限りTwitterのAPIまわりはめちゃくちゃになっていそうなので、真面目に守る必要性をあんまり感じない
ここまでTwitterの公式APIが壊れてるなら、なりふり構ってられない
こんな感じかな基素
そんな感じですtakker
もう作っているひといたのか
自分だけrobots.txt守っているのもばからしいな(モラルハザード)
あれ?Twitterの内部APIのend pointってこんなに単純だったっけ?
もっといろんなAPIを叩いていた覚えがあるのだが……
まあ試せばわかるか

js
window.getTweet = async (id, lang = "en") => { const params = new URLSearchParams([ ["features", "tfw_timeline_list:linktr.ee,tr.ee,terra.com.br,www.linktr.ee,www.tr.ee,www.terra.com.br;tfw_horizon_timeline_12034:treatment;tfw_tweet_edit_backend:on;tfw_refsrc_session:on;tfw_chin_pills_14741:color_icons;tfw_tweet_result_migration_13979:tweet_result;tfw_sensitive_media_interstitial_13963:interstitial;tfw_experiments_cookie_expiration:1209600;tfw_duplicate_scribes_to_settings:on;tfw_video_hls_dynamic_manifests_15082:true_bitrate;tfw_show_blue_verified_badge:off;tfw_related_videos_15128:many_vids;tfw_tweet_edit_frontend:on"], ["id", id], // 不要だった // ["lang", lang], ]); const res = await GM_fetch(`https://cdn.syndication.twimg.com/tweet-result?${params}`, { headers: { "content-type": "application/json; charset=utf-8", }, }); return await res.json(); };
い け ま し たtakker
展開先URLも含まれている
text : tweet本文
photos : 展開先画像URL
entities.urls : 展開先外部リンク
あとcdn.syndication.twimg.com/robots.txtは空っぽだったので、怒られなければ使っても良さそう
倫理面もクリア!!!
試した。やりかた基素
Chrome Developer Toolsのコンソールに上のコードをコピペ
$ await window.getTweet('1637434235534589952')
twitter-info-proxyを改造してこの機能を入れたい基素
こんな感じで置換したら良さそう基素
js
const lines = [];  const text = tweet.text // URLの置換 let replacedText = ''; tweet.entities.urls.forEach(i => { replacedText = text.replace(i.url, i.expanded_url) }) lines.push(replacedText) // TODO: Gyazoにアップロードする lines.push(tweet.photos.map(u => u.url).join('\n')) lines.join('\n')
とりあえず動くものはできた基素
TODO
scrapbox-url-customizerに統合したい
TwitterのURLの場合だけこっちが読み出されるようにしたい
Gyazoアップロード機能
連ツイはどうすれば取得できる?
これはAPIでも取れなかったのでKarel Capekではin_reply_toからやや複雑なSQLで自前で構築してます基素
当時、内部向けにしかないのかーと思った
2.0なら取れるかも?

oEmbed APIを使うのが良さそうinajob
認証不要、RateLimitなし
APIなのでrobot.txtで不許可にもしていない
inlineはこれを使っている
画像ではなくHTMLが取れる
画像URLがとれないんですよね基素
あ、そうか、これを使うときはwidgets.jsが取ってきて埋めてるのかな?inajob
widgets.jsがiframeを作っていた、残念