Twitterの画像をScrapboxに引用するスクリプトを作る
(のりしろ)
ChatGPTでTwitterのツイートを取得する実験をやったがうまくいかなかった
自分で作ることにした
できたもの
/motoso/Tweetを取り込むPopup menu v2
連続したツイートには対応していないのでTwitter to Scrapbox GPTの利用も検討してください基素.icon
どうやって対応したの?……ってtwitterのwebページからscrapingしたのかtakker.icon
Scrapboxから実行することにこだわらないほうが簡単で便利かも?わからん。
使い方がよくわからなかったwogikaze.icon
同意tkgshn.icon
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に追加すればいいのか?
そうです基素.icon
---
できるまでのログ
Twitterの画像をScrapboxに引用するスクリプトを作る
https://pic.twitter.com/RFED10Ssn6 から画像に変換することはできなそう
上の例なら https://twitter.com/motoso/status/1637434235534589952/photo/1 にリダイレクトされるだけ
欲しいURLは https://pbs.twimg.com/media/FrlVwJyaUAA9iHC?format=jpg&name=large
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から復元できるなぁ
code: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 = imageElement0.attribs.src
// TODO: 画像をGyazoに送りつける
lines.push(imageURl)
return(lines.join('\n'))
}
console.log(await getLatestTweet());
こういうのをUserScriptから呼び出せるかな?
流石に重そう
API serverを立てて呼び出せば作れるtakker.icon
例:tweet2image
いい方法はあるだろうか
deno、プロジェクトを作るのを考えずにコードを書いたらすぐ動くのいいな〜
方法は2つtakker.icon
puppeteer経由でweb scrapingして取得する
基素.iconさんの試したやつ
twitter.comの内部APIを借りて取得する
けしからんことしていいならこれが確実
けしからんこと=robots.txtの禁止事項を無視する
現状見る限りTwitterのAPIまわりはめちゃくちゃになっていそうなので、真面目に守る必要性をあんまり感じない
ここまでTwitterの公式APIが壊れてるなら、なりふり構ってられない
こんな感じかな基素.icon
https://github.com/fa0311/TweetURLtoData/blob/master/TweetURLtoData.py
そんな感じですtakker.icon
もう作っているひといたのか
自分だけrobots.txt守っているのもばからしいな(モラルハザード)
あれ?Twitterの内部APIのend pointってこんなに単純だったっけ?
もっといろんなAPIを叩いていた覚えがあるのだが……
まあ試せばわかるか
Twitterの内部APIで試す
ported from fa0311/TweetURLtoData
要/takker/GM_fetch
code: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.icon
https://gyazo.com/ebe0f523bba9771d49acd06016b334ff
展開先URLも含まれている
text: tweet本文
photos: 展開先画像URL
entities.urls: 展開先外部リンク
あとcdn.syndication.twimg.com/robots.txtは空っぽだったので、怒られなければ使っても良さそう
倫理面もクリア!!!
試した。やりかた基素.icon
/takker/GM_fetch#63e0576b1280f000005cda28をTamperMonkeyに追加
Chrome Developer Toolsのコンソールに上のコードをコピペ
$ await window.getTweet('1637434235534589952')
twitter-info-proxyを改造してこの機能を入れたい基素.icon
こんな感じで置換したら良さそう基素.icon
code: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')
とりあえず動くものはできた基素.icon
/motoso/Tweetを取り込むPopup menu v2
TODO
scrapbox-url-customizerに統合したい
TwitterのURLの場合だけこっちが読み出されるようにしたい
Gyazoアップロード機能
連ツイはどうすれば取得できる?
これはAPIでも取れなかったのでKarel Capekではin_reply_toからやや複雑なSQLで自前で構築してます基素.icon
当時、内部向けにしかないのかーと思った
2.0なら取れるかも?
oEmbed APIを使うのが良さそうinajob.icon
https://developer.twitter.com/en/docs/twitter-for-websites/oembed-api
認証不要、RateLimitなし
APIなのでrobot.txtで不許可にもしていない
inlineはこれを使っている
画像ではなくHTMLが取れる
画像URLがとれないんですよね基素.icon
あ、そうか、これを使うときはwidgets.jsが取ってきて埋めてるのかな?inajob.icon
widgets.jsがiframeを作っていた、残念