正式に管理する気がない一時作業をブログにメモするシリーズ、的な。
ちょいと「htmlページのリンク切れチェック」を手早くやる必要に迫られたのだが、出来合いのデカいちゃんとしたものはいらんし、てことで、まさに「手早く」見繕った。なんだかんだ面倒ではあるのよね、毎度一時間はかからん作業なのだとはいえ。
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 を推奨している」とだけは考えないでね。