URLをリンクに変更する (WebHelpersのauto_link)

マリオカートやり過ぎて目がシバシバしているbonlifeです。この記事を見て、そういうのって色んな人が似たようなことやってるのよね、きっと。と思って少しだけ調べてみました。

正規表現を使ってURLをリンクにする。

それっぽいキーワードで適当に検索したところ、Pylonsで使われているWebHelpersにRailsにインスパイアされた機能があることが判明。

WebHelpersのauto_link_urls

AUTO_LINK_RE = re.compile(r"""
                        (                          # leading text
                          <\w+.*?>|                # leading HTML tag, or
                          [^=!:'"/]|               # leading punctuation, or 
                          ^                        # beginning of line
                        )
                        (
                          (?:https?://)|           # protocol spec, or
                          (?:www\.)                # www.*
                        ) 
                        (
                          [-\w]+                   # subdomain or domain
                          (?:\.[-\w]+)*            # remaining subdomains or domain
                          (?::\d+)?                # port
                          (?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path
                          (?:\?[\w\+%&=.;-]+)?     # query string
                          (?:\#[\w\-]*)?           # trailing anchor
                        )
                        ([\.,"'?!;:]|\s|<|$)       # trailing text
                           """, re.X)

捕捉しないカッコが出るだけで難しく見えるマジックですね。この正規表現をこんな関数で使う。

def auto_link_urls(text, **href_options):
    extra_options = tag_options(**href_options)
    def handle_match(matchobj):
        all = matchobj.group()
        a, b, c, d = matchobj.group(1, 2, 3, 4)
        if re.match(r'<a\s', a, re.I):
            return all
        text = b + c
        if b == "www.":
            b = "http://www."
        return '%s<a href="%s%s"%s>%s</a>%s' % (a, b, c, extra_options, text, d)
    return re.sub(AUTO_LINK_RE, handle_match, text)

<a で始まってたら、処理せずにそのまま返して、www. 始まりだったら http:// をつけるのね。正規表現の h の後にも ? を追加して ttp:// はじまりにも対応したら良いのに。ってこれは日本限定の文化かな。

WebHelpersのauto_link_email_addresses

ちなみにメールアドレス用の正規表現もそれらしい関数に含まれてましたよ。

def auto_link_email_addresses(text):
    return re.sub(r'([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)',
                  r'<a href="mailto:\1">\1</a>', text)

ふーん。何で \w 使ったり使わなかったりなんだろう。後半部分でも \w 使えますよね。って思って調べてみたら、\w だと _ (アンダースコア)も含んでしまうんですね。だから外してるのか。これくらいがザル過ぎず、複雑になり過ぎない絶妙なラインってことなのかしら。って納得しかけたけど、ローカルパート部分に . (ドット)が2回出てくる?アレレ。 + の次にあるエスケープされていないドットは何なんだろう…。文字クラスの書き方が分からなくなってきましたが、とりあえず保留。