LLaMAでウェブスクレイピング 3: あらゆるウェブサイトを構造化JSONに変換する (2025年ガイド)

LLaMA 3を使用して、大きなHTMLを構造化JSONに変換し、一般的なスクレイピングのハードルをスキップします。
8 分読
Web scraping with Llama blog image

従来のウェブスクレイピング手法は、ウェブサイトのレイアウトが変わったり、ボット対策が厳しくなったりすると、しばしば破綻します。このガイドでは、LLaMA 3-Metaの強力なオープンウェイト言語モデルを使用して、ほぼすべてのウェブサイトから構造化データを抽出し、クリーンで使用可能なJSONに変換する、より弾力性のあるAIを搭載したアプローチを学びます。

始めよう。

なぜウェブスクレイピングにLLaMA 3を使うのか?

LLaMA 3(2024年4月リリース)は、メタ社のオープンウエイトのラージ言語モデルで、8Bから405Bのパラメータ・サイズで利用できる。幅広いユースケースとハードウェア容量をサポートしている。その後にリリースされたLLaMA 3.1、3.2、3.3では、パフォーマンスと文脈理解が大幅に向上しています。

従来のウェブスクレイピング手法は、XPathやCSSのような静的セレクタに依存しており、ウェブサイトの構造が変わると簡単に壊れてしまいます。対照的に、LLaMA 3は、人間のようにコンテンツを文脈的に理解することで、インテリジェントなデータ抽出を可能にします。

これは理想的なことだ:

  • アマゾンなどのeコマースサイト
  • データ解析
  • ウェブサイトが更新されるたびに壊れることのない、より弾力性のあるスクレーパーの作成
  • スクレイピングしたデータを自社環境内に保持する-機密情報には不可欠

ウェブスクレイピングにAIを活用する方法について、詳しくはこちらをご覧ください。

前提条件

LLMのウェブスクレイピングに飛び込む前に、以下のことを確認してください:

  • Python3のインストール
  • Pythonの基本的な知識(専門家である必要はありません)
  • 互換性のあるオペレーティングシステム: – macOS(macOS 11 Big Sur以降が必要) – Linux – Windows(Windows 10以降が必要)
  • 十分なハードウェアリソース(以下のモデル選択の詳細を参照)

Ollamaのインストール

Ollamaは、大規模な言語モデルのダウンロード、セットアップ、ローカルでの実行を簡素化する軽量ツールである。

ollama-llm-download-installation-page

始めよう:

  1. Ollamaの公式サイトを見る
  2. お使いのオペレーティングシステム用のアプリケーションをダウンロードしてインストールします。
  3. 重要インストール中、Ollamaはターミナルコマンドを実行するよう促しますが、まだ実行しないでください。まずは正しいモデルのバージョンを選択しましょう。

LLaMAモデルの選択

まずはOllamaのモデル・ライブラリーをご覧いただき、お客様のハードウェアとユースケースに最適なLLaMAバージョンをお選びください。

ほとんどのユーザーにとって、llama3.1:8bはパフォーマンスと効率のバランスが最も取れています。軽量で高性能で、約4.9GBのディスクスペースと6-8GBのRAMを必要とします。ほとんどの最新のラップトップでスムーズに動作する。

よりパワフルなマシンで作業しており、より大きな推論能力やコンテキストの長さが必要な場合は、70Bや 405Bのような大型モデルへのスケールアップを検討してください。これらは、より多くのメモリと計算能力を必要とします。

モデルの引き上げと実行

LLaMA 3.1(8B)モデルをダウンロードして初期化するには、以下のコマンドを実行します:

ollama run llama3.1:8b

モデルがダウンロードされると、簡単な対話形式のプロンプトが表示されます:

>>> Send a message (/? for help)

簡単なクエリーでモデルをテストできます:

>>> who are you?
I am LLaMA, *an AI assistant developed by Meta AI...*

上記のような正常な応答が返ってくれば、そのモデルが正しくインストールされていることが確認できる。プロンプトを終了するには、/byeと入力する。

次に、Ollamaサーバーを起動する:

ollama serve

