孤独にそっくり

開いている窓の前で立ち止まるな

pythonで読書メーターの中身をスクレイピングして題名を抜き出す

こんにちは。
今回は久々にプログラミングでもします。
前から読書メーターの中身をスクレイピングして、世界文学ランキングと自動的に比べてくれるアプリ欲しいな~と思っていたので、少しずつ作ることにしました。
そのうちWebアプリにしたいのでPython使います。
というか、Pythonで何か作りたいな~からはじまってるのでホントは順序が逆です。


Anacondaを使うとインストールがとっても簡単でよかったです。
Pythonのバージョンは3.5。
ついでにエディターにAtom導入しました。

今回の目標:ダウンロードしたHTMLファイルをスクレイピングして本の題名を抜き出す

これだけです。できそうなところからってことで。
ひとまず、HTMLファイルを読み込んで、HTMLParserを使ってみる。
ライブラリチュートリアルまんまで

# coding: UTF-8
from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Start tag:", tag)
        for attr in attrs:
            print("     attr:", attr)
    def handle_data(self, data):
        print("Data     :", data)
if __name__ == '__main__':
    f = open("booklist.html","r",encoding="UTF-8")
    #print(f.read())
parser = MyHTMLParser()
parser.feed(f.read())

下の場合

<div class="book_box_book_image">
<a href="http://bookmeter.com/b/4102001131"><img src="./読書メーター - あなたの読書量をグラフで管理_files/51FYGZQ04PL._SL150_.jpg" alt="荒野のおおかみ (新潮文庫)"></a>
</div>
<div class="book_box_book_title">
<a href="http://bookmeter.com/b/4102001131">荒野のおおかみ</a>
</div>
<div class="book_box_book_author"><a href="http://bookmeter.com/s?q=%E3%83%98%E3%83%83%E3%82%BB">ヘッセ</a></div>
<span class="readers"><a href="http://bookmeter.com/b/4102001131">933登録</a></span>

次のようになる

Start tag: div
     attr: ('class', 'book_box_book_image')
Data     : 
					
Start tag: a
     attr: ('href', 'http://bookmeter.com/b/4102001131')
Start tag: img
     attr: ('src', './読書メーター - あなたの読書量をグラフで管理_files/51FYGZQ04PL._SL150_.jpg')
     attr: ('alt', '荒野のおおかみ (新潮文庫)')
End tag  : a
Data     : 
					
End tag  : div
Data     : 
					
Start tag: div
     attr: ('class', 'book_box_book_title')
Data     : 
						
Start tag: a
     attr: ('href', 'http://bookmeter.com/b/4102001131')
Data     : 荒野のおおかみ
End tag  : a
Data     : 
					
End tag  : div
Data     : 
					
Start tag: div
     attr: ('class', 'book_box_book_author')
Start tag: a
     attr: ('href', 'http://bookmeter.com/s?q=%E3%83%98%E3%83%83%E3%82%BB')
Data     : ヘッセ
End tag  : a
End tag  : div
Data     : 
					
Start tag: span
     attr: ('class', 'readers')
Start tag: a
     attr: ('href', 'http://bookmeter.com/b/4102001131')
Data     : 933登録
End tag  : a
End tag  : span

このdivタグのclassを指定して、中にあるaタグの中身を抜き出したい。

PythonでHTMLをパースする:HTMLParser | UX MILKさんと
HTMLParserを使ってページのタイトルを取得 - おし、プログラミングさん
を読んで、フラグを使ったり、attrsがキーで取り出せることがわかったので、それを真似て書いてみる。それから'\n\t\t\t\t\t\t'の問題を修正して最終版は下の通り

# coding: UTF-8
from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.flag = False
        self.title = []
    def handle_starttag(self, tag, attrs):
        if tag == "div":
            attrs = dict(attrs) # タプルを辞書に変換する
            if 'class' in attrs:
                if attrs['class'] == 'book_box_book_title':
                    self.flag = True
    def handle_data(self, data):
        if self.flag:
            if data != "\n\t\t\t\t\t\t":
                self.title.append(data)
                print("Data     :", data)
    def handle_endtag(self, tag):
        self.flag = False

if __name__ == '__main__':
    f = open("booklist2.html","r",encoding="UTF-8")
    #print(f.read())
    parser = MyHTMLParser()
    parser.feed(f.read())
    print(parser.title)

'\n\t\t\t\t\t\t'の問題

