ナンバーズ3のWebページを解析し結果をMySQLに登録して出現頻度のヒストグラム表示するPythonサンプルプログラムをVisual Studio Codeで作成して実行する

1.概要

Webページからデータを取ってきて、データベースに格納し、解析するまでの大まかな流れを試す。

対象のデータには、構造も簡単でデータ件数もほどほど(数千件)あるナンバーズ3の結果を使用してみる。

http://www.takarakujinet.co.jp/numbers3/index2.html

numbers3_02

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')

登録できた!

numbers3

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)

出現値の分布ヒストグラムを表示できた!

numbers3_03

以上。