# Amazon データのスクレイピング

Python を使って Amazon の商品データをスクレイピングする方法についてまとめます。

# 注意事項

重要: Amazon の利用規約では、自動化されたデータ収集(スクレイピング)が禁止されています。本記事は教育目的であり、実際にスクレイピングを行う場合は以下の点に注意してください。

  • Amazon の利用規約を必ず確認する
  • robots.txt を確認する
  • 適切なレート制限を設ける
  • 商用利用は避ける
  • 公式 API(Amazon Product Advertising API)の利用を検討する

# 必要なライブラリ

スクレイピングに必要なライブラリをインストールします。

pip install requests beautifulsoup4 lxml selenium

# 各ライブラリの役割

  • requests: HTTP リクエストを送信
  • BeautifulSoup: HTML の解析とパース
  • lxml: HTML パーサー(BeautifulSoup で使用)
  • selenium: ブラウザ自動化(JavaScript で動的に生成されるコンテンツに対応)

# 基本的なスクレイピング(requests + BeautifulSoup)

シンプルな HTTP リクエストで HTML を取得し、BeautifulSoup で解析する方法です。

import requests
from bs4 import BeautifulSoup
import time

def scrape_amazon_product(url):
    """
    Amazon商品ページから基本情報を取得
    """
    # User-Agentを設定(必須)
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'ja,en-US;q=0.7,en;q=0.3',
        'Accept-Encoding': 'gzip, deflate',
        'Connection': 'keep-alive',
    }

    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()

        soup = BeautifulSoup(response.content, 'lxml')

        # 商品タイトル
        title = soup.find('span', {'id': 'productTitle'})
        title_text = title.get_text(strip=True) if title else 'タイトルが見つかりません'

        # 価格
        price = soup.find('span', {'class': 'a-price-whole'})
        price_text = price.get_text(strip=True) if price else '価格が見つかりません'

        # 評価
        rating = soup.find('span', {'class': 'a-icon-alt'})
        rating_text = rating.get_text(strip=True) if rating else '評価が見つかりません'

        return {
            'title': title_text,
            'price': price_text,
            'rating': rating_text,
            'url': url
        }

    except requests.exceptions.RequestException as e:
        print(f'エラーが発生しました: {e}')
        return None

    # レート制限を考慮して待機
    time.sleep(2)

# 使用例
url = 'https://www.amazon.co.jp/dp/B08N5WRWNW'
product_data = scrape_amazon_product(url)
if product_data:
    print(f"商品名: {product_data['title']}")
    print(f"価格: {product_data['price']}円")
    print(f"評価: {product_data['rating']}")

# 検索結果のスクレイピング

Amazon の検索結果ページから複数の商品情報を取得する例です。

import requests
from bs4 import BeautifulSoup
import time
import re

