Pukiwiki を html 化する修行

Wiki を html に落としたい。バックアップ目的だったり、Wiki をオーサリングツールとして使ったり、とそんな要求もあるだろう。とにかく、そんなことがしたかったので、Python で書いてみた。

  • Wiki エンジンは PukiWiki 1.4.7
  • サイトには BASIC 認証をかけてある
  • リンクはおおむね張れていればいいこととする
    • 単語検索結果のページとか、静的に作っておくのが難しいリンクは張れていなくてもあきらめる
  • 添付ファイルはあきらめる
# wiki2html.py for python3                                                      

import urllib.request
import hashlib
import codecs
import re
import sys
import functools

username = 'user'
password = 'secret'
base_url = 'http://pukiwiki.example.com/'
charset = 'EUC-JP'
realm = 'PukiWiki Area'
base_dir = '/home/takeyuki/pukiwiki/'

pattern = r'<a' +\
          r'(?P<pre>  (?: \s+ \w+="[^"]+")* )' +\
          r'\s+ href="' + base_url + r'\?(?P<query>[^"]+)"' +\
          r'(?P<post> (?: \s+ \w+="[^"]+")* \s*)' +\
          r'>'
ro = re.compile(pattern, re.VERBOSE)

auth_handler = urllib.request.HTTPBasicAuthHandler()
auth_handler.add_password(realm, base_url, username, password)
opener = urllib.request.build_opener(auth_handler)
urllib.request.install_opener(opener)

ro2 = re.compile(r'cmd=read&amp;page=')

def get_file_name(query):
    table = { 'Top' : 'index.html',
              'cmd=list' : 'list.html' }
    query = ro2.sub('', query)
    return table[query] if query in table \
           else hashlib.sha1(query.encode()).hexdigest() + '.html'

def convert(listup, match):
   query = match.group('query')
   path = get_file_name(query)
   if listup:
       if not query in pages:
           pages[query] = Page(query)
   return '<a{} href="{}"{}>'.format(match.group('pre'),
                                     path, match.group('post'))

class Page:
    def __init__(self, query):
        self.query = query

    def download(self):
        file = get_file_name(self.query)
        sys.stderr.write("{} -> {}\n".format(self.query, file))
        url = base_url + '?' + self.query
        res = urllib.request.urlopen(url)
        content = ''
        for line in res.readlines():
            try:
                content = content + line.decode(charset)
            except UnicodeDecodeError as e:
                pass
        res.close()
        func = functools.partial(convert, self.query == 'cmd=list')
        str = ro.sub(func, content)
        f = codecs.open(base_dir + file, 'w', 'EUC-JP')
        f.write(str)
        f.close()

list_page = Page('cmd=list')
pages = {}
list_page.download()
for page in pages.values():
    page.download()

いろいろと苦心している様が見て取れるコード。美しくない。

メモ。

  • Query String を sha1 で 16 進表記したファイル名にしている
    • 「?cmd=read&page=X」と「?X」は同じ結果になるっぽいので一応対処している
  • 最初に「?cmd=list」で得られるページ一覧を取得する
    • HTML 内のリンクを書き換えつつ、辞書に保管しておく
  • 辞書に保管された Query String でページを取得していく
    • このときは HTML 内のリンクを書き換えても辞書を更新しない
  • デコードエラーのときはそのバイト列を破棄する
    • この辺、投げやり