このコマンドはhttp://127.0.0.1:11434/、ローカルのOllamaインスタンスを起動するサーバーはバックグラウンドで実行し続ける必要があるので、このターミナルウィンドウは開いたままにしておくこと。

Ollamaは実行中です」というメッセージが表示されるはずです。

LLMを搭載したAmazonスクレイパーの構築

このセクションでは、Amazonから商品の詳細を抽出するスクレーパーを構築します。Amazonは、そのダイナミックなコンテンツと強力なボット対策により、最も困難なターゲットの1つです。

オフィスチェア製品ページ

私たちは次のような重要な詳細を抽出する:

  • 商品名
  • 現在/元の価格
  • 割引
  • 評価とレビュー
  • 概要と特徴
  • 在庫とASIN

AIを活用した多段階ワークフロー

従来のスクレイピング、特にAmazonのような複雑なeコマースサイトでのスクレイピングの限界を克服するために、LLaMAを搭載したスクレイパーは、スマートで多段階のワークフローに従っています:

  1. ブラウザの自動化– Seleniumを使用してページをロードし、動的コンテンツをレンダリングする。
  2. HTML抽出– 商品の詳細を含むコンテナを特定し、抽出します。
  3. Markdown変換– トークン数を減らし、LLMの効率を向上させるためにHTMLをMarkdownに変換します。
  4. LLM処理– LLaMAで構造化プロンプトを使用して、クリーンで構造化されたJSONを抽出する。
  5. 出力処理– 下流での使用や分析のために、抽出されたJSONを保存します。

以下は、ワークフローの視覚的な内訳である:

では、そのプロセスを順を追って見ていこう。これらの例では、そのシンプルさと人気のためにPythonを使用していますが、JavaScriptや他のお好みの言語でも同様の結果を得ることができることに注意してください。

ステップ1 – 必要なライブラリのインストール

まず、必要なPythonライブラリをインストールする:

pip install requests selenium webdriver-manager markdownify
  • requests– LLMサービスにAPIコールを送信するための最適なPython HTTPクライアント
  • selenium– ブラウザを自動化し、JavaScriptを多用するウェブサイトに最適。
  • webdriver-manager– 正しいChromeDriverのバージョンを自動的にダウンロードして管理する
  • markdownify– HTML を Markdown に変換する

ステップ2 – ヘッドレス・ブラウザの初期化

Seleniumを使ってヘッドレスブラウザをセットアップする:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager

options = Options()
options.add_argument("--headless")

driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install()),
    options=options
)

ステップ3 – 製品HTMLを抽出する

アマゾンの商品詳細は、動的にレンダリングされ

コンテナ内にラップされている。このセクションが読み込まれるのを待ち、HTMLを抽出します:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 15)
product_container = wait.until(
    EC.presence_of_element_located((By.ID, "ppd"))
)

# Extract the full HTML of the product container
page_html = product_container.get_attribute("outerHTML")

このアプローチだ:

  • JavaScriptでレンダリングされたコンテンツ(価格や評価など)を待つ
  • ヘッダー、フッター、サイドバーを無視し、関連する製品セクションのみを対象とする。

PythonでAmazonの商品データをスクレイピングする方法の完全ガイドをご覧ください。

ステップ4 – HTMLをMarkdownに変換する

アマゾンのページには、LLMが処理するには非効率的な、深くネストされたHTMLが含まれています。重要な最適化は、このHTMLをきれいなMarkdownに変換することで、トークン数を劇的に減らし、理解度を向上させる。

スクリプトを実行すると、amazon_page.htmlと amazon_page.mdの2つのファイルが生成されます。両方をトークン計算ツールに貼り付けて、トークン数を比較してみてください。

以下に示すように、HTMLには約270,000のトークンが含まれている:

トークン計算機-html-トークン

Markdownバージョン?わずか~11,000トークン

トークン計算機-マークダウン-トークン

この96%の削減は、次のことにつながる:

  • コスト効率– トークンの数が少なければ少ないほど、APIコストやコンピュート・コストが低くなります。
  • 処理の高速化– 入力データが少ない=LLMの応答が速い
  • 精度の向上– よりクリーンでフラットなテキストは、モデルが構造化データをより正確に抽出するのに役立ちます。