Data     :				
Data     : 青い脂
Data     : 			
Data     : 勝手に生きろ!
Data     : 		
Data     : 新編 不穏の書、断章
Data     : 			
Data     : パルムの僧院〈下〉
Data     : 			
Data     : くそったれ!少年時代
Data     : 			
Data     : 幸福について―人生論
Data     : 			
Data     : パルムの僧院 (上)
Data     : 			
Data     : 夜と霧 新版
Data     : 			
Data     : アメリカ文学のレッスン
Data     :					
Data     : ペドロ・パラモ
Data     :				
Data     : パルプ
Data     : 			
Data     : ドン・キホーテ〈後篇3〉
Data     : 			
Data     : ゴドーを待ちながら
Data     :			
Data     : ドン・キホーテ〈後篇2〉
Data     :			
Data     : ジェイルバード 630巻
Data     : 				
Data     : コレラの時代の愛
Data     : 				
Data     : 荒野のおおかみ
Data     : 				
Data     : ドン・キホーテ〈後篇1〉
Data     : 			
Data     : 仮面の告白
Data     : 			
Data     : 笑いと忘却の書
['\n\t\t\t\t\t\t', '青い脂', '\n\t\t\t\t\t\t', '勝手に生きろ!', '\n\t\t\t\t\t\t', '新編 不穏の書、断章', '\n\t\t\t\t\t\t', 'パルムの僧院〈下〉', '\n\t\t\t\t\t\t', 'くそったれ!少年時代', '\n\t\t\t\t\t\t', '幸福について―人生論', '\n\t\t\t\t\t\t', 'パルムの僧院 (上)', '\n\t\t\t\t\t\t', '夜と霧 新版', '\n\t\t\t\t\t\t', 'アメリカ文学のレッスン', '\n\t\t\t\t\t\t', 'ペドロ・パラモ', '\n\t\t\t\t\t\t', 'パルプ', '\n\t\t\t\t\t\t', 'ドン・キホーテ〈後篇3〉', '\n\t\t\t\t\t\t', 'ゴドーを待ちながら', '\n\t\t\t\t\t\t', 'ドン・キホーテ〈後篇2〉', '\n\t\t\t\t\t\t', 'ジェイルバード 630巻', '\n\t\t\t\t\t\t', 'コレラの時代の愛', '\n\t\t\t\t\t\t', '荒野のおおかみ', '\n\t\t\t\t\t\t', 'ドン・キホーテ〈後篇1〉', '\n\t\t\t\t\t\t', '仮面の告白', '\n\t\t\t\t\t\t', '笑いと忘却の書']

空白の謎Dataがとても邪魔だけど何が出力されているのかよくわからない
divタグの中のaタグの中身を出してるから、きっとdivタグのDataが出てるのかな。
そもそも本のタイトルと作者名を別にして扱えないからめんどくさい……。
フラグで条件分岐みたいなことしたいけどできないっぽいしよくわからない。
などと考えて、仕方ないから値を取り出してみたら'\n\t\t\t\t\t\t'だった。対症療法で消したら一応きれいになった。本当は正規表現特殊文字をはじくようにしたい。

Data     : 青い脂
Data     : 勝手に生きろ!
Data     : 新編 不穏の書、断章
Data     : パルムの僧院〈下〉
Data     : くそったれ!少年時代
Data     : 幸福について―人生論
Data     : パルムの僧院 (上)
Data     : 夜と霧 新版
Data     : アメリカ文学のレッスン
Data     : ペドロ・パラモ
Data     : パルプ
Data     : ドン・キホーテ〈後篇3〉
Data     : ゴドーを待ちながら
Data     : ドン・キホーテ〈後篇2〉
Data     : ジェイルバード 630巻
Data     : コレラの時代の愛
Data     : 荒野のおおかみ
Data     : ドン・キホーテ〈後篇1〉
Data     : 仮面の告白
Data     : 笑いと忘却の書
['青い脂', '勝手に生きろ!', '新編 不穏の書、断章', 'パルムの僧院〈下〉', 'くそったれ!少年時代', '幸福について―人生論', 'パルムの僧院 (上)', '夜と霧 新版', 'アメリカ文学のレッスン', 'ペドロ・パラモ', 'パルプ', 'ドン・キホーテ〈後篇3〉', 'ゴドーを待ちながら', 'ドン・キホーテ〈後篇2〉', 'ジェイルバード 630巻', 'コレラの時代の愛', '荒野のおおかみ', 'ドン・キホーテ〈後篇1〉', '仮面の告白', '笑いと忘却の書']

まとめ

今回はここまでで。
たったこれだけのことをするまでにも、そもそもAtom-RunnerでPython実行したら日本語が文字化けしまくって相当消耗したり(これ試したけどすぐには文字化けが治らなくて、何度か再起動したら治った。今でもよくわからない)、ファイルの開き方とかわからないし、ライブラリは情報が多すぎて読む気にならなかったり、意外と時間がかかってます。
あとPython3とPython2系との違い(例えばprintにかっこつけないといけない)とかに気が付くまで時間がかかったりもしました。アプリを作りながら、Dive Into Python 3あたりを読んでちょっと勉強しようかな、やる気が起きたら。
もっとすらすら思った通りにコードがかければいいのに、そのためには勉強して苦しまないといけないというジレンマ。プログラミングも大変だなあと思いました。

追記
読書メータースクレイピングしている人たくさんいたけど事前に調べてなかったから全然気が付いてなかったけどまあいいか。
ユーザの番号と読んだ本リストの数を打ち込めばWebからとってきてくれるようになった。

# coding: UTF-8
import urllib.request
import re
from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.flag = False
        self.title = []
        self.max = 0
    def handle_starttag(self, tag, attrs):
        if tag == "div":
            attrs = dict(attrs) # タプルを辞書に変換する
            if 'class' in attrs:
                if attrs['class'] == 'book_box_book_title':
                    self.flag = True
    def handle_data(self, data):
        p = re.compile('\s') #正規表現的なやつ
        if self.flag:
            if p.match(data)==None:
                self.title.append(data)
                print("Data     :", data)
    def handle_endtag(self, tag):
        self.flag = False

if __name__ == '__main__':
    parser = MyHTMLParser()
    number = (あなたのユーザ番号) #ユーザ番号
    n= (読んだ本リストの最大値)#読んだ本のページ数
    for i in range(n):
        str ='http://bookmeter.com/u/%d/booklist&p=%d' % (number,i)
        with urllib.request.urlopen(str) as res:
            html = res.read().decode("utf-8")
        parser.feed(html)
    print(parser.title)

メールアドレスとパスワードでログインするとか、自分のそういうことしたいけど何すればいいかイマイチわからない…
うーん