ナンバーズ3のWebページを解析し結果をMySQLに登録して出現頻度のヒストグラム表示するPythonサンプルプログラムをVisual Studio Codeで作成して実行する
1.概要
Webページからデータを取ってきて、データベースに格納し、解析するまでの大まかな流れを試す。
対象のデータには、構造も簡単でデータ件数もほどほど(数千件)あるナンバーズ3の結果を使用してみる。
http://www.takarakujinet.co.jp/numbers3/index2.html
2.環境
2.1 Python venv 環境
Visual Studio Code で Python を使えるようにして、NumPy、SciPy、matplotlibは、この間設定したので、venvの環境を有効化し、Webページ解析用、MySQL登録用に以下のライブラリをインストール
仮想環境をアクティブ化
PS C:\workspaces\vscode> cd .. PS C:\workspaces> cd .\venv\datascience\Scripts\ PS C:\workspaces\venv\datascience\Scripts> .\activate (datascience) PS C:\workspaces\venv\datascience\Scripts>
関連ライブラリをインストール
(datascience) PS C:\workspaces\venv\datascience\Scripts> pip install beautifulsoup4 (datascience) PS C:\workspaces\venv\datascience\Scripts> pip install lxml (datascience) PS C:\workspaces\venv\datascience\Scripts> pip install pymysql
2.2 MySQLの設定とテーブルの作成
MySQLをインストールして、テーブルを作成
CREATE TABLE `numbers3` ( `kaigou` int(11) NOT NULL, `chusen_date` date DEFAULT NULL, `chusen_num` varchar(3) DEFAULT NULL, `straight_kuchi` int(11) DEFAULT NULL, `straight_yen` int(11) DEFAULT NULL, `box_kuchi` int(11) DEFAULT NULL, `box_yen` int(11) DEFAULT NULL, `set_straight_kuchi` int(11) DEFAULT NULL, `set_straight_yen` int(11) DEFAULT NULL, `set_box_kuchi` int(11) DEFAULT NULL, `set_box_yen` int(11) DEFAULT NULL, `min_kuchi` int(11) DEFAULT NULL, `min_yen` int(11) DEFAULT NULL, PRIMARY KEY (`kaigou`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
3.サンプルソース
3.1 Webページを解析してデータベース登録
# -*- coding: utf-8 -*- ''' numbers3 の結果を Database へ登録する ''' from urllib.request import urlopen from urllib.parse import urlencode from bs4 import BeautifulSoup import re import math import time import pymysql from settings import DB_HOST, DB_USER, DB_PASSWORD, DB_SCHEME DEFAULT_HOWMANY = 100 def save_numbers3_history(from_k, to_k): ''' 指定範囲のナンバーズ3の結果をデータベースに取込む from_k - 開始回 to_k - 終了回 ''' rec_map = get_numbers3_history(from_k, to_k) try: con = pymysql.connect( host=DB_HOST, user=DB_USER, password=DB_PASSWORD, db=DB_SCHEME, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) with con.cursor() as cur: for rec in rec_map.values(): sql = """ INSERT INTO `app`.`numbers3` (`kaigou`, `chusen_date`, `chusen_num`, `straight_kuchi`, `straight_yen`, `box_kuchi`, `box_yen`, `set_straight_kuchi`, `set_straight_yen`, `set_box_kuchi`, `set_box_yen`, `min_kuchi`, `min_yen`) VALUES( {0[kaigou]}, {0[chusen_date]}, {0[chusen_num]}, {0[straight_kuchi]}, {0[straight_yen]}, {0[box_kuchi]}, {0[box_yen]}, {0[set_straight_kuchi]}, {0[set_straight_yen]}, {0[set_box_kuchi]}, {0[set_box_yen]}, {0[min_kuchi]}, {0[min_yen]}); """.format(rec) print(sql) cur.execute(sql) con.commit() except Exception as exception: print(exception) finally: con.close() print('finish work') def get_numbers3_history(from_k, to_k): ''' 指定範囲のナンバーズ3の結果を取得する from_k - 開始回 to_k - 終了回 ''' result = {} start_kai = int(from_k) end_kai = int(to_k) calc_diff = lambda end_kai, start_kai: end_kai - start_kai + 1 diff_kai = calc_diff(end_kai, start_kai) howmany = DEFAULT_HOWMANY if DEFAULT_HOWMANY < diff_kai else diff_kai while end_kai >= start_kai: result.update( get_numbers3( end_kai, howmany if calc_diff(end_kai, start_kai) > howmany else calc_diff(end_kai, start_kai))) end_kai = end_kai - howmany return result def get_numbers3(kaigou, howmany): ''' 指定範囲のナンバーズ3の結果をWEBから取得する kaigou - 指定回 howmany - 指定回を含み、何回過去分を取得するか ''' time.sleep(2) result = {} post_data = { 'searchway':'kaigou', # kaigou or day 'year':'', 'month':'', 'day':'', 'kaigou':kaigou, 'howmany':howmany } # HTTP リクエストは、data パラメーターが指定された場合 POST に、指定されない場合に GET になります。 url = 'http://www.takarakujinet.co.jp/ajax/numbers3/pastResultPage.do' html = urlopen(url, urlencode(post_data).encode('utf-8')) print("url:{0}, data:{1}", url, post_data) soup = BeautifulSoup(html, "lxml") #print(soup) tr_tag_list = soup.find_all("tr") header_keys = [ 'kaigou', 'chusen_date', 'chusen_num', 'straight_kuchi', 'box_kuchi', 'set_straight_kuchi', 'set_box_kuchi', 'min_kuchi'] yen_keys = ['straight_yen', 'box_yen', 'set_straight_yen', 'set_box_yen', 'min_yen'] rec = None for tr_tag in tr_tag_list: td_tag_list = tr_tag.find_all("td") is_header_tr = tr_tag.find('td', {'rowspan':'2'}) if is_header_tr: rec = {} td_pos = 0 for td_tag in td_tag_list: val = td_tag.get_text() if td_pos == 0: result[val] = rec if td_pos == 1: val = re.sub(r'\r\n[ ]*$', r'', val) val = re.sub(r'\r\n[ ]*', r'/', val) val = re.sub(r'/', r'-', val) val = "'{0}'".format(val) if td_pos == 2: val = "'{0}'".format(val) if td_pos >= 3: if re.match(r'.*該当なし.*', val): val = '0' else: val = re.sub(r'[^0-9]', '', val) if val == '': val = 'null' rec[header_keys[td_pos]] = val td_pos = td_pos + 1 else: td_pos = 0 for td_tag in td_tag_list: val = td_tag.get_text() if re.match(r'.*該当なし.*', val): val = '0' else: val = re.sub(r'[^0-9]', '', val) assert rec != None, "rec must not be None" rec[yen_keys[td_pos]] = val td_pos = td_pos + 1 return result if __name__ == '__main__': save_numbers3_history('1', '11')
登録できた!
3.2 ヒストグラムを表示
# -*- coding: utf-8 -*- ''' ナンバーズ3のヒストグラムを作成 ''' import matplotlib.pyplot as plt import numpy as np import pymysql from settings import DB_HOST,DB_USER,DB_PASSWORD,DB_SCHEME def get_conn(): ''' MySQL コネクションを取得 ''' conn = pymysql.connect( host=DB_HOST, user=DB_USER, password=DB_PASSWORD, db=DB_SCHEME, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) return conn def fetch_all(sql): ''' SELECT SQLを実行し結果を全件返す ''' conn = get_conn() result = {} try: with conn.cursor() as cur: cur.execute(sql) result = cur.fetchall() finally: conn.close() return result def hist_chusen_num(hist_type, pos=None): ''' 抽選結果からヒストグラムを生成する ''' #http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.bar recs = fetch_all('SELECT chusen_num FROM numbers3') hist = {} if hist_type == 'all': # 3桁の整数としての出現頻度 data_list = [int(rec['chusen_num']) for rec in recs] bins = np.arange(0, 1000) hist = np.histogram(data_list, bins) plt.bar(bins[:-1], hist[0]) if hist_type == 'num': # 1桁ずつの出現頻度 nested = [list(rec['chusen_num']) for rec in recs] data_list = [int(x) for inner in nested for x in inner] bins = np.arange(0, 11) hist = np.histogram(data_list, bins) plt.bar(bins[:-1], hist[0]) if hist_type == 'pos': # 出現位置を指定した頻度 data_list = [int(list(rec['chusen_num'])[pos]) for rec in recs] bins = np.arange(0, 11) hist = np.histogram(data_list, bins) plt.bar(bins[:-1], hist[0]) plt.show() if __name__ == '__main__': hist_chusen_num('all') #hist_chusen_num('num') #hist_chusen_num('pos',pos=1)
出現値の分布ヒストグラムを表示できた!
以上。