AIエージェントがHTMLよりもMarkdownを好む理由については、こちらをお読みください。

ここでは、Pythonで変換を行う方法を説明する:

from markdownify import markdownify as md

clean_text = md(page_html, heading_style="ATX")

ステップ5 – データ抽出プロンプトの作成

よく構造化されたプロンプトは、LLMから一貫性のあるきれいなJSON出力を得るために重要です。以下は、定義済みのフォーマットで有効なJSONのみを返すようにモデルに指示するプロンプトです:

PROMPT = (
    "You are an expert Amazon product data extractor. Your task is to extract product data from the provided content. "
    "Return ONLY valid JSON with EXACTLY the following fields and formats:\n\n"
    "{\n"
    '  "title": "string – the product title",\n'
    '  "price": number – the current price (numerical value only)",\n'
    '  "original_price": number or null – the original price if available,\n'
    '  "discount": number or null – the discount percentage if available,\n'
    '  "rating": number or null – the average rating (0–5 scale),\n'
    '  "review_count": number or null – total number of reviews,\n'
    '  "description": "string – main product description",\n'
    '  "features": ["string"] – list of bullet point features,\n'
    '  "availability": "string – stock status",\n'
    '  "asin": "string – 10-character Amazon ID"\n'
    "}\n\n"
    "Return ONLY the JSON without any additional text."
)

ステップ6 – LLM APIを呼び出す

Ollamaがローカルで動作している場合、MarkdownテキストをHTTP API経由でLLaMAインスタンスに送ることができる:

import requests
import json

response = requests.post(
    "<http://localhost:11434/api/generate>",
    json={
        "model": "llama3.1:8b",
        "prompt": f"{PROMPT}\n\n{clean_text}",
        "stream": False,
        "format": "json",
        "options": {
            "temperature": 0.1,
            "num_ctx": 12000,
        },
    },
    timeout=250,
)

raw_output = response.json()["response"].strip()
product_data = json.loads(raw_output)

それぞれのオプションは何をするのか:

  • temperature– 0.1に設定すると、決定論的な出力が得られます(JSONフォーマットに最適です)。
  • num_ctx– コンテキストの最大長を定義します。12,000トークンは、ほとんどのアマゾンの商品ページで十分です。
  • streamFalseの場合、APIは処理後の完全なレスポンスを返す。
  • format– 出力形式(JSON)を指定します。
  • model– 使用するLLaMAのバージョンを示します。

変換されたMarkdownには通常約11,000トークンが含まれるため、それに応じてコンテキスト・ウィンドウ(num_ctx)を設定することが重要です。この値を増やすと長い入力を処理できるようになりますが、RAMの使用量が増え、処理が遅くなります。商品ページが特に長い場合や、それをサポートする計算リソースがある場合にのみ、コンテキストの上限を増やしてください。

ステップ7 – 結果を保存する

最後に、構造化された商品データをJSONファイルに保存する:

with open("product_data.json", "w", encoding="utf-8") as f:
    json.dump(product_data, f, indent=2, ensure_ascii=False)

ステップ8:スクリプトの実行

スクレイパーを実行するには、アマゾンの商品URLを指定し、スクレイピング機能を呼び出す:

if __name__ == "__main__":
    url = "<https://www.amazon.com/Black-Office-Chair-Computer-Adjustable/dp/B00FS3VJAO>"

    # Call your function to scrape and extract product data
    scrape_amazon_product(url)

ステップ9 – フルコードの例

以下は、すべてのステップを一貫したエンド・ツー・エンドのワークフローにまとめた完全なPythonスクリプトである:

import json
import logging
import time
from typing import Final, Optional, Dict, Any

import requests
from markdownify import markdownify as html_to_md
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager

# Configuration constants
LLM_API_CONFIG: Final[Dict[str, Any]] = {
    "endpoint": "<http://localhost:11434/api/generate>",
    "model": "llama3.1:8b",
    "temperature": 0.1,
    "context_window": 12000,
    "stream": False,
    "timeout_seconds": 220,
}

