cloverrose's blog

Python, Machine learning, Emacs, CI/CD, Webアプリなど

Twitter公式に合わせたurlize フィルター #djangoja

Django sprint 13.2で、Djangoの標準urlizeフィルタの挙動がイケてないって気づいた。

urlizeフィルタはプレーンな文字列中からhttp://cloverrose.hateblo.jp/といったURL文字列を見つけて
http://cloverrose.hateblo.jp/のようにアンカーにしてくれるもの

何がイケてないか調べると、URLの前後に空白文字がないといけないってことだった。

フィルタはdjango/template/defaultfilters.pyのdef urlize(value, autoescape=None):で定義されている。

実際のロジックはdjango/utils/html.pyのdef urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):で定義されている。
関係ある場所だけに簡単化したもの

word_split_re = re.compile(r'(\s+)')
simple_url_re = re.compile(r'^https?://\w')

def urlize(text):
    for word in word_split_re.split(text):  # 空白文字でtextを分割
        if '.' in word or '@' in word or ':' in word:  # urlはこれらの文字を含む
            # Deal with punctuation.  # 前後の余計な文字を削除する(この部分の実装が怪しい気がするけど、置いとく)
            # 略

            # Make URL we want to point to.
            if simple_url_re.match(middle):  # 先頭マッチ
                url = smart_urlquote(middle)

            # Make link.  # aタグをつける
            # 略

これを読むと、URLの前後に空白がないと正しく発見、URL化できないことがわかる。

次のイケてないところはtargetの設定(特に_blank)ができないこと
他サイトへのリンクはtarget="_blank"にしたい。
extending urlize in django - Stack Overflowにあるように、
出来上がったaタグに置換を施すアドホックなアイデアが示されている。


これらの問題を、最初はURLの前後にスペースをつけて、Django標準のurlizeフィルタでURL化して、
置換によりtarget="_blank"をつけるって方法を考えた。
でも、URLの前後にスペースをつけるための正規表現が結構複雑になった。

そこで、Twitterぽく@や#もURL化して、かつ前後に空白がなくてもURL化できるカスタムフィルタを定義することにした。

変則的な部分に関しては今後も逐次追加していく。


おまけ:
Twitter公式の挙動を調べる & テストするために
こんなかんじのプロフィールになった
twhy - ken5aさんのプロフィールログ
twhy - ken5aさんのプロフィールログ

#に関しては【正規表現,jQuery,PHP】twitterハッシュタグを抽出する - すたら日記に書いてある
@に関しては、@1 @_など、一文字の数字や_も許されてる。@nameの後に\w(半角英数字とアンダーバー)以外の文字列が来たらそこで終了。
@の前はいくつか来てはいけない文字があって、

1. screen_nameとして使える文字(半角英数字とアンダーバー)
2. !@#$%&*


おまけ2:
Twitterの公式でプロフィール変更で@の前にダブルコーテーション"をおいたケースをチェックしたかったんだけど、@の前のダブルコーテーションはなくなってしまう(謎)