（メモ）とあるWebページから欲しい部分だけを抜き出してSphinxページに直し、それをPDFにして印刷するまで

わけあって[このページ](http://kaigoouen.net/program/gymnastics/gymnastics_1.html)にある健歩体操というものを資料化したかった。
お試しで**Sphinx**の勉強をしていたのでこれを使う。無理やり使っている感もあるので実はものすごく効率が悪かったかもしれないけれど、割とよく出来たからまあ良し。

###### 成果物（健歩体操）

* ホップ：　http://www.slideshare.net/junishikawa528/ss-45819097
* ステップ：　http://www.slideshare.net/junishikawa528/ss-45819186
* ジャンプ：　http://www.slideshare.net/junishikawa528/ss-45819235

空白ページとかが途中で混じっているけれどもまあ良し。

#### 使ったもの

###### Sphinx

http://sphinx-users.jp/

###### BeautifulSoup

http://tdoc.info/beautifulsoup/

**HTMLParser** というものがあるみたいだが使い方がよく分からず、個人的に分かりやすかったのでこれを使った。

```
pip install BeautifulSoup
```

あとはこれを使って問題のページのHTMLから解説画像と文章を抜き出し、Sphinxで資料の形に直していく。
詳しくは後で。

# Sphinxページに直す

### だいたいの流れを考えよう

まず体操のページのURLは、

```
http://kaigoouen.net/program/gymnastics/gymnastics_{体操の種類の番号}_{ページ番号}.html
```

という感じで、数字の入る箇所が二つある（どうでもいいけど、このサイトはまさかページごとにHTMLを作ってるのだろうか？）。

###### 体操の種類の番号

健歩体操にはホップ、ステップ、ジャンプと三種類のレベルがあるのだが、**ホップなら２、ステップなら３、ジャンプなら４が入る**らしい（ちなみに１には前説みたいなもののようなので今回は不要）。


###### ページ番号

ホップ、ステップ、ジャンプいずれの体操も複数のページから成っているのでここにはその**ページ数が入る**。

これらを踏まえて考えたら大体こんな感じの流れか？

1. 体操の種類を選択
2. 選んだ体操からページを選択
3. 選んだページのHTMLから欲しいところを抜き出す
4. 次のページに進んで2に戻る（最終ページになるまで繰り返す）

作る資料はホップ、ステップ、ジャンプでそれぞれ個別に作るつもりなのでだいたいこれでOK。


### 細かい流れを考えよう

上で考えた流れでいうと、３の部分を考える。

どうやら自分が欲しい部分は**box02というクラスのdiv要素**内にある模様。

例えばホップならこんな感じ。

````html
<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スクリプトを書く。[ドキュメント](http://sphinx-users.jp/)を見ながら頑張るのみ。

ひとまず**項目名は見出し**にし、**解説文と画像は横に並べたかったのでテーブル方式**とした。

#### 少し困ったこと

##### 元のページの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ファイルを出力する際に頻出。

```py
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.

で、[ここ](http://qiita.com/mitzi2funk/items/0a9e84ee1cf4b241524b)を参考に

```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にしたいドキュメントを

```py
('hop','step','jump'),
```

みたいにして連ねるのだと思っていたがそれは違っていて、

```py
('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_5                                          
>
>Complete!

できたようなので参考にしたページに従い、

```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
<line>\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> line
done
build succeeded.

よし、上手くいっていないけれども**画像を読み込もうとしてる場所が明らかに変**ですね。
しかし直し方はよく分からない。外部から画像を取ってくることは想定していないのか？

エラーメッセージ的に調べるのが難しかったので色々と実験してみたところ、どうやら**.rst内で[replace](http://sphinx-users.jp/reverse-dict/table/replace-table.html)を使わなければ大丈夫**なようだった。
一種のバグなのだろうか？なんにせよ使わない方向で解決。

次、文字化けしている。

怪しいのは、

>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'てあるけど、そんなフォント知らないし何これ？

http://www.fontsquirrel.com/fonts/dejavu-sans-mono

あ、どうみても英語用のフォントだ。ということはスタイルシートを読み込めてない？
**conf.py**は間違っていなさそうだったので、スタイルシートとしている**ja.json**を調べてみる。ドキュメント通りに書いたと思うのだが。
で、数時間くらい悩んだ結果が、一ヵ所だけクォーテーションを閉じていなかったという凡ミス。あとシングルでは駄目で、ダブルじゃないといけないらしいこともついでに分かった。

何にせよそれでようやく設定した日本語フォントを探してくれるようになった・・・のだが、**見つからない**。
というかそもそも**fc-listコマンドで何も返ってこない**状態だった。

まあ、フォントファイルを/usr/share/fonts以下に移動したら無事解決し文字化けも直ったのだが、そうなると**conf.pyのpdf_font_path**の意味が分からない。現状は中身を空にしているが問題なく日本語表示できているし、試しに他の場所のパスを書いても反映されていない感じ。他の場所に置きたいときはどうすればいいのだろうか？（**Fontconfig**の設定をどこかで書き換える？）

# こんな感じのPythonスクリプトになった

```py:hopstepjump.py
#! /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
```

一行のコマンドで１０００行を超えるファイルが書き上がるのはなかなかの快感だ。
ともあれこれで構成を作ったらあとは```make html```なり```make pdf```をするだけ。

#### GitHub

https://github.com/juniskw/hopstepjump/tree/no_replace_listtable

ところで知らない外国人の人がauthoredになっているのだが、これってひょっとして何かイタズラされてる？