DEFAULT_PRODUCT_DATA: Final[Dict[str, Any]] = {
    "title": "",
    "price": 0.0,
    "original_price": None,
    "discount": None,
    "rating": None,
    "review_count": None,
    "description": "",
    "features": [],
    "availability": "",
    "asin": "",
}

PRODUCT_DATA_EXTRACTION_PROMPT: Final[str] = (
    "You are an expert Amazon product data extractor. Your task is to extract product data from the provided content. "
    "Return ONLY valid JSON with EXACTLY the following fields and formats:\n\n"
    "{\n"
    '  "title": "string - the product title",\n'
    '  "price": number - the current price (numerical value only),\n'
    '  "original_price": number or null - the original price if available,\n'
    '  "discount": number or null - the discount percentage if available,\n'
    '  "rating": number or null - the average rating (0-5 scale),\n'
    '  "review_count": number or null - total number of reviews,\n'
    '  "description": "string - main product description",\n'
    '  "features": ["string"] - list of bullet point features,\n'
    '  "availability": "string - stock status",\n'
    '  "asin": "string - 10-character Amazon ID"\n'
    "}\n\n"
    "Return ONLY the JSON without any additional text."
)

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[logging.StreamHandler()],
)

def initialize_web_driver(headless: bool = True) -> webdriver.Chrome:
    """Initialize and return a configured Chrome WebDriver instance."""
    options = Options()
    if headless:
        options.add_argument("--headless=new")

    service = Service(ChromeDriverManager().install())
    return webdriver.Chrome(service=service, options=options)

def fetch_product_container_html(product_url: str) -> Optional[str]:
    """Retrieve the HTML content of the Amazon product details container."""
    driver = initialize_web_driver()
    try:
        logging.info(f"Accessing product page: {product_url}")
        driver.set_page_load_timeout(15)
        driver.get(product_url)

        # Wait for the product container to appear
        container = WebDriverWait(driver, 5).until(
            EC.presence_of_element_located((By.ID, "ppd"))
        )
        return container.get_attribute("outerHTML")
    except Exception as e:
        logging.error(f"Error retrieving product details: {str(e)}")
        return None
    finally:
        driver.quit()

def extract_product_data_via_llm(markdown_content: str) -> Optional[Dict[str, Any]]:
    """Extract structured product data from markdown text using LLM API."""
    try:
        logging.info("Extracting product data via LLM API...")
        response = requests.post(
            LLM_API_CONFIG["endpoint"],
            json={
                "model": LLM_API_CONFIG["model"],
                "prompt": f"{PRODUCT_DATA_EXTRACTION_PROMPT}\n\n{markdown_content}",
                "format": "json",
                "stream": LLM_API_CONFIG["stream"],
                "options": {
                    "temperature": LLM_API_CONFIG["temperature"],
                    "num_ctx": LLM_API_CONFIG["context_window"],
                },
            },
            timeout=LLM_API_CONFIG["timeout_seconds"],
        )
        response.raise_for_status()

        raw_output = response.json()["response"].strip()
        # Clean JSON output if it's wrapped in markdown code blocks
        if raw_output.startswith(("```json", "```")):
            raw_output = raw_output.split("```")[1].strip()
            if raw_output.startswith("json"):
                raw_output = raw_output[4:].strip()

        return json.loads(raw_output)

    except requests.exceptions.RequestException as e:
        logging.error(f"LLM API request failed: {str(e)}")
        return None
    except json.JSONDecodeError as e:
        logging.error(f"Failed to parse LLM response: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Unexpected error during data extraction: {str(e)}")
        return None

