なんとなくWebスクレイピング
今日の授業は、ほぼ復習でした。
「リスト」「タプル」「ディクショナリ」「セット」
このあたりの再読と確認テスト。
意外と紙に書くタイプのテストだと、
スペルミスとかありえるので、どきどき。
解答例にない書き方をして、一時はNGになったものの
実際にコーディングしたら動いたので、OKにしたり、
なんか学生みたい(笑
最後の一時間で、「関数」に突入。
pythonの関数って、引数と戻り値が自由すぎる!
どこかでJavaと比較したものを整理したいです。
どこかで、ね。
totoをスクレイピング
さて、ここからは前回の続き。
JRAに挫折し、totoのサイトをスクレイピングできないかチャレンジします。
https://store.toto-dream.com/dcs/subos/screen/pi04/spin011/PGSPIN01101InitLotResultLsttoto.form
試合結果のページを眺めながら、目標設定をします。
目標
『回数を指定し、対象のページから結果をスクレイピング。
CSVファイルに出力する』
教材っぽい目標ですね。
学習観点でいうと、学びポイントは以下。
・HTML文字列の取得
・パーサークラスを使って、対象データを抽出
・複数ページを走査
・CSVファイルにデータ出力
ソースの紹介
とりあえず出来上がったコードは以下。
※行数が表示できると見やすいですね。
近日、方法を探してみます。
## totoデータ取得 ## ## 清書版 ## import csv import urllib.request from html.parser import HTMLParser # パーサークラス class local_HTMLParser(HTMLParser): # 初期化メソッド def __init__(self): HTMLParser.__init__(self) # クラス内変数の初期化 self.table = False self.title = False self.tr = False self.table_cnt = 0 self.row = [] self.rows = [] # 開始タグ取得メソッド def handle_starttag(self, tag, attrs): # table if ( tag == "table" ) and ( len(attrs) == 5 ) and ( attrs[4][1] == "adjustment" ) : self.table_cnt += 1 if self.table_cnt == 1 : self.table = True else : self.table = False # tr if ( self.table == True ) and ( tag == "tr" ) : if ( len(attrs) == 1 ) and ( attrs[0][1] == "backgray" ) : self.tr = True else : self.tr == False # データ取得メソッド def handle_data(self, data): # タイトル取得 if self.table == True and self.title == False: # toto が実施された回だけデータ取得 if "回 toto くじ結果" in data : self.title = data[1:5] # データ取得 elif self.table == True and self.title != "" and self.tr == True : # toto は1回13試合を予想 ROW_NUM = 13 # カラム数 COLMUN_NUM = 7 # データを格納 dat = data.strip() if len(self.rows) < ROW_NUM and dat != "" : if len(self.row) < COLMUN_NUM : self.row.append(dat) else : self.row.insert(0, self.title) self.rows.append(self.row) self.row = [] self.row.append(dat) ## メイン 処理 ## # 入力 while True : start_time = int(input("取得開始回を入力してください(第260~):")) end_time = int(input("取得終了回を入力してください:")) if start_time <= end_time : break else : print("入力が不正です。\n開始 <= 終了 で入力してください。\n") # スクレイピング for time in range(int(start_time), int(end_time)+1) : # HTML読込 url = "https://store.toto-dream.com/dcs/subos/screen/pi04/spin011/PGSPIN01101LnkHoldCntLotResultLsttoto.form?holdCntId=" + str(time) page = urllib.request.urlopen(url) html = page.read() page_str = html.decode() # HTML解析 p = local_HTMLParser() p.feed(page_str) # CSVファイル出力 for csv_row in p.rows : if len(p.rows) > 0 : with open("C:/WORK/test_data/" + "toto_" + str(start_time) + "-" + str(end_time) + ".csv", "a", newline ="", encoding='utf-8_sig') as f: writer = csv.writer(f, lineterminator='\n') # 行末は改行 writer.writerow(csv_row) p.close() # 終了時処理 print("\n処理が終了しました")
解説というか、備忘というか
・HTML文字列の取得
→# HTML読込
urlを文字列にして、
上のほうで import した、 urllib.request の urlopen() でpageを開きます。
read() で読んだ html をdecode() するのですが、
ここで、文字コードに注意が必要です。
JRAのサイトをスクレイピングしたとき、ここで文字コードエラーが発生し、
page_str = html.decode('CP932')
としました。
元のHTMLが s-jis で書かれているってことなんですかね?ワカッテナイ
・パーサークラスを使って、対象データを抽出
→# HTML解析
ローカルクラス local_HTMLParser を生成して、
page_str を食わせます。
local_HTMLParser では以下の処理をしています。
__init__(self)
: local_HTMLParser 内で使用する変数を定義しておきます。
ここで定義することで、クラス内でグローバルに使えます。
handle_starttag(self, tag, attrs)
: パラメータの tag と atters からタグと属性を取得、判定し、
解析を開始、終了をフラグで管理します。
ちなみに、何とか動いてますが、この部分のアルゴリズムが汚い。。。
handle_data(self, data)
: 同一のサイトで「toto」「mini toto A組」「mini toto B組」「totoGoal3」の記述があるため、
「toto」のみを取得するために、タイトルを判定しています。
「toto」のデータ(テーブル)構成は行が13(試合)カラムは7つです。
data.strip() と dat != "" でごみを取り除き、二次元リストに格納します。
・複数ページを走査
→# スクレイピング
urlの末尾が「回」の番号となっているので、
for 文でループさせることで、複数ページを走査します。
なお、開始回と終了回は input で任意に指定できるようにしました。
次回予告
授業は淡々と進んでいるため、
ブログに書くネタには乏しいのが悩みです。
面白いことをかけるように、努力します。