def scrape_amazon_search(keyword, max_pages=1):
    """
    Amazon検索結果から商品情報を取得
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
        'Accept-Language': 'ja,en-US;q=0.7,en;q=0.3',
    }

    products = []

    for page in range(1, max_pages + 1):
        # 検索URL
        search_url = f'https://www.amazon.co.jp/s?k={keyword}&page={page}'

        try:
            response = requests.get(search_url, headers=headers, timeout=10)
            response.raise_for_status()

            soup = BeautifulSoup(response.content, 'lxml')

            # 商品カードを取得
            items = soup.find_all('div', {'data-component-type': 's-search-result'})

            for item in items:
                # 商品タイトル
                title_elem = item.find('h2', {'class': 'a-size-mini'})
                title = title_elem.get_text(strip=True) if title_elem else 'N/A'

                # 価格
                price_elem = item.find('span', {'class': 'a-price-whole'})
                price = price_elem.get_text(strip=True) if price_elem else 'N/A'

                # 評価
                rating_elem = item.find('span', {'class': 'a-icon-alt'})
                rating = rating_elem.get_text(strip=True) if rating_elem else 'N/A'

                # 商品URL
                link_elem = item.find('a', {'class': 'a-link-normal'})
                product_url = 'https://www.amazon.co.jp' + link_elem['href'] if link_elem else 'N/A'

                products.append({
                    'title': title,
                    'price': price,
                    'rating': rating,
                    'url': product_url
                })

            # レート制限対策
            time.sleep(3)

        except requests.exceptions.RequestException as e:
            print(f'ページ {page} でエラーが発生しました: {e}')
            continue

    return products

# 使用例
keyword = 'Python プログラミング'
results = scrape_amazon_search(keyword, max_pages=1)

for i, product in enumerate(results, 1):
    print(f"{i}. {product['title']}")
    print(f"   価格: {product['price']}円")
    print(f"   評価: {product['rating']}")
    print(f"   URL: {product['url']}\n")

# Selenium を使った動的コンテンツの取得

JavaScript で動的に生成されるコンテンツを取得する場合は、Selenium を使用します。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import time

def scrape_amazon_with_selenium(url):
    """
    Seleniumを使ってAmazon商品ページをスクレイピング
    """
    # Chromeオプション設定
    chrome_options = Options()
    chrome_options.add_argument('--headless')  # ヘッドレスモード
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    chrome_options.add_argument('--disable-blink-features=AutomationControlled')
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    chrome_options.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36')

    driver = webdriver.Chrome(options=chrome_options)

    try:
        driver.get(url)

        # ページが読み込まれるまで待機
        wait = WebDriverWait(driver, 10)

        # 商品タイトルを取得
        title_elem = wait.until(
            EC.presence_of_element_located((By.ID, 'productTitle'))
        )
        title = title_elem.text

        # 価格を取得
        try:
            price_elem = driver.find_element(By.CSS_SELECTOR, '.a-price-whole')
            price = price_elem.text
        except:
            price = '価格が見つかりません'

        # 評価を取得
        try:
            rating_elem = driver.find_element(By.CSS_SELECTOR, '.a-icon-alt')
            rating = rating_elem.get_attribute('innerHTML')
        except:
            rating = '評価が見つかりません'

        return {
            'title': title,
            'price': price,
            'rating': rating,
            'url': url
        }

    except Exception as e:
        print(f'エラーが発生しました: {e}')
        return None

    finally:
        driver.quit()

# 使用例
url = 'https://www.amazon.co.jp/dp/B08N5WRWNW'
product_data = scrape_amazon_with_selenium(url)
if product_data:
    print(f"商品名: {product_data['title']}")
    print(f"価格: {product_data['price']}円")
    print(f"評価: {product_data['rating']}")

# エラーハンドリングとベストプラクティス

# 1. レート制限の実装

import time
from functools import wraps

def rate_limit(delay=2):
    """
    レート制限デコレータ
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            time.sleep(delay)
            return result
        return wrapper
    return decorator

@rate_limit(delay=3)
def scrape_with_delay(url):
    # スクレイピング処理
    pass

# 2. リトライ機能の実装

import requests
from time import sleep

def scrape_with_retry(url, max_retries=3, delay=5):
    """
    リトライ機能付きスクレイピング
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
    }

    for attempt in range(max_retries):
        try:
            response = requests.get(url, headers=headers, timeout=10)
            response.raise_for_status()
            return response

        except requests.exceptions.RequestException as e:
            if attempt < max_retries - 1:
                print(f'リトライ {attempt + 1}/{max_retries}: {e}')
                sleep(delay * (attempt + 1))  # 指数バックオフ
            else:
                print(f'最大リトライ回数に達しました: {e}')
                raise

    return None

# 3. プロキシの使用

import requests

def scrape_with_proxy(url, proxy=None):
    """
    プロキシを使用したスクレイピング
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
    }

    proxies = {
        'http': proxy,
        'https': proxy,
    } if proxy else None

    try:
        response = requests.get(url, headers=headers, proxies=proxies, timeout=10)
        response.raise_for_status()
        return response
    except requests.exceptions.RequestException as e:
        print(f'エラーが発生しました: {e}')
        return None

# 4. データの保存

import csv
import json

def save_to_csv(data, filename='amazon_products.csv'):
    """
    CSV形式で保存
    """
    if not data:
        return

    with open(filename, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=data[0].keys())
        writer.writeheader()
        writer.writerows(data)

def save_to_json(data, filename='amazon_products.json'):
    """
    JSON形式で保存
    """
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

# Amazon Product Advertising API(推奨)

スクレイピングの代わりに、Amazon が提供する公式 API である**Amazon Product Advertising API 5.0(PA-API 5.0)**を使用することを強く推奨します。API を使用することで、利用規約に準拠しながら、安定して商品データを取得できます。

