今回はビックカメラの商品情報をスクレイピングする方法について紹介します。
Dockerを使って環境を構築し、Pythonでスクレイピングを行います。
GitHub
仕様
- ビックカメラの検索窓に冷蔵庫と打ち込んだ検索結果から1,2ページの下記情報を抜き出してCSVで吐き出す
- 商品名
- 価格
- ポイント
- 環境依存をなくすため、Dockerでの実行を前提とする
https://www.biccamera.com/bc/category/?q=%97%E2%91%A0%8C%C9
動作環境
OS:Windows
Docker Desktop
ディレクトリ構成
workspace/
├── Dockerfile
├── requirements.txt
├── Constants.py
├── PageFetcher.py
├── ProductExtractor.py
└── main.py
構築手順
- Dockerでの環境構築
- 検索結果ページのHTMLを取得するクラスの実装
- HTMLから必要な情報を抽出するクラスの実装
- データをCSVに出力するクラスの実装
- メイン処理を行うスクリプトの作成
詳細
Dockerでの環境構築
Docker デスクトップのインストール
Dockerデスクトップをインストールします。
以下のリンクからダウンロードできます。
https://www.docker.com/ja-jp/products/docker-desktop/
Docker関連ファイルの作成
Dockerfile
# ベースイメージとしてPythonを使用
FROM python:3.9-slim
# 作業ディレクトリを設定
WORKDIR /app
# 依存関係をコピーしてインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションのソースコードをコピー
COPY . .
# メインスクリプトを実行
CMD ["python", "main.py"]
requirements.txt
beautifulsoup4
requests
pandas
検索結果ページのHTMLを取得するクラスの実装
ビックカメラの検索窓に冷蔵庫
と打った場合の表示画面取得
PageFetcher.py
import requests
from Constants import Constants
import urllib.parse
class PageFetcher:
def __init__(self):
# 基本URLとヘッダーを設定する
self.constants = Constants()
self.base_url = self.constants.BASE_URL
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
}
self.encoded_query_str = urllib.parse.quote(self.constants.encoded_query) # クエリ文字列をURLエンコードして、encoded_query_strに格納
def fetch_page(self, page_number):
# 指定したページ番号の検索結果を取得する
search_url = f"{self.base_url}?q={self.encoded_query_str}&p={page_number}"
response = requests.get(search_url, headers=self.headers, verify=False) # 証明書を無視
response.raise_for_status() # エラーチェック
response.encoding = 'shift_jis' # 文字化け防止のためShiftJISに設定
return response.text
def fetch_pages(self, start_page, end_page):
# 指定した範囲のページの検索結果を取得する
pages = []
for page_number in range(start_page, end_page + 1):
page_content = self.fetch_page(page_number)
pages.append(page_content)
return pages
定数管理
Constants.py
class Constants:
query = "冷蔵庫" #検索窓に打ちたいキーワード
#検索後に遷移されるURL
__BASE_URL = "https://www.biccamera.com/bc/category/"
#ビックカメラのページで指定されている文字コードで文字化け対策
__encoded_query = query.encode("shift_jis")
@property
def BASE_URL(self):
return self.__BASE_URL
@BASE_URL.setter
def BASE_URL(self, value):
raise AttributeError("Cannot modify read-only attribute")
@property
def encoded_query(self):
return self.__encoded_query
@encoded_query.setter
def encoded_query(self, value):
raise AttributeError("Cannot modify read-only attribute")
HTMLから必要な情報を抽出するクラスの実装
ここでは、商品検索結果から以下を抜き出すこととします。
- 商品名
- 価格
- ポイント
また、商品によってはポイント等が無い可能性があるため、その場合はNAとする仕様にする。
ProductExtractor.py
from bs4 import BeautifulSoup
class ProductExtractor:
def __init__(self):
# 初期化処理(特に必要な場合)
pass
def extract_products(self, html_content):
products = []
# BeautifulSoupを使用してHTMLコンテンツを解析する
soup = BeautifulSoup(html_content, 'html.parser')
# <li data-item="data-item"> タグをすべて抽出する
items = soup.select('li[data-item="data-item"]')
# 各アイテムから商品名、価格、ポイントを抽出する
for item in items:
name_tag = item.select_one('p.bcs_title a.bcs_item')
price_tag = item.select_one('p.bcs_price')
point_tag = item.select_one('p.bcs_point')
# 商品名を抽出、タグが存在しない場合は 'NA' を設定
name = name_tag.get_text(strip=True) if name_tag else 'NA'
# 価格を抽出、タグが存在しない場合は 'NA' を設定
price = price_tag.get_text(strip=True) if price_tag else 'NA'
# ポイントを抽出、タグが存在しない場合は 'NA' を設定
point = point_tag.get_text(strip=True) if point_tag else 'NA'
product_info = {
'name': name,
'price': price,
'point': point
}
products.append(product_info)
# 製品情報を返す
return products
データをCSVに出力するクラスの実装
吐き出すCSVは以下の仕様とする。
- 取得日の日付を記載
- CSVが存在し且つ取得日の日付が同じ場合、ver1, ver2と数字をあげていく
CSVWriter.py
import csv
from datetime import datetime
import os
import re
class CSVWriter:
def __init__(self):
# 現在の日付を取得
self.date_str = datetime.now().strftime('%Y%m%d')
self.filename = self.generate_filename()
def generate_filename(self):
# ファイル名のパターンを定義
pattern = re.compile(rf'ver(\d+)_{self.date_str}_products\.csv')
version = 1
# カレントディレクトリのファイルをチェック
for file in os.listdir('.'):
match = pattern.match(file)
if match:
current_version = int(match.group(1))
if current_version >= version:
version = current_version + 1
# 新しいファイル名を生成
return f'ver{version}_{self.date_str}_products.csv'
def write_to_csv(self, products):
with open(self.filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.DictWriter(file, fieldnames=['name', 'price', 'point'])
writer.writeheader()
for product in products:
writer.writerow(product)
メイン処理を行うスクリプトの作成
複数ページ取得をしたい場合、下記の引数を変更することで対応可能
pages_content = fetcher.fetch_pages(1, 2)
main.py
from PageFetcher import PageFetcher
from ProductExtractor import ProductExtractor
from CSVWriter import CSVWriter
def main():
fetcher = PageFetcher()
extractor = ProductExtractor()
# 1ページ目と2ページ目のHTMLコンテンツを取得
pages_content = fetcher.fetch_pages(1, 2)
# 各ページの製品名を抽出して表示
all_products = []
for content in pages_content:
products = extractor.extract_products(content)
all_products.extend(products)
# CSVWriterを使用して製品情報をCSVに書き込む
csv_writer = CSVWriter()
csv_writer.write_to_csv(all_products)
if __name__ == "__main__":
main()
実行
# Dockerイメージのビルド
docker build -t biccamera_scraiping .
# Dockerコンテナの実行
## Windows の PowerShellの場合
docker run --rm -v ${PWD}:/app biccamera_scraiping
## Macの場合
docker run --rm -v $(pwd):/app biccamera_scraiping
結果のサンプル
ver1_20240831_products.csv
name,price,point
東芝 2ドア冷蔵庫 セミマットブラック GR-V15BS(K) [幅47.9cm /153L /2ドア /右開きタイプ /2023年],"40,780円","4,078ポイント"
パナソニック 冷蔵庫 FVFシリーズ ハーモニーホワイト NR-FVF45S1-W [幅68.5cm /451 /6ドア /観音開きタイプ /2024年] 《基本設置料金セット》,"177,550円","17,755ポイント"
東芝 3ドア冷蔵庫 マットチャコール GR-V33SC(KZ) [幅60cm /326L /3ドア /右開きタイプ /2023年] 《基本設置料金セット》,"95,230円","9,523ポイント"
東芝 冷蔵庫 マットホワイト GR-U36SC-WU [幅60cm /356L /3ドア /右開きタイプ /2022年] 《基本設置料金セット》,"94,800円",948ポイント
...
まとめ
今回は、Dockerを使ってビックカメラの商品情報をスクレイピングする方法を紹介しました。Dockerを使うことで、環境構築が簡単になり、どのOSでも同じ手順で実行できるのが便利です。ぜひ試してみてください.