
taken by caribb under CC BY-NC-ND
動物本と言えばオライリー。オライリーと言えば動物本。良質な洋書を翻訳した本が多く(書き下ろしもあります)、品質は折り紙付きな出版社です。オライリーの本に接する人は大きく二つに分かれると思います:
- 本を読む人
- 洋書を翻訳する人
このブログで紹介するスクリプトは、後者の人で、尚かつ翻訳にSphinxを使うよ!というレアな人向けです。適当に作ってみたのですが、便利そうなので公開します。テストは手元にあったThe Art of Community
とErlang Programming
の2冊で行っています。どっちも良い本です。
Sphinxを使ってオライリー本の翻訳をされる方はご利用ください。
使い方
使う時はまず、xdoc2txtを使って、PDFからテキストを抽出します。その後は次のスクリプトに解析させると、章ごとにchapter_01.rstみたいなファイルが作られます。1章の前はpreface.rst。あとはそれらをまとめるindex.rstもおまけで作られます。翻訳用に特化しているので、原文は既にコメントアウト済みで出力されます。なお、出力したテキストファイルは、input.txtにリネームして、スクリプトと同じフォルダに置いてください。手抜きなのでご勘弁を。
今後直すとしたら
一番やりたいのは、セクションタイトルをきちんと出力する部分ですね。今は階層情報がすっかり抜け落ちてしまうため、全部フラットになってしまいます。だれか、目次情報(階層付き)の抽出の仕方が分かる方、情報お願いします。タイトルが分かっていたら、今は無理矢理やっている、タイトル抽出部とかをシンプルにできるだろうしー。別ファイルで、セクションタイトルの階層構造をインデントで表現したファイルを作っておいて、それを読み込むとかでもいいかも。
スクリプトのポイント
まぁ、コメントも書いてないし、テストも書いてないぐらいの適当スクリプト(でもそれでも動いてしまうPythonの恐ろしさよ)ですが、工夫したのは@handleデコレータを作って、イベント駆動型にしたことですね。@handleを増やせば勝手に色々なフォーマットを追加できるだろうし。段落の先頭の行の抽出とか、タイトルの抽出は適当に行っているので、精度はいまいちかも。上記の二つの本を流した感じではうまくいっているようには見えましたけど。
import re
current_doc = file("preface.rst", "w")
basenames = ["preface"]
patterns = []
is_first = False
stop_words = set(
("a,about,am,among,an,and,any,are,as,at,be,by,else,"
"ever,every,for,from,in,into,is,just,let,no,nor,not,of,"
"off,on,only,or,own,rather,since,so,some,than,the,"
"then,tis,to,too,twas,was,were,while,will,with,yet".split(",")))
def handle(pattern):
def _register(func):
if callable(pattern):
patterns.append((pattern, func))
else:
pattern_obj = re.compile(pattern)
patterns.append((pattern_obj, func))
return func
return _register
def filename(number):
replace = {"one":1, "two":2, "three":3, "four":4, "five":5,
"six":6, "seven":7, "eight":8, "nine":9, "ten":10,
"eleven":11, "twelve":12, "thirteen":13, "fourteen":14}
if number in replace:
basename = "chapter_%02d" % replace[number]
else:
try:
number_num = int(number)
basename = "chapter_%02d" % number_num
except ValueError:
basename = "chapter_" + number.lower()
basenames.append(basename)
return basename + ".rst"
@handle("CHAPTER (.*)")
def chapter_title(line, match):
global current_doc
global is_first
current_doc = file(filename(match.group(1).lower()), "w")
is_first = True
@handle(r"--\d+/\d+--")
def skip_pagenumber(line, match):
pass
@handle(r"[A-Z][A-Z ]+?[A-Z]\s+?(\d+)\n\Z")
def pagenumber1(line, match):
current_doc.write("\n.. [ page %s ]\n\n" % match.group(1))
@handle(r"(\d+)\s+?[A-Z][A-Z ]+?[A-Z]\n\Z")
def pagenumber2(line, match):
current_doc.write("\n.. [ page %s ]\n\n" % match.group(1))
@handle(r"[A-Z][A-Za-z ]+\s+?\|\s+?(\d+)\n\Z")
def pagenumber3(line, match):
current_doc.write("\n.. [ page %s ]\n\n" % match.group(1))
@handle(r"(\d+)\s+?\|\s+?Chapter")
def pagenumber4(line, match):
current_doc.write("\n.. [ page %s ]\n\n" % match.group(1))
def is_title(line):
line = line.strip()
if not line:
return
if line[0].islower() and line.endswith("."):
return
words = 0
match = 0
for word in line.split():
if word.lower() in stop_words:
continue
words += 1
if word[0].istitle():
match += 1
if words <= 3:
return match == words
else:
return match >= words - 2
@handle(is_title)
def write_title(line, result):
global is_first
bar = "=" * (len(line)-1)
if is_first:
current_doc.write("\n.. %s\n %s %s\n" % (bar, line, bar))
is_first = False
else:
current_doc.write("\n.. %s %s\n" % (line, bar))
@handle("[A-Z]")
def start_paragraph(line, match):
current_doc.write("\n.. %s" % line)
@handle(r"\? ")
def bullet_list(line, match):
current_doc.write("\n.. * %s" % line.split("? ")[1])
def default_func(line):
current_doc.write(" %s" % line.lstrip())
def main():
for line in file("input.txt"):
for pattern, func in patterns:
if callable(pattern):
result = pattern(line)
if result:
func(line, result)
break
else:
match = pattern.match(line)
if match:
func(line, match)
break
else:
default_func(line)
index = file("index.rst", "w")
index.write("""Title
=====
.. toctree::
:maxdepth: 2
:numbered:
%s
""" % "\n ".join(basenames))
if __name__ == "__main__":
main()
モンクレール http://www.moncler.ne.jp
モンクレール http://www.monclersales.jp
モンクレール http://www.moncler007.com
モンクレール http://www.monclershop.jp
抽象化時計クラスの小道具は、協定で面白いメディアのできます。調査結果を認めるその時計ブランドを受け入れる豊かさのブランドのより多くの面白いメディア プラットフォームに出席を追加します。
精力剤:http://www.bestkanpou.com/Energy/Energy.html
中絶薬:http://www.bestkanpou.com/product/121.html
威哥王:http://www.bestkanpou.com/product/437.html
三便宝:http://www.bestkanpou.com/product/349.html
Cialis:http://www.bestkanpou.com/product/24.html
levitra:http://www.bestkanpou.com/product/21.html
FLY D5:http://www.bestkanpou.com/product/231.html
女性用ウィッグ:http://www.besttojapan.com/_c135
耐熱ウィッグ:http://www.besttojapan.com/_c136
ロング ウィッグ:http://www.besttojapan.com/_c320
媚薬:http://www.akanpo.com/catalog/60.html
催淫カプセル:http://www.akanpo.com/product/339.html
催淫:http://www.akanpo.com/product/443.html
女性用媚薬:http://www.akanpo.com/catalog/60.html
moncler:http://www.moncleronline.jp
モンクレール 通販:http://www.moncleronline.jp
モンクレール 2012:http://www.moncleronline.jp/mokureru-8-b0.html
http://www.vikishoes.com
トリーバーチ 靴
http://www.vikishoes.com/5--
トリーバーチ シューズ
http://www.vikishoes.com/8--04-boots
トリーバーチ ブーツ
http://www.vikishoes.com/6-wedges
トリーバーチウェッジヒールで
http://www.vikishoes.com/7-heels
トリーバーチハイヒール
http://www.vikishoes.com/9--
トリーバーチバッグ
http://www.vikishoes.com/10--boots
UGG ブーツ
バーバリー バッグ http://www.burberrymore.com/6--bags
バーバリーベルト http://www.burberrymore.com/7--
バーバリーコート http://www.burberrymore.com