0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

X投稿URLの表記ゆれを正規化して投稿IDだけを取り出す

0
Posted at

ユーザーから「Xの投稿のURL」を受け取って処理する機能を作ると、最初に躓くのがURLの表記ゆれです。同じ投稿を指していても、貼られてくる文字列は何種類もあります。

パーサーを素直に書くと、テストで使った1パターンだけ通って、実際に使われ始めた途端に「このURLは対応していません」を連発します。

実際に貼られてくるパターン

手元で見かけた範囲でも、これだけあります。

https://x.com/user/status/1234567890123456789
https://twitter.com/user/status/1234567890123456789
https://mobile.twitter.com/user/status/1234567890123456789
https://x.com/user/status/1234567890123456789?s=20&t=AbCdEf
https://x.com/i/status/1234567890123456789
https://x.com/i/web/status/1234567890123456789
https://x.com/user/status/1234567890123456789/photo/1
https://x.com/user/status/1234567890123456789/video/1

ドメインが2種類、サブドメイン付きがあり、共有時に付くクエリパラメータがあり、ユーザー名の代わりに i が入る形式があり、末尾にメディアの位置を示すパスが付くこともあります。

さらに、モバイルアプリの共有からコピーすると前後に改行や説明文がくっついてくることもあります。貼り付け欄に入ってくるのは「URLだけ」とは限りません。

結局必要なのは投稿IDひとつ

ここで方針を決めると楽になります。このURLから欲しいのは投稿IDだけです。ドメインもユーザー名もクエリも、IDを取り出すための飾りでしかありません。

ユーザー名は特にあてになりません。i に置き換わることがあるうえ、そもそも投稿者がハンドルを変更すれば古いURLのユーザー名部分は実体と一致しなくなります。それでもIDが同じなら同じ投稿を指します。

つまり正規化の仕事は「URLをきれいにすること」ではなく、入力から19桁前後の数値IDを1つ確実に取り出すことです。

function extractPostId(input) {
  const m = String(input).match(/(?:x|twitter)\.com\/(?:i\/(?:web\/)?)?[^/]+\/status(?:es)?\/(\d+)/);
  return m ? m[1] : null;
}

これで上のパターンは全部通ります。statuses も許しているのは古いURLが残っているためです。クエリも末尾の /photo/1 も、マッチ対象の外にあるので勝手に無視されます。

やりすぎないほうがいい正規化

逆に、やらないほうがいいこともあります。

短縮URLを自前で展開しない。 t.co が挟まっている場合に、サーバー側で先にリダイレクトを追いかけて実体を得たくなりますが、ユーザーの入力を起点に任意のURLへリクエストを飛ばす処理になります。入口を絞るか、素直に対応外として返すほうが安全です。

IDの見た目で弾かない。 桁数で厳密にチェックしたくなりますが、IDの桁は将来増えます。数値であること以上の制約を入れると、いずれ正しいURLを弾く側に回ります。

エラーは3つに分ける

実装上いちばん効くのはこれでした。失敗を全部「無効なURLです」にまとめないことです。

  • URLとして解釈できなかった — 別サイトのURL、ただの文章、空欄
  • 投稿は特定できたが、対象が取得できない — 削除済み、非公開アカウント
  • 投稿は取得できたが、メディアが含まれていない — テキストだけの投稿

1番目はユーザーの貼り間違いなので貼り直してもらえば解決します。2番目は何度貼り直しても解決しません。3番目はそもそも取りに行く対象がありません。同じメッセージで返すと、ユーザーは2番目と3番目でも貼り直しを繰り返すことになります。

実際のツールでの扱い

私が公開しているX Video Downloaderでも、入力の扱いはこの方針です。x.com と twitter.com の両方を受け付け、共有時に付くクエリや末尾のメディアパスは無視して投稿IDだけを見ます。取得できるのは公開されている投稿だけで、非公開アカウントや削除済みの投稿は対象外です。Xとは無関係の個人プロジェクトです。

まとめ

URLの表記ゆれは「後で対応する」と思っていると、問い合わせの形でまとめて返ってきます。入力から取り出したいものをIDひとつに絞ってしまえば、パターンが増えても正規表現の外側の話になるので、あとが楽になります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?