HTML のドキュメントを Sphinx に移行する

CodeIgniter の HTML で書かれたユーザガイドを Sphinx の RST へ変換したときの作業記録です。

最終的なソースコードは、以下に置いてあります。

html2rest を用意する

BeautifulSoup が必要なのでインストールします。

$ sudo apt-get install python-beautifulsoup

html2rest.py を bitbucket から取得します。

$ hg clone https://bitbucket.org/djerdo/musette

musette/musette/html/html2rest.py にあります。

textwrap でマルチバイト文字列を fold するため、textwrap でマルチバイト文字列を fold する - daily dayflower にある mbtextwrap.py を html2rest.py と同じフォルダに置きます。Gist にコピーを置いておきました。

MBTextWrapper を使うように、リダイレクトしてもエラーにならないように、出力フォーマットの調整のために html2rest.py を変更します。

diff -r ec24787e957b musette/html/html2rest.py
--- a/musette/html/html2rest.py	Mon Feb 01 16:54:15 2010 +0000
+++ b/musette/html/html2rest.py	Thu Oct 06 22:27:14 2011 +0900
@@ -27,12 +27,12 @@
 import codecs
 from sgmllib import SGMLParser
 from StringIO import StringIO
-from textwrap import TextWrapper
+from mbtextwrap import MBTextWrapper
 
 CODEBLOCK = '.. sourcecode:: python'
 BLOCKTAGS = ['div', 'blockquote']
 IGNORETAGS = ['title', 'style', 'script']
-UNDERLINES = list('=-~`+;')
+UNDERLINES = list('#=~`+;')
 
 # Fredrik Lundh, http://effbot.org/zone/re-sub.html
 def unescape(text):
@@ -61,7 +61,7 @@
 
     def __init__(self):
         self._lines = []
-        self._wrapper = TextWrapper()
+        self._wrapper = MBTextWrapper(encoding='utf-8')
 
     def __len__(self):
         return len(self._lines)
@@ -122,7 +122,7 @@
             if self.inblock > 1:
                 indent = 4 * (self.inblock - 1)
                 self.linebuffer.indent(indent)
-            self.writer.write(unescape(self.linebuffer.read()))
+            self.writer.write(unescape(self.linebuffer.read()).encode(sys.getfilesystemencoding()))
             self.linebuffer.clear()
 
     def flush_stringbuffer(self):
@@ -218,7 +218,7 @@
 
     def end_a(self):
         if self.link:
-            self.data(' <%s>`__' % self.link)
+            self.data(' <%s>`' % self.link)
             self.link = None
 
     def start_pre(self, attrs):
@@ -245,7 +245,7 @@
             self.writeline()
         else:
             self.writeline()
-        self.lists.append('+ ')
+        self.lists.append('- ')
         self.inblock += 1
 
     def end_ul(self):
@@ -299,7 +299,7 @@
     def end_li(self):
         self.flush_stringbuffer()
         linebuf = self.linebuffer
-        if linebuf and linebuf[0] and linebuf[0].lstrip()[:2] in ['+ ', '#.']:
+        if linebuf and linebuf[0] and linebuf[0].lstrip()[:2] in ['- ', '#.']:
             start=1
         else:
             # the start of the <li> has already been written, perhaps because

これで、以下のようにすると RSTファイルを生成できます。

$ python html2rest.py HTMLファイル > RSTファイル

変換処理

本家リポジトリの最新の user_guide_src を user_guide_ja_src として ci-ja リポジトリにコピーします。

作成が必要なファイルのリストを生成します。

$ cd user_guide_ja_src/source
$ find . -name '*.rst' > FILES

変換処理の shell script を作成します。

convert.sh

for i in `cat FILES`; do
  file=`echo $i | sed -e "s/\.rst$//"`
  file="../../user_guide_ja/$file.html"
  if [ ! -f $file ]; then
    continue
  fi

  ls $file
  python $HOME/html2rest.py $file > $i
  php filter.php $i
done

cat << EOD >> index.rst 
.. toctree::
	:glob:
	:titlesonly:
	:hidden:
	
	*
	overview/index
	installation/index
	general/index
	libraries/index
	database/index
	helpers/index
	documentation/index
EOD

後処理の filter.php を作成します。

filter.php

<?php

$file = $argv[1];

if (file_exists($file)) {
  $lines = file($file);
}
else {
  exit("No such file: $file\n"); 
}

$remove_before = 'ユーザガイドを検索'; 
$remove_after = '前のトピック|ページの先頭';

$new_doc = '';
$state = 'head';

foreach ($lines as $line) {
  if ($state === 'head') {
    if (preg_match('/' . $remove_before . '/', $line, $matches)) {
      //var_dump($matches);
      $state = 'after_head';
    }
  }
  else if ($state === 'after_head') {
    if ($line != "\n") {
      $state = 'content';
      $new_doc .= $line;
    }
  }
  else {
    if (preg_match('/' . $remove_after . '/', $line, $matches)) {
      break;
    }
    else {
      $new_doc .= $line;
    }
  }
}

file_put_contents($file, $new_doc);

これで準備完了しました。

$ sh convret.sh

で変換処理が走ります。