def scrape_amazon_product(
    product_url: str, output_file: str = "product_data.json"
) -> None:
    """Scrape an Amazon product page and save extracted data along with HTML and Markdown to files."""
    start_time = time.time()
    logging.info(f"Starting scrape for: {product_url}")

    # Step 1: Fetch product page HTML
    product_html = fetch_product_container_html(product_url)
    if not product_html:
        logging.error("Failed to retrieve product page content")
        return

    # Optional: save HTML for debugging
    with open("amazon_product.html", "w", encoding="utf-8") as f:
        f.write(product_html)

    # Step 2: Convert HTML to Markdown
    product_markdown = html_to_md(product_html)

    # Optional: save Markdown for debugging
    with open("amazon_product.md", "w", encoding="utf-8") as f:
        f.write(product_markdown)

    # Step 3: Extract structured data via LLM
    product_data = (
        extract_product_data_via_llm(product_markdown) or DEFAULT_PRODUCT_DATA.copy()
    )

    # Step 4: Save JSON results
    try:
        with open(output_file, "w", encoding="utf-8") as json_file:
            json.dump(product_data, json_file, indent=2, ensure_ascii=False)
        logging.info(f"Successfully saved product data to {output_file}")
    except IOError as e:
        logging.error(f"Failed to save JSON results: {str(e)}")

    elapsed_time = time.time() - start_time
    logging.info(f"Completed in {elapsed_time:.2f} seconds")

if __name__ == "__main__":
    # Example usage
    test_url = (
        "<https://www.amazon.com/Black-Office-Chair-Computer-Adjustable/dp/B00FS3VJAO>"
    )
    scrape_amazon_product(test_url)

スクリプトが正常に実行されると、抽出された商品データがproduct_data.jsonという名前のファイルに保存されます。出力は次のようになります:

{
    "title": "Home Office Chair Ergonomic Desk Chair Mesh Computer Chair with Lumbar Support Armrest Executive Rolling Swivel Adjustable Mid Back Task Chair for Women Adults, Black",
    "price": 36.98,
    "original_price": 41.46,
    "discount": 11,
    "rating": 4.3,
    "review_count": 58112,
    "description": 'Office chair comes with all hardware and tools, and is easy to assemble in about 10–15 minutes. The high-density sponge cushion offers flexibility and comfort, while the mid-back design and rectangular lumbar support enhance ergonomics. All components are BIFMA certified, supporting up to 250 lbs. The chair includes armrests and an adjustable seat height (17.1"–20.3"). Its ergonomic design ensures a perfect fit for long-term use.',
    "features": [
        "100% mesh material",
        "Quick and easy assembly",
        "High-density comfort seat",
        "BIFMA certified quality",
        "Includes armrests",
        "Ergonomic patented design",
    ],
    "availability": "In Stock",
    "asin": "B00FS3VJAO",
}

ほら!ごちゃごちゃしたHTMLがきれいなJSONに生まれ変わる。これがウェブスクレイピングにおけるLLMのマジックだ。

ボット対策を克服する

上記のウェブスクレイピング・ボットを実行すると、CAPTCHAチャレンジのようなアマゾンのボット対策に遭遇する可能性が高い:

amazon-captcha-アンチボット・チャレンジ

これは重要な限界を浮き彫りにしている:私たちのLLaMAベースのワークフローはHTMLの解析に優れていますが、高度なボット対策が施されたサイトでは、コンテンツへのアクセスはまだ困難です

これを克服するには、Amazon CAPTCHAを回避し、その他のウェブスクレイピングの課題に対処する必要がある。

Bright Dataのスクレイピングブラウザは、従来のツールが失敗するような最も保護されたウェブサイトでも確実にロックを解除するなど、現代のウェブ環境の複雑さを処理するための専用ソリューションです。

さらに詳しくスクレイピング・ブラウザとヘッドレス・ブラウザの比較

ブライトデータスクレイピングブラウザを使う理由

Bright Data Scraping Browserは、最新のウェブスクレイピングプロジェクトをスケーリングするために構築された、プロキシインフラと高度なブロック解除機能を内蔵したヘッドレス、クラウドベースのブラウザです。Bright DataUnlockerスクレイピングスイートの一部です。

開発者やデータチームがこれを選ぶ理由は以下の通りだ:

  • 信頼できるTLSフィンガープリントとステルス回避技術
  • 150M以上のIPプロキシネットワークによる内蔵IPローテーション
  • 自動CAPTCHA解決
  • インフラストラクチャの削減 – コストのかかるクラウドのセットアップや継続的なメンテナンスが不要になります。
  • Playwright、Puppeteer、Seleniumのネイティブサポート
  • 大量データ抽出のための無制限のスケーラビリティ

