chklink.py

正式に管理する気がない一時作業をブログにメモするシリーズ、的な。

ちょいと「htmlページのリンク切れチェック」を手早くやる必要に迫られたのだが、出来合いのデカいちゃんとしたものはいらんし、てことで、まさに「手早く」見繕った。なんだかんだ面倒ではあるのよね、毎度一時間はかからん作業なのだとはいえ。

要 six。
 1 # -*- coding: utf-8 -*-
 2 import re
 3 from six.moves import html_parser
 4 from six.moves.urllib.parse import urlsplit, urlunsplit, unquote, quote
 5 from six.moves.urllib.request import urlopen
 6 
 7 
 8 class LinkChecker(html_parser.HTMLParser):
 9     def __init__(self, base):
10         html_parser.HTMLParser.__init__(self)
11         self._base = base
12 
13     def handle_starttag(self, tag, attrs):
14         def _q(s):
15             return quote(s.encode("utf-8"))
16 
17         dejav = set()
18         for key, value in attrs:
19             if key in ('href', 'src') and not value.startswith("mailto:"):
20                 pageurl = value.strip()
21                 if '#' in pageurl:
22                     pageurl, _ = pageurl.split('#', 1)
23                 if not re.match(r"^https?://", pageurl):
24                     pageurl = self._base + pageurl
25                 if pageurl not in dejav:
26                     dejav.add(pageurl)
27                     sr = urlsplit(pageurl)
28                     chktarg = urlunsplit((sr.scheme, _q(sr.netloc), _q(sr.path), None, None))
29                     try:
30                         urlopen(chktarg)
31                     except Exception as e:
32                         print(e, unquote(chktarg))
33 
34 
35 if __name__ == '__main__':
36     import sys
37     base = sys.argv[1]
38     url = base + sys.argv[2]
39     req = urlopen(url)
40     cont = req.read().decode("cp932")
41     parser = LinkChecker(base)
42     parser.feed(cont)
43     parser.close()

特定のページ専用に作ったもので、汎用には作ってない。特にページのエンコーディングは、もしあなたがこれを再利用したいと願う場合は、書き換えないといかんだろう。

こうやってブログるメモな行為に価値を(無理やり)見出すとするならば、これは「HTMLParserの例」としてのそれではなくて six の使い方だったりする。実は「Python 2 と 3 両方で動くものを作る」ので「頻出かつ面倒」なワースト3に入るのだわね。個人的にワーストは subprocess 周りで、二番目がこの「six.moves」対象になってるものたち。six なしには悶絶可能指数∞、ついでに「覚えられるかーっっ」と叫びたくなること必至。だからこうして一つ実例が見える形で残ってると、後日たぶん助かる。


ところで、ずっと言ってきた通り、ワタシはまだ個人的な事情で Python 2.7 も見捨てないんだけれど、ここで言ったように Python 2.x をちゃんと嫌いになってくれたかな? お願いだから「Python 2.7 を推奨している」とだけは考えないでね。