# API の特徴

  • 公式サポート: Amazon が公式に提供する API
  • 利用規約準拠: スクレイピングとは異なり、利用規約に準拠
  • 安定性: レート制限やエラーハンドリングが明確
  • 豊富なデータ: 商品情報、価格、レビュー、画像など詳細なデータを取得可能
  • アフィリエイトリンク生成: アフィリエイトリンクを自動生成可能

# 登録とセットアップ

  1. Amazon アソシエイトプログラムへの登録

  2. Product Advertising API への登録

  3. 必要なライブラリのインストール

pip install paapi5-python-sdk

# 基本的な実装

from paapi5_python_sdk.api.default_api import DefaultApi
from paapi5_python_sdk.configuration import Configuration
from paapi5_python_sdk.auth.credentials import Credentials
from paapi5_python_sdk.models.search_items_request import SearchItemsRequest
from paapi5_python_sdk.models.search_items_resource import SearchItemsResource
from paapi5_python_sdk.models.get_items_request import GetItemsRequest
from paapi5_python_sdk.models.get_items_resource import GetItemsResource
from paapi5_python_sdk.models.partner_type import PartnerType
from paapi5_python_sdk.rest import ApiException

# 認証情報の設定
ACCESS_KEY = 'YOUR_ACCESS_KEY'
SECRET_KEY = 'YOUR_SECRET_KEY'
PARTNER_TAG = 'YOUR_ASSOCIATE_TAG'  # アソシエイトID
HOST = 'webservices.amazon.co.jp'  # 日本市場
REGION = 'us-west-2'  # リージョン

# 認証情報と設定
credentials = Credentials(
    access_key=ACCESS_KEY,
    secret_key=SECRET_KEY
)

configuration = Configuration(
    credentials=credentials,
    host=HOST,
    region=REGION
)

# APIクライアントの作成
default_api = DefaultApi(configuration=configuration)

# 商品検索

def search_products(keywords, item_count=10):
    """
    キーワードで商品を検索
    """
    try:
        # 検索リクエストの作成
        search_items_request = SearchItemsRequest(
            partner_tag=PARTNER_TAG,
            partner_type=PartnerType.ASSOCIATES,
            keywords=keywords,
            search_index='All',  # 全カテゴリ
            item_count=item_count,
            resources=[
                SearchItemsResource.ITEM_INFO_TITLE,
                SearchItemsResource.ITEM_INFO_FEATURES,
                SearchItemsResource.OFFERS_LISTINGS_PRICE,
                SearchItemsResource.OFFERS_LISTINGS_AVAILABILITY,
                SearchItemsResource.IMAGES_PRIMARY_LARGE,
                SearchItemsResource.CUSTOMER_REVIEWS_STAR_RATING,
                SearchItemsResource.CUSTOMER_REVIEWS_COUNT,
            ]
        )

        # APIリクエストの実行
        response = default_api.search_items(search_items_request)

        if response.search_result is None:
            print('検索結果が見つかりませんでした')
            return []

        products = []
        for item in response.search_result.items:
            product = {
                'asin': item.asin,
                'title': item.item_info.title.display_value if item.item_info and item.item_info.title else 'N/A',
                'price': None,
                'availability': None,
                'image_url': None,
                'rating': None,
                'review_count': None,
                'url': f'https://www.amazon.co.jp/dp/{item.asin}'
            }

            # 価格情報
            if item.offers and item.offers.listings:
                listing = item.offers.listings[0]
                if listing.price:
                    product['price'] = listing.price.display_amount
                if listing.availability:
                    product['availability'] = listing.availability.message

            # 画像URL
            if item.images and item.images.primary:
                product['image_url'] = item.images.primary.large.url

            # 評価情報
            if item.customer_reviews:
                if item.customer_reviews.star_rating:
                    product['rating'] = item.customer_reviews.star_rating.value
                if item.customer_reviews.count:
                    product['review_count'] = item.customer_reviews.count

            products.append(product)

        return products

    except ApiException as e:
        print(f'APIエラーが発生しました: {e}')
        return []
    except Exception as e:
        print(f'エラーが発生しました: {e}')
        return []

# 使用例
keyword = 'Python プログラミング'
results = search_products(keyword, item_count=5)

for i, product in enumerate(results, 1):
    print(f"{i}. {product['title']}")
    print(f"   ASIN: {product['asin']}")
    print(f"   価格: {product['price']}")
    print(f"   在庫: {product['availability']}")
    print(f"   評価: {product['rating']} ({product['review_count']}件)")
    print(f"   URL: {product['url']}\n")