最大の特徴は?わずか数行のコードで既存のワークフローに統合できる。

クラウドベースのウェブスクレイピングに移行する企業が増えている理由をお読みください。

スクレイピング・ブラウザの設定

スクレイピング・ブラウザを始めるには:

Bright Dataアカウントを作成し(新規ユーザーは支払い方法を追加した後、5ドルのクレジットを受け取ります)、ダッシュボードで「Proxies & Scraping」に進み、「Get started」をクリックします。

ブライトデータ・スクレイピング・ソリューション・ダッシュボード

新しいゾーン(例:test_browser)を作成し、プレミアムドメインや CAPTCHAソルバーなどの機能を有効にします。

brightdata-create-スクレイピング・ブラウザゾーン

次に、ダッシュボードからSeleniumのURLをコピーします。

brightdata-selenium-connection-credentials

スクレイピング・ブラウザ用にコードを修正する

スクレイピング・ブラウザ経由で接続するようにinitialize_web_driver関数を更新する:

from selenium.webdriver import Remote
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.chromium.remote_connection import ChromiumRemoteConnection

SBR_WEBDRIVER = "<https://username:password@host>:port"

def initialize_web_driver():
    options = ChromeOptions()
    sbr_connection = ChromiumRemoteConnection(SBR_WEBDRIVER, "goog", "chrome")
    driver = Remote(sbr_connection, options=options)
    return driver

これであなたのスクレイパーはブライトデータのインフラを経由し、Amazonやその他のボット対策システムを簡単に処理できるようになりました。

より高度な機能については、スクレイピング・ブラウザのドキュメントをご覧ください。

次のステップと代替案

LLaMAを搭載したスクレーパーの機能を拡張したり、他の実装を検討するために、以下の改善や代替案を検討してください。

  • スクリプトを再利用可能にする:柔軟に使用できるように、URLとプロンプトをコマンドライン引数として渡せるようにする。
  • 認証情報を保護するスクレイピング・ブラウザの認証情報を.envファイルに保存し、python-dotenvを使って安全にロードします。
  • 複数ページのサポートを追加する:複数ページをクロールし、ページネーションを処理するロジックを実装する。
  • より多くのウェブサイトをスクレイピング– Scraping Browserの検知防止機能を使って、他のeコマース・プラットフォームをスクレイピングする
  • Googleサービスからのデータ抽出Google FlightsGoogle SearchGoogle Trends専用のスクレイパーを構築するか、Bright DataのSERP APIを使用してすぐに使える検索データを取得します。

マネージド・ソリューションがお好きな方、あるいは他のLLM主導の方法を模索したい方は、以下のオプションが適しているかもしれません:

  1. ジェミニでスクレイピング
  2. 戸惑いながらのスクレイピング
  3. Crawl4AIとDeepSeekでAIスクレイパーを構築する

結論

このガイドでは、LLaMA 3を使用して弾力性のあるウェブスクレーパーを構築するための強固な基盤を提供します。大規模な言語モデルの推論機能と高度なスクレイピングツールを組み合わせることで、最小限の労力で複雑なウェブサイトから構造化データを抽出することができます。

ウェブスクレイピングにおいて、検知やブロックを避けることは最大の課題の一つです。Bright Data Scraping Browserは、ダイナミックレンダリング、フィンガープリンティング、アンチボットプロテクションを自動的に処理することで、この問題に対処します。これは、スケーラブルなデータ抽出をサポートするために設計された、より広範なツール群の一部です:

  • プロキシ・サービス1億5000万以上の家庭用IPにアクセスし、地域制限を回避
  • Web Scraper API– 専用エンドポイントを介して100以上の人気ウェブサイトから構造化データを抽出します。
  • Web Unlocking API– アンチスクレイピングシステムをバイパスして、あらゆるURLから完全にレンダリングされたHTMLを取得します。
  • SERP API– すべての主要検索エンジンからリアルタイムの検索結果を収集します。

今すぐ登録して、Bright Dataのスクレイピングおよびプロキシツール一式を無料でお試しください!

クレジットカードは必要ありません