前回はプロジェクトの概要を説明しましたが、今回はいよいよ実装に入ります。まずは何より、データがないと始まらない。
というわけで、LOTO7の過去データを収集するスクリプトを作っていきます。Webスクレイピングという手法を使って、公式サイトから抽選結果を自動で取得し、データベースに保存するところまでやります。
Webスクレイピングとは?
Webスクレイピングっていうのは、簡単に言うと「Webサイトから自動でデータを抽出する技術」のことです。人間が手作業でコピペする代わりに、プログラムに自動でやってもらうイメージですね。
ただし、注意点があります。Webスクレイピングは、サイトによっては利用規約で禁止されていることもあるので、必ず確認してから実行してください。また、サーバーに負荷をかけすぎないように、適切な間隔を空けてリクエストすることも大切です。
今回のプロジェクトでは、あくまで個人的な学習目的で、少量のデータを収集するだけなので問題ないと判断しています。でも、商用利用とか大量データの収集をする場合は、必ず規約を確認してくださいね。
必要なライブラリ
今回使うPythonライブラリは以下の3つです。
requestsは、WebサイトにHTTPリクエストを送ってHTMLを取得するライブラリ。BeautifulSoupは、取得したHTMLを解析して、必要な情報を抽出するライブラリ。そしてpandasは、取得したデータを整形して扱いやすくするライブラリです。
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
from datetime import datetime
データ収集の戦略
さて、データを収集する前に、どんなデータが必要なのかを整理しましょう。LOTO7の抽選結果には、以下のような情報が含まれています。
- 回号(第何回の抽選か)
- 抽選日
- 本数字(7つの数字)
- ボーナス数字(2つの数字)
この中で、予測に必要なのは主に「本数字」と「ボーナス数字」、そして「抽選日」です。
実際のスクレイピングコード
それでは、実際のコードを見ていきましょう。ここでは、基本的な構造を示します。
# 01_data_scraper.py
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
from datetime import datetime
import sqlite3
from pathlib import Path
class Loto7Scraper:
"""LOTO7のデータをスクレイピングするクラス"""
def __init__(self):
# ここに実際のURLを設定してください
# self.base_url = "https://example.com/loto7/results"
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
self.data = []
def fetch_page(self, url):
"""Webページを取得"""
try:
response = requests.get(url, headers=self.headers)
response.raise_for_status()
# サーバーに負荷をかけないよう、少し待機
time.sleep(1)
return response.text
except requests.RequestException as e:
print(f"エラーが発生しました: {e}")
return None
def parse_results(self, html):
"""HTMLから抽選結果を解析"""
soup = BeautifulSoup(html, 'html.parser')
# ここは実際のHTMLの構造に合わせて変更してください
# 以下はサンプルコードです
# results = soup.find_all('div', class_='result-row')
#
# for result in results:
# draw_number = result.find('span', class_='draw-number').text
# draw_date = result.find('span', class_='draw-date').text
# main_numbers = [int(num.text) for num in result.find_all('span', class_='main-number')]
# bonus_numbers = [int(num.text) for num in result.find_all('span', class_='bonus-number')]
#
# self.data.append({
# 'draw_number': draw_number,
# 'draw_date': draw_date,
# 'main_numbers': main_numbers,
# 'bonus_numbers': bonus_numbers
# })
pass
def save_to_csv(self, filename='loto7_data.csv'):
"""データをCSVファイルに保存"""
df = pd.DataFrame(self.data)
output_path = Path('data/raw') / filename
df.to_csv(output_path, index=False, encoding='utf-8')
print(f"データを保存しました: {output_path}")
return df
if __name__ == "__main__":
scraper = Loto7Scraper()
# 実際の処理は各自で実装してください
print("スクレイピングスクリプトのテンプレートです")
コード中のbase_urlやclass_='result-row'みたいな部分は、実際のサイトの構造に合わせて変更する必要があります。これは、サイトによってHTMLの書き方が全然違うからです。
HTMLの構造を調べる
ここで、実際にどうやってHTMLの構造を調べるか説明します。ブラウザの開発者ツールを使うと簡単です。
Chrome や Firefox で、調べたいWebページを開いて、右クリックして「検証」または「要素を調査」を選択します。すると、HTMLのソースコードが見えるウィンドウが開きます。
そこで、抽選結果の数字の部分にマウスカーソルを持っていくと、その部分のHTMLがハイライトされます。例えば、<span class="number">7</span>みたいな感じで書かれているのが分かると思います。
このclass="number"の部分が重要で、BeautifulSoupでデータを抽出する時に、このクラス名を指定して取得します。
データベースへの保存
CSVファイルでデータを保存してもいいんですけど、今回はSQLiteデータベースも使ってみましょう。データベースを使うと、データの検索や更新が楽になるんです。
# 02_data_storage.py
import sqlite3
import pandas as pd
from pathlib import Path
from datetime import datetime
class Loto7Database:
"""LOTO7データを管理するデータベースクラス"""
def __init__(self, db_path='data/loto7_database.db'):
self.db_path = Path(db_path)
self.db_path.parent.mkdir(parents=True, exist_ok=True)
self.conn = None
self.cursor = None
def connect(self):
"""データベースに接続"""
self.conn = sqlite3.connect(self.db_path)
self.cursor = self.conn.cursor()
print(f"データベースに接続しました: {self.db_path}")
def create_tables(self):
"""テーブルを作成"""
# 抽選結果テーブル
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS draw_results (
draw_number INTEGER PRIMARY KEY,
draw_date TEXT NOT NULL,
num1 INTEGER NOT NULL,
num2 INTEGER NOT NULL,
num3 INTEGER NOT NULL,
num4 INTEGER NOT NULL,
num5 INTEGER NOT NULL,
num6 INTEGER NOT NULL,
num7 INTEGER NOT NULL,
bonus1 INTEGER NOT NULL,
bonus2 INTEGER NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)
''')
self.conn.commit()
print("テーブルを作成しました")
def insert_draw_result(self, draw_number, draw_date, main_nums, bonus_nums):
"""抽選結果を挿入"""
try:
self.cursor.execute('''
INSERT INTO draw_results
(draw_number, draw_date, num1, num2, num3, num4, num5, num6, num7, bonus1, bonus2)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (draw_number, draw_date, *main_nums, *bonus_nums))
self.conn.commit()
except sqlite3.IntegrityError:
print(f"回号 {draw_number} は既に存在します(スキップ)")
def get_all_results(self):
"""全ての抽選結果を取得"""
query = "SELECT * FROM draw_results ORDER BY draw_number"
df = pd.read_sql_query(query, self.conn)
return df
def get_statistics(self):
"""統計情報を取得"""
self.cursor.execute("SELECT COUNT(*) FROM draw_results")
total_draws = self.cursor.fetchone()[0]
self.cursor.execute("SELECT MIN(draw_date), MAX(draw_date) FROM draw_results")
date_range = self.cursor.fetchone()
return {
'total_draws': total_draws,
'first_draw_date': date_range[0],
'last_draw_date': date_range[1]
}
def close(self):
"""データベース接続を閉じる"""
if self.conn:
self.conn.close()
print("データベース接続を閉じました")
if __name__ == "__main__":
# テスト用のサンプルデータ
db = Loto7Database()
db.connect()
db.create_tables()
# サンプルデータを挿入(実際のデータは各自で収集)
# db.insert_draw_result(1, '2013-04-05', [1, 2, 3, 4, 5, 6, 7], [8, 9])
stats = db.get_statistics()
print(f"統計情報: {stats}")
db.close()
このコードで、SQLiteデータベースにデータを保存できるようになります。SQLiteは、ファイルベースの軽量なデータベースで、サーバーのセットアップとか不要なので、個人プロジェクトにはちょうどいいんですよ。
データ収集の自動化
スクレイピングスクリプトができたら、次は定期的に実行する仕組みを作りたいですよね。LOTO7は毎週金曜日に抽選があるので、週に1回自動でデータを取得できると便利です。
Linuxのcronや、WindowsのタスクスケジューラーでPythonスクリプトを定期実行できます。でも、それは第8回の「実運用編」で詳しくやるので、今回は手動で実行する形でOKです。
実際にやってみた結果
さて、実際にこのスクリプトを動かしてみました。結果は…まあまあ成功です。
最初は、HTMLの構造を把握するのに苦労しました。開発者ツールとにらめっこしながら、どのクラス名を使えばいいか試行錯誤しましたね。特に、数字が複数のspan要素に分かれていたりすると、ちょっと面倒です。
あと、過去の抽選結果が複数ページに分かれている場合、ページネーションを処理する必要があります。「次へ」ボタンのURLを取得して、全ページを巡回するループを書きました。この辺は、サイトの構造次第なので、臨機応変に対応するしかないです。
ハマったポイント
スクレイピングで一番ハマったのは、エンコーディングの問題です。日本語のサイトだと、文字コードが Shift_JIS とか EUC-JP だったりして、UTF-8で読み込むと文字化けするんですよ。
これは、requestsのresponse.encodingを適切に設定することで解決しました。
response = requests.get(url)
response.encoding = response.apparent_encoding # 自動判定
html = response.text
あと、サイトによっては、JavaScriptで動的にコンテンツを読み込んでいることがあります。この場合、requestsとBeautifulSoupだけでは取得できないので、Seleniumという別のツールが必要になります。幸い、LOTO7のサイトは静的なHTMLだったので、今回は使わずに済みました。
収集したデータの確認
データ収集が終わったら、ちゃんとデータが取れているか確認しましょう。pandasで簡単に確認できます。
import pandas as pd
# CSVから読み込み
df = pd.read_csv('data/raw/loto7_data.csv')
# データの確認
print(f"総データ数: {len(df)}件")
print(f"期間: {df['draw_date'].min()} ~ {df['draw_date'].max()}")
# 最初の5件を表示
print(df.head())
これで、データがちゃんと取れているか、欠損値がないか、数字の範囲が正しいか(1~37の範囲内か)などをチェックできます。
データクリーニング
取得したデータには、たまに変なデータが混じっていることがあります。例えば、メンテナンス中で抽選が中止になった回とか、HTMLの構造が微妙に違う回とか。
そういうデータを見つけたら、手動で修正するか、スクリプトで自動的に除外するようにします。データの品質は、後の分析や予測の精度に直結するので、ここは手を抜けないポイントです。
次回予告
今回は、LOTO7のデータを収集する方法を解説しました。Webスクレイピングの基本から、データベースへの保存まで、一通りの流れをカバーしました。
次回は、収集したデータを分析します。「探索的データ分析」という手法で、データの特徴やパターンを探っていきます。数字の出現頻度、連続する数字の傾向、奇数と偶数のバランスなど、色々な角度から見ていく予定です。
データ分析って、実は予測モデルを作るより面白かったりするんですよ。何が見えてくるか、楽しみにしていてください!
参考リンク
- BeautifulSoup 公式ドキュメント: https://www.crummy.com/software/BeautifulSoup/
- Requests 公式ドキュメント: https://requests.readthedocs.io/
- SQLite 公式サイト: https://www.sqlite.org/
それでは、また次回!


コメント