# ASIN から商品情報を取得

def get_product_by_asin(asin):
    """
    ASINから商品情報を取得
    """
    try:
        # 商品情報取得リクエストの作成
        get_items_request = GetItemsRequest(
            partner_tag=PARTNER_TAG,
            partner_type=PartnerType.ASSOCIATES,
            item_ids=[asin],
            resources=[
                GetItemsResource.ITEM_INFO_TITLE,
                GetItemsResource.ITEM_INFO_FEATURES,
                GetItemsResource.ITEM_INFO_BY_LINE_INFO,
                GetItemsResource.ITEM_INFO_CONTENT_INFO,
                GetItemsResource.OFFERS_LISTINGS_PRICE,
                GetItemsResource.OFFERS_LISTINGS_AVAILABILITY,
                GetItemsResource.OFFERS_LISTINGS_CONDITION,
                GetItemsResource.IMAGES_PRIMARY_LARGE,
                GetItemsResource.CUSTOMER_REVIEWS_STAR_RATING,
                GetItemsResource.CUSTOMER_REVIEWS_COUNT,
            ]
        )

        # APIリクエストの実行
        response = default_api.get_items(get_items_request)

        if response.items_result is None or len(response.items_result.items) == 0:
            print('商品が見つかりませんでした')
            return None

        item = response.items_result.items[0]

        product = {
            'asin': item.asin,
            'title': item.item_info.title.display_value if item.item_info and item.item_info.title else 'N/A',
            'features': [],
            'brand': None,
            'price': None,
            'availability': None,
            'condition': None,
            'image_url': None,
            'rating': None,
            'review_count': None,
            'url': f'https://www.amazon.co.jp/dp/{item.asin}'
        }

        # 商品の特徴
        if item.item_info and item.item_info.features:
            product['features'] = item.item_info.features.display_values

        # ブランド情報
        if item.item_info and item.item_info.by_line_info:
            product['brand'] = item.item_info.by_line_info.brand.display_value

        # 価格情報
        if item.offers and item.offers.listings:
            listing = item.offers.listings[0]
            if listing.price:
                product['price'] = listing.price.display_amount
            if listing.availability:
                product['availability'] = listing.availability.message
            if listing.condition:
                product['condition'] = listing.condition.display_value

        # 画像URL
        if item.images and item.images.primary:
            product['image_url'] = item.images.primary.large.url

        # 評価情報
        if item.customer_reviews:
            if item.customer_reviews.star_rating:
                product['rating'] = item.customer_reviews.star_rating.value
            if item.customer_reviews.count:
                product['review_count'] = item.customer_reviews.count

        return product

    except ApiException as e:
        print(f'APIエラーが発生しました: {e}')
        return None
    except Exception as e:
        print(f'エラーが発生しました: {e}')
        return None

# 使用例
asin = 'B08N5WRWNW'
product = get_product_by_asin(asin)
if product:
    print(f"商品名: {product['title']}")
    print(f"ブランド: {product['brand']}")
    print(f"価格: {product['price']}")
    print(f"在庫: {product['availability']}")
    print(f"評価: {product['rating']} ({product['review_count']}件)")
    print(f"特徴: {', '.join(product['features'][:3])}")

# カテゴリ別検索

def search_by_category(keywords, category='Books', item_count=10):
    """
    カテゴリを指定して商品を検索
    """
    # 利用可能なカテゴリ(SearchIndex)
    # All, Books, Electronics, HomeAndKitchen, SportsAndOutdoors など

    try:
        search_items_request = SearchItemsRequest(
            partner_tag=PARTNER_TAG,
            partner_type=PartnerType.ASSOCIATES,
            keywords=keywords,
            search_index=category,
            item_count=item_count,
            resources=[
                SearchItemsResource.ITEM_INFO_TITLE,
                SearchItemsResource.OFFERS_LISTINGS_PRICE,
                SearchItemsResource.IMAGES_PRIMARY_LARGE,
            ]
        )

        response = default_api.search_items(search_items_request)

        if response.search_result is None:
            return []

        products = []
        for item in response.search_result.items:
            products.append({
                'asin': item.asin,
                'title': item.item_info.title.display_value if item.item_info and item.item_info.title else 'N/A',
                'price': item.offers.listings[0].price.display_amount if item.offers and item.offers.listings else None,
                'url': f'https://www.amazon.co.jp/dp/{item.asin}'
            })

        return products

    except ApiException as e:
        print(f'APIエラーが発生しました: {e}')
        return []

# 使用例
results = search_by_category('Python', category='Books', item_count=5)

# エラーハンドリングとレート制限

import time
from paapi5_python_sdk.models.errors import Errors

def search_with_error_handling(keywords, max_retries=3):
    """
    エラーハンドリング付き検索
    """
    for attempt in range(max_retries):
        try:
            search_items_request = SearchItemsRequest(
                partner_tag=PARTNER_TAG,
                partner_type=PartnerType.ASSOCIATES,
                keywords=keywords,
                search_index='All',
                item_count=10,
                resources=[SearchItemsResource.ITEM_INFO_TITLE]
            )

            response = default_api.search_items(search_items_request)

            # エラーのチェック
            if response.errors:
                for error in response.errors:
                    error_code = error.code
                    error_message = error.message

                    if error_code == 'InvalidParameterValue':
                        print(f'パラメータエラー: {error_message}')
                        return []
                    elif error_code == 'TooManyRequests':
                        print(f'レート制限: {error_message}')
                        time.sleep(60)  # 1分待機
                        continue
                    else:
                        print(f'エラー: {error_code} - {error_message}')

            return response.search_result.items if response.search_result else []

        except ApiException as e:
            if e.status == 429:  # Too Many Requests
                print(f'レート制限に達しました。待機します...')
                time.sleep(60)
                continue
            else:
                print(f'APIエラー: {e}')
                if attempt < max_retries - 1:
                    time.sleep(5)
                    continue
                else:
                    return []

        except Exception as e:
            print(f'エラー: {e}')
            if attempt < max_retries - 1:
                time.sleep(5)
                continue
            else:
                return []

    return []

# 環境変数での認証情報管理

セキュリティのため、認証情報は環境変数で管理することを推奨します。

import os
from dotenv import load_dotenv

# .envファイルから環境変数を読み込む
load_dotenv()

ACCESS_KEY = os.getenv('AMAZON_ACCESS_KEY')
SECRET_KEY = os.getenv('AMAZON_SECRET_KEY')
PARTNER_TAG = os.getenv('AMAZON_PARTNER_TAG')

.envファイルの例:

AMAZON_ACCESS_KEY=your_access_key_here
AMAZON_SECRET_KEY=your_secret_key_here
AMAZON_PARTNER_TAG=your_associate_tag_here

# API の制限事項

  • レート制限: 1 秒あたり 1 リクエスト(TPS: 1)
  • 1 日のリクエスト数: プランによって異なる(無料プランは制限あり)
  • 利用可能な市場: 日本、米国、英国など複数の市場に対応
  • アフィリエイトリンク: API で取得した商品にはアフィリエイトリンクを付与可能

# API とスクレイピングの比較

項目 Product Advertising API スクレイピング
利用規約 準拠 違反の可能性
安定性 高い 低い(HTML 構造変更の影響)
データの質 構造化された高品質データ HTML から抽出(不安定)
レート制限 明確 不明確
コスト 無料プランあり 無料だがリスクあり
メンテナンス 少ない 多い

# 実装時の注意点

Amazon の商品データを取得する方法は大きく 2 つある。

# スクレイピングの場合

  1. 利用規約の遵守: Amazon の利用規約を必ず確認し、違反しないようにする
  2. レート制限: 適切な待機時間を設けてサーバーに負荷をかけない
  3. User-Agent の設定: 適切な User-Agent を設定する
  4. エラーハンドリング: リトライ機能やエラーハンドリングを実装する
  5. 公式 API の検討: 可能であれば Amazon Product Advertising API の利用を検討する

# Product Advertising API の場合(推奨)

  1. 公式サポート: Amazon が公式に提供する API を使用
  2. 利用規約準拠: スクレイピングとは異なり、利用規約に準拠
  3. 安定性: レート制限やエラーハンドリングが明確
  4. 豊富なデータ: 商品情報、価格、レビュー、画像など詳細なデータを取得可能

推奨: 商用利用や本番環境では、必ず Amazon Product Advertising API を使用する。スクレイピングは教育目的や個人の学習用途でのみ使用し、商用利用は避けることを強く推奨する。

2025-12-31

同じタグを持つ記事をピックアップしました。