わけあってこのページにある健歩体操というものを資料化したかった。
お試しでSphinxの勉強をしていたのでこれを使う。無理やり使っている感もあるので実はものすごく効率が悪かったかもしれないけれど、割とよく出来たからまあ良し。
成果物(健歩体操)
- ホップ: http://www.slideshare.net/junishikawa528/ss-45819097
- ステップ: http://www.slideshare.net/junishikawa528/ss-45819186
- ジャンプ: http://www.slideshare.net/junishikawa528/ss-45819235
空白ページとかが途中で混じっているけれどもまあ良し。
使ったもの
Sphinx
BeautifulSoup
HTMLParser というものがあるみたいだが使い方がよく分からず、個人的に分かりやすかったのでこれを使った。
pip install BeautifulSoup
あとはこれを使って問題のページのHTMLから解説画像と文章を抜き出し、Sphinxで資料の形に直していく。
詳しくは後で。
Sphinxページに直す
だいたいの流れを考えよう
まず体操のページのURLは、
http://kaigoouen.net/program/gymnastics/gymnastics_{体操の種類の番号}_{ページ番号}.html
という感じで、数字の入る箇所が二つある(どうでもいいけど、このサイトはまさかページごとにHTMLを作ってるのだろうか?)。
体操の種類の番号
健歩体操にはホップ、ステップ、ジャンプと三種類のレベルがあるのだが、ホップなら2、ステップなら3、ジャンプなら4が入るらしい(ちなみに1には前説みたいなもののようなので今回は不要)。
ページ番号
ホップ、ステップ、ジャンプいずれの体操も複数のページから成っているのでここにはそのページ数が入る。
これらを踏まえて考えたら大体こんな感じの流れか?
- 体操の種類を選択
- 選んだ体操からページを選択
- 選んだページのHTMLから欲しいところを抜き出す
- 次のページに進んで2に戻る(最終ページになるまで繰り返す)
作る資料はホップ、ステップ、ジャンプでそれぞれ個別に作るつもりなのでだいたいこれでOK。
細かい流れを考えよう
上で考えた流れでいうと、3の部分を考える。
どうやら自分が欲しい部分はbox02というクラスのdiv要素内にある模様。
例えばホップならこんな感じ。
<div class="box02">
<h2>
<img alt="項目名" src="text.gif"></img>
</h2>
<div class="box02_txt_hop">
<div class="hop_box">
<div class="hop_txt">解説</div>
<div class="hop_image">
<img alt="参考写真" src="image.jpg"></img>
</div>
</div>
<div class="hop_box">
<!--項目の続き-->
</div>
</div>
<div class="box02">
<!--次の項目-->
</div>
で、この中で欲しいのは・・・
- 項目名
- 解説
- 参考写真のsrc部分
以上。BeautifulSoupのモジュールを使ってこれらを絞り込む。要素名とかクラス名でfindやらfindAllできるのでそれほど難しくなかった。
あとは絞り込んだものをSphinx用の.rst形式に書き換えるPythonスクリプトを書く。ドキュメントを見ながら頑張るのみ。
ひとまず項目名は見出しにし、解説文と画像は横に並べたかったのでテーブル方式とした。
少し困ったこと
元のページのHTMLが変だった
各項目の解説文と画像を抜き出す際に最初は該当箇所をクラス名で上位から辿ってみようと考えた。で、Firefoxの要素を検証で調べてみたところ、どうやらbox02>box02_txt_hop>hop_box>hop_imageまたはhop_txtという感じで掘り下げていけばよさそうだったのでそれでやっていたのだが、そうするとどうにも各項目ともに最後の解説が抜け落ちてしまう。
それでよく見てみたところ、どうやらなぜか各項目の最後になる解説だけは文章、画像ともにhop_boxクラスに含まれておらず、上位のbox02_txt_hopの直下となっているようだった(理由は不明)。なので、hop_boxは最初から無視する方向に決定。そもそもBeautifulSoupのfindやfindAllは対象とした要素以下全てから条件に沿って探してくれるっぽいので、わざわざ一階層ずつ探っていく必要はなかったみたいだ。
テーブルが自動で縞模様になる
中身がテキストだけならそのほうが綺麗なのだろうが画像込みだと個人的には見づらくて気に入らなかった。しかしスタイルの変え方(HTMLはともかくPDFのほう)がよく分からなかったのでいちいち新しいテーブルを作る形式にして対処した。
UnicodeDecodeErrorとUnicodeEncodeError
Pythonスクリプトから.rstファイルを出力する際に頻出。
type(text)
とかで調べながら地道に直していく。
PDFにして印刷するまで
rst2pdfとlatexという二通りのやり方があるようだが、前者のほうが簡単そうに見えたので今回はrst2pdfを使うことにした。
そして、次々とハマったのだ(pipでインストールしたのがいけなかったのかも)。
make pdf
でエラーになる
ImportError: reportlab requires Python 2.7+ or 3.3+; 3.0-3.2 are not supported.
で、ここを参考に
pip install -U reportlab==2.5
したらエラーが変化。
[ERROR] pdfbuilder.py:130 need more than 3 values to unpack
Traceback (most recent call last):
File "/home/vagrant/www/public/hopstepjump/venv/lib/python2.6/site-packages/rst2pdf/pdfbuilder.py", line 78, in write
docname, targetname, title, author = entry[:4]
ValueError: need more than 3 values to unpack
FAILED
build succeeded, 1048 warnings.
要はconf.pyの書き方が違った。最初自分はpdf_documentsにはpdfにしたいドキュメントを
('hop','step','jump'),
みたいにして連ねるのだと思っていたがそれは違っていて、
('docName', u'ファイル名', u'PDFの表紙にくるタイトル', u'著者'),
ということらしい(というか実はマニュアルに書いてあった)。このタプルはリストの中に含まれているので、複数のドキュメントをPDF化したいのであれば複数のタプルを加えればいい。
ともあれ気を取り直してまたmake pdf
IOError: decoder jpeg not available
identity=[ImageReader@0x3a3ce10 filename='/home/vagrant/www/public/hopstepjump/venv/lib/python2.6/site-packages/rst2pdf/images/image-missing.jpg']
FAILED
build succeeded.
またしても新たな問題。jpegが駄目?
調べてみたところ別にPILが悪いのではなく、libjpegというのが必要らしい。
http://kwmt27.net/index.php/2013/07/14/python-pil-error-decoder-jpeg-not-available/
これをインストールしてPILも再インストールとのこと
yumでいけるかな?
yum search libjpeg
============================= N/S Matched: libjpeg =============================
libjpeg-turbo-devel.i686 : Headers for the libjpeg-turbo library
libjpeg-turbo-devel.x86_64 : Headers for the libjpeg-turbo library
libjpeg-turbo-static.x86_64 : Static version of the libjpeg-turbo library
libjpeg-turbo.x86_64 : A MMX/SSE2 accelerated library for manipulating JPEG
: image files
libjpeg-turbo.i686 : A MMX/SSE2 accelerated library for manipulating JPEG image
: files
どれか分からないのでyum install libjpeg
してみる(勝手に最適なやつを選んでくれることを期待)。
Updated:
libjpeg-turbo.x86_64 >0:1.2.1-3.el6_5Complete!
できたようなので参考にしたページに従い、
pip install -I pillow
make pdf
IOError: decoder jpeg not available
identity=[ImageReader@0x221e150 filename='/home/vagrant/www/public/hopstepjump/venv/lib/python2.6/site-packages/rst2pdf/images/image-missing.jpg']
FAILED
build succeeded.
まさかの進展なし・・・。
yum list | grep libjpeg
してみたらちゃんとインストールはできてるんだけど、pillowとpilって同じじゃないんですかね。
ということで、
pip install pil
しかし変わらず・・・・。
これか?http://all-rounder-biz.blogspot.jp/2013/06/macioerror-decoder-jpeg-not-available.html
違う・・・・・。
じゃあこれ?http://d.hatena.ne.jp/rougeref/20130116
はい残念でした・・・・・・。
えーと、PILインストール後の表示を見ると
*** TKINTER support not available
*** JPEG support not available
--- ZLIB (PNG/ZIP) support available
*** FREETYPE2 support not available
*** LITTLECMS support not available
--------------------------------------------------------------------
となっている。確かにJPEGはnot availableのままだ。エラーメッセージは嘘をつかない。
ここか?http://dev-pao.blogspot.jp/2010/04/python-imaging-library-piljpeg.html
駄目・・・・・・。
次http://d.hatena.ne.jp/rougeref/20130116
あ、ひょっとして・・
yum install libjpeg-devel
で、PILを消して再インストール。
--- JPEG support available
develがインストールできていなかったのが原因だったらしい。
ともあれJPEG問題は解決ということでmake pdf
。
[ERROR] image.py:110 Missing image file: /home/vagrant/www/public/hopstepjump/http://kaigoouen.net/img/hop_pic_108.jpg
\u304b\u304b\u3068\u304b\u3089\u3064\u3044\u3066\u30fb\u30fb\u30fb\u3064\u307e\u5148\u3067\u3051\u308a\u51fa\u3059\u3001\u3092\u7e70\u308a\u8fd4\u3057\u306a\u304c\u3089\u3001\u6b69\u304d\u307e\u3057\u3087\u3046\u3002 line
done
build succeeded.
よし、上手くいっていないけれども画像を読み込もうとしてる場所が明らかに変ですね。
しかし直し方はよく分からない。外部から画像を取ってくることは想定していないのか?
エラーメッセージ的に調べるのが難しかったので色々と実験してみたところ、どうやら**.rst内でreplaceを使わなければ大丈夫**なようだった。
一種のバグなのだろうか?なんにせよ使わない方向で解決。
次、文字化けしている。
怪しいのは、
sh: fc-match: コマンドが見つかりません
[ERROR] findfonts.py:208 Unknown font: DejaVu Sans Mono-Bold
の部分(いっぱい出ている)。fc-matchとは何ぞや?ということでググってみると、どうやらfontconfigというライブラリで入るコマンドみたい。
ではyumで
yum search fontconfig
=========================== N/S Matched: fontconfig ============================
fontconfig.i686 : Font configuration and customization library
fontconfig.x86_64 : Font configuration and customization library
fontconfig-devel.i686 : Font configuration and customization library
fontconfig-devel.x86_64 : Font configuration and customization library
また普通のとdevelとがある。同じ轍は二度踏まない・・・ということでdevelをインストール。
そしてまたmake pdf
してみたところ、先の「コマンドが見つかりません」は解消。
ただ下の行は未解決。そもそも'DejaVu Sans Mono-Bold'てあるけど、そんなフォント知らないし何これ?
あ、どうみても英語用のフォントだ。ということはスタイルシートを読み込めてない?
conf.pyは間違っていなさそうだったので、スタイルシートとしているja.jsonを調べてみる。ドキュメント通りに書いたと思うのだが。
で、数時間くらい悩んだ結果が、一ヵ所だけクォーテーションを閉じていなかったという凡ミス。あとシングルでは駄目で、ダブルじゃないといけないらしいこともついでに分かった。
何にせよそれでようやく設定した日本語フォントを探してくれるようになった・・・のだが、見つからない。
というかそもそもfc-listコマンドで何も返ってこない状態だった。
まあ、フォントファイルを/usr/share/fonts以下に移動したら無事解決し文字化けも直ったのだが、そうなるとconf.pyのpdf_font_pathの意味が分からない。現状は中身を空にしているが問題なく日本語表示できているし、試しに他の場所のパスを書いても反映されていない感じ。他の場所に置きたいときはどうすればいいのだろうか?(Fontconfigの設定をどこかで書き換える?)
こんな感じのPythonスクリプトになった
# ! /usr/bin/env python
# *-*coding:utf-8*-*
import sys,re,urllib2
from BeautifulSoup import BeautifulSoup
#### reference page ####
base_url = 'http://kaigoouen.net'
hopstepjump = {
'hop':{
'index':2,
'last_page':25,
},
'step':{
'index':3,
'last_page':39,
},
'jump':{
'index':4,
'last_page':46,
},
}
#### for make .rst files ####
br = u'\n'
page_break = u".. raw:: pdf%s PageBreak" % (br*2)
def soup_to_sphinx(pg):
p = 1
while p <= pg['last_page']:
url = base_url + '/program/gymnastics/gymnastics_{index}_{page}.html'.format(index=pg['index'],page=p)
htmldata = urllib2.urlopen(url)
soup = BeautifulSoup( unicode(htmldata.read(),'utf-8') )
for box in soup.findAll('div',{'class':'box02'}):
lessons = box.find('div',{'class':'box02_txt_%s' % choice})
if lessons is not None:
title = box.contents[1].contents[0]['alt']
print( sphinx_head(title) )
images = lessons.findAll('div',{'class':'%s_image' % choice})
texts = lessons.findAll('div',{'class':'%s_txt' % choice})
texts = iter(texts)
for image in images:
src = base_url + image.contents[0]['src']
image = sphinx_image(src)
text = texts.next().renderContents()
text = sphinx_text(text)
print( sphinx_listtable(image,text) )
print(page_break)
htmldata.close()
p += 1
def sphinx_head(txt):
return br.join([br,txt,u"="*30+br]).encode('utf-8')
def sphinx_listtable(s_img,s_txt):
table = u".. list-table::" + br
image = u" * - %s" % s_img
text = u" - | %s" % s_txt
return br.join([table,image,text,br]).encode('utf-8')
def sphinx_image(src):
option = u":width: 150pt"
return u".. image:: %s" % src + br + u" %s" % option
def sphinx_text(txt):
text = txt.decode('utf-8').replace(u"<br />",br)
if br in text:
texts = text.splitlines()
text = reduce(lambda x,y: x + br + u" | " + y,texts)
return text
if __name__ == '__main__':
try:
choice = sys.argv[1]
page = hopstepjump[choice]
except IndexError:
print("[エラー]:オプションが必要です('hop'か'step'か'jump'のいずれか)。")
exit(" - 例: 'python %s hop'" % sys.argv[0])
except KeyError:
print("[エラー]:オプションが違います('hop'か'step'か'jump'のいずれか)。")
exit(" - 例: 'python %s hop'" % sys.argv[0])
soup_to_sphinx(page)
使い方
python hopstepjump.py hop > hop.rst
一行のコマンドで1000行を超えるファイルが書き上がるのはなかなかの快感だ。
ともあれこれで構成を作ったらあとはmake html
なりmake pdf
をするだけ。
GitHub
ところで知らない外国人の人がauthoredになっているのだが、これってひょっとして何かイタズラされてる?