Python Playwrightでログイン付きECサイトの商品情報を全件スクレイピング

Python Playwrightでログイン付きECサイトの商品情報を全件スクレイピング

公開: 更新:
CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座

ログインが必要な練習用ECサイトから、全8ページ・96件の商品情報をPythonとPlaywrightで丸ごと取り出してみました。ログインの待ち方、ページ送り、価格や評価の数値化まで、実際に動いたコードをそのまま分解して解説します。

PythonでPlaywrightのコードを生成したプロンプトと作り方

まず「どこを見れば何が取れるか」をプロンプトに全部書きました。ログインフォームのセレクタ、一覧のURLパターン、カード内のクラス名まで具体的に渡すと、LLMが架空のセレクタを想像で埋める余地がなくなり、一発で動くコードになりやすくなります。

次に、ログイン付きサイトで最も失敗しやすい2点、つまり「送信後の遷移待ち」と「カード描画待ち」を、expect_navigationとwait_for_selectorという実際のAPI名で名指ししました。ここを曖昧にすると、待たずに空のDOMを読んで0件になるコードが返ってきます。

最後に「詳細ページは巡回しない」という禁止条件と、CSV/JSON保存+集計表示という出口を指定しています。やらないことを書くのは、相手にスクレイピングの節度を守らせる一番手軽な方法です。

Python生成プロンプトで工夫したポイント

  • 送信ボタンのクリックはページ遷移の完了を待ってから次へ進む(with page.expect_navigation(): の中でクリックする):ログイン処理が壊れる原因のほぼ全ては遷移待ちの欠落。APIの使い方まで指定すると、click直後に旧DOMを読む定番バグを構造的に潰せる。
  • page.wait_for_selector('a.card')でカードの描画を待ってから、カード1枚ずつ...取り出す:待たずにquery_selector_allすると例外も出ずに0件が返る=気づけない失敗になる。待機を明示させることで「静かな失敗」を防げる。
  • 「¥1,280」のような価格は正規表現で数字だけを取り出してintに:実際の表示文字列の例を渡すのが効く。通貨記号やカンマ・★印を含む生テキストのまま保存されるのを防ぎ、そのまま集計できるデータになる。
  • 商品詳細ページは巡回しない(必要な項目は一覧カードに全部あるため、無駄なリクエストを増やさない):やらないことを明示しないと96ページ分の追加巡回を勝手に足してくる。速度と相手サーバへの負荷の両方を守る指示。

同じPythonコードを作れる生成プロンプト(コピペ用・全文)

実際の生成時には、この記事のシステム都合の指定(図の保存先など)を少しだけ足していますが、以下はそのままコピペして同じものが作れるように整えた全文です。

PythonとPlaywright(同期API)で、ログインが必要な練習用ECサイト「Sample Mart」から商品情報を全件スクレイピングするスクリプトを書いてください。 【対象サイト】 - ログインページ: https://sample-mart-practice.trends-cc.workers.dev/login - 資格情報: ユーザー名demo / パスワードpractice-pass - ログインフォーム: input#username、input#password、button[type="submit"] - 商品一覧: https://sample-mart-practice.trends-cc.workers.dev/products?page=1〜page=8(12件/ページ・全96件) - 商品カードはa.card。カード内に .cat(カテゴリ)、.pname(商品名)、.price(価格)、.meta .star(評価)があり、hrefは /product/<id> の形。 【やること】 1. Chromiumを起動し(動きを見たいのでheadless=Falseでも可)、ログインページを開く。 2.ユーザー名とパスワードを入力し、送信ボタンのクリックはページ遷移の完了を待ってから次へ進む(with page.expect_navigation(): の中でクリックする)。 3.ログイン後のセッションを保ったまま、商品一覧の1ページ目から8ページ目まで順に巡回する。 4.各ページでpage.wait_for_selector('a.card')でカードの描画を待ってから、カード1枚ずつ 商品ID・商品名・カテゴリ・価格・評価・詳細ページURLを取り出す。 5.「¥1,280」のような価格は正規表現で数字だけを取り出してintに、「★4.5」のような評価はfloatに変換する。商品IDはhrefの末尾から切り出し、それをキーに重複を除く。 6.商品詳細ページは巡回しない(必要な項目は一覧カードに全部あるため、無駄なリクエストを増やさない)。 7.取得した全件をproducts.csvとproducts.jsonに保存し、最後に「取得件数」「カテゴリ別の件数」「価格の最小・最大・平均」をコンソールに表示する。 【コードの条件】 - 使うライブラリはplaywright(同期API)と標準ライブラリのみ。 - URLや資格情報は先頭の定数にまとめ、差し替えやすくする。 - 「価格の正規化」「1ページ分のカード抽出」「全体の流れ」を関数に分け、処理の段落ごとに日本語コメントを入れる。

Python・Playwrightの環境構築(Windows 11/PowerShellで検証)

この記事のセットアップ手順と掲載コードは、Windows 11 Pro、PowerShell 5.1、Python 3.13.3、Playwright 1.55.0で動作確認しています。仮想環境を有効化せず、その中のPythonを直接指定するため、以下のコマンドはPowerShellとコマンドプロンプト(cmd)の両方で使えます。

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install playwright==1.55.0
.\.venv\Scripts\python.exe -m playwright install chromium

macOS・Linuxでは仮想環境内のPythonパスが異なります。今回の動作確認環境とは異なるため、以下は環境差分を補う参考手順です。

python3 -m venv .venv
./.venv/bin/python -m pip install playwright==1.55.0
./.venv/bin/python -m playwright install chromium
  • -m pip install playwrightはPythonパッケージを、-m playwright install chromiumはChromium本体をインストールします。両方を実行してください。
  • 今回使用したPlaywright 1.55.0Python 3.9以上が必要です。この記事ではPython 3.13.3で確認しています。
  • sync_playwright()は通常の.pyスクリプト向けです。Jupyterなど実行中のイベントループ内では非同期APIを使います。
  • 練習用サイト以外へ応用する場合は、利用規約・robots.txt・アクセス頻度を必ず確認すること。

Python Playwrightでログイン付きECを全件取得する方針

今回の題材は、練習用に用意したECサイト「Sample Mart(自作の練習用EC)」です。ログインしないと商品一覧が見られない作りになっていて、一覧は1ページ12件・全8ページに分かれています。

やることはシンプルで、ログイン→一覧を1ページずつ巡回→カードから情報を抽出、の3段構えです。この順番を守るだけで、認証付きサイトのスクレイピングはかなり素直に書けます。

なぜPlaywrightかというと、ログインフォームの入力やボタンのクリック、画面遷移の待機といった「人間がブラウザでやる操作」をそのままコードにできるからです。今回は同期APIを使い、上から下へ読める手続き的なコードにしました。

読者のみなさんも同じサイト(https://sample-mart-practice.trends-cc.workers.dev)にログインdemo / practice-passで入れるので、同じコードをそのまま動かして追体験できます。外部の実サイトを無断で叩く必要はありません。

このセクションの用語

スクレイピング
Webページを機械的に開いて、そこに表示されている情報をプログラムで取り出す作業のことです。
Playwright
ブラウザを自動操作するライブラリです。Pythonから実際のChromeなどを起動して、クリックや入力を再現できます。
同期API
処理を上から順番に実行していく書き方のことです。Playwrightには非同期版もありますが、初心者は同期版のほうが読みやすいです。
セッション
ログイン状態を保持する仕組みです。ログイン後に発行されるCookieをブラウザが持ち続けることで「ログイン済みの自分」として扱われます。
ログインから全件抽出までの処理の流れ ログイン画面を開くID・パスワードを入力して送信遷移完了を待つ(セッション確立)/products?page=Nを1〜8まで巡回カードから商品情報を抽出・重複排除
ログインから全件抽出までの処理の流れ

Pythonでの準備とSample Martのページ構造

必要なのはPythonとPlaywrightだけです。pip install playwrightのあとにplaywright installを実行して、操作対象のブラウザ本体をダウンロードしておきます。この2段階を忘れると、実行時にブラウザが見つからずエラーになります。

コード側で使う標準ライブラリre(正規表現)のみ。表示用の文字列を数値へ直す用途に使います。

このセクションの用語

セレクタ
HTMLの中から特定の要素を指し示す書き方です。.cardならclass名がcardの要素、input#usernameならid名がusernameの入力欄を指します。
正規表現
文字列のパターンを表す記法です。「数字だけ取り出す」のような処理を短く書けます。Pythonではreモジュールで扱います。
検証ツール
ブラウザに標準搭載された開発者向け機能で、表示中のページのHTML構造やclass名を確認できます。

Pythonで対象ページのHTML構造を先に読む

スクレイピングはコードを書く前に「どのセレクタを掴むか」を決める作業が半分です。ブラウザの検証ツールで一覧ページを覗くと、商品1件がa.cardというリンク要素になっているのが分かります。

  • a.card … 商品カード全体。href/product/1のような詳細URLが入る
  • .pname … 商品名
  • .cat … カテゴリ(衣料・家電など)
  • .price … 「¥1,280」形式の価格表示
  • .meta .star … 「★ 4.5」形式の評価表示

Pythonでは設定を先頭にまとめる

対象URL・資格情報・巡回範囲(1〜8ページ)はコード冒頭の定数にまとめました。あとから対象を変えたくなったとき、書き換える場所が1か所で済みます。


Python研修一覧はこちら

目的に合うPython研修を一覧形式から探したい方は、ぜひご利用ください。

Python研修を比較する

Java研修一覧はこちら

目的に合うJava研修を一覧形式から探したい方は、ぜひご利用ください。

Java研修を比較する

PHP研修一覧はこちら

目的に合うPHP研修を一覧形式から探したい方は、ぜひご利用ください。

PHP研修を比較する

新入社員研修

目的に合う新入社員研修を一覧形式から探したい方は、ぜひご利用ください。

新入社員研修を比較する

全ての研修からも探したい方はこちら

Python Playwrightのログイン処理と全件巡回コードを解説

コード全体は3つのパートでできています。表示テキストを数値に直すヘルパー群、1ページ分のカードを抜き出す_collect_cards、そして全体を組み立てるrunです。

runの中では、ログイン→1ページ目取得→2ページ目以降のループ、という流れがそのまま上から並びます。処理の順番と画面の動きが1対1で対応しているので、途中で止めてどこまで進んだか追いやすいのが利点です。

特に肝になるのは「待つ」処理です。Playwrightは要素が操作可能になるまで自動で待ってくれますが、ページ遷移の完了だけは明示的に待たないと、まだログイン前のDOMを掴んで空振りします。

このセクションの用語

DOM
ブラウザがHTMLを読み込んで組み立てた、ページの構造データのことです。スクレイピングはこのDOMから値を読み取ります。
Cookie
サイトがブラウザに預ける小さなデータで、ログイン状態の保持に使われます。Playwrightはこれを自動で保持してくれます。
重複排除
同じデータを2回以上記録しないようにする処理です。今回は商品IDを集合(set)に入れて判定しています。
ヘルパー関数
本筋の処理から細かい仕事を切り出した小さな関数のこと。読みやすさと使い回しのために分けます。
カード1枚がdictになるまでのデータの流れ a.cardを全件取得hrefから商品IDを切り出すseen_idsで重複チェック名前・カテゴリ・価格・評価を抽出productsリストへ追加
カード1枚がdictになるまでのデータの流れ
"""Sample Mart(練習用EC)にログインし、全8ページの商品を全件スクレイピングする。"""

import re

# 設定(対象サイト・資格情報・巡回範囲)

BASE_URL = "https://sample-mart-practice.trends-cc.workers.dev"
LOGIN_URL = f"{BASE_URL}/login"
USERNAME = "demo"
PASSWORD = "practice-pass"
FIRST_PAGE = 1
LAST_PAGE = 8


# 表示用テキストを数値へ正規化するヘルパー

def _parse_price(text):
    """「¥1,280」のような表示から数値だけを取り出してintにする。"""
    digits = re.sub(r"[^0-9]", "", text or "")
    return int(digits) if digits else None


def _parse_rating(text):
    """「★ 4.5」のような表示から小数を取り出してfloatにする。"""
    m = re.search(r"\d+(?:\.\d+)?", text or "")
    return float(m.group()) if m else None


def _text_of(card, selector):
    """カード内の要素テキストを取り出す(無ければ空文字)。"""
    el = card.query_selector(selector)
    return el.inner_text().strip() if el else ""


# 1ページ分の商品カードを抽出する

def _collect_cards(page, products, seen_ids):
    for card in page.query_selector_all("a.card"):
        href = card.get_attribute("href") or ""
        product_id = href.rstrip("/").split("/")[-1]
        if not product_id or product_id in seen_ids:
            continue
        seen_ids.add(product_id)
        products.append(
            {
                "id": product_id,
                "name": _text_of(card, ".pname"),
                "category": _text_of(card, ".cat"),
                "price": _parse_price(_text_of(card, ".price")),
                "rating": _parse_rating(_text_of(card, ".meta .star")),
                "url": f"{BASE_URL}{href}",
            }
        )


# メイン処理(ログイン → 全ページ巡回 → 全件抽出)

def run(page, on_step=None):
    def step(name):
        if on_step:
            on_step(name)

    # ログイン画面を開く
    page.goto(LOGIN_URL, wait_until="domcontentloaded")
    page.wait_for_selector("input#username")
    step("ログイン画面")

    # 資格情報を入力し、遷移完了を待ってログインする
    page.fill("input#username", USERNAME)
    page.fill("input#password", PASSWORD)
    with page.expect_navigation():
        page.click('button[type="submit"]')

    # 1ページ目の商品一覧を取得する(ここでセッションが効いているか分かる)
    products = []
    seen_ids = set()
    page.goto(f"{BASE_URL}/products?page={FIRST_PAGE}", wait_until="domcontentloaded")
    page.wait_for_selector("a.card")
    _collect_cards(page, products, seen_ids)
    step("ログイン後の商品一覧(1ページ目)")

    # 2ページ目以降を最終ページまで巡回して積み上げる
    for page_no in range(FIRST_PAGE + 1, LAST_PAGE + 1):
        page.goto(f"{BASE_URL}/products?page={page_no}", wait_until="domcontentloaded")
        page.wait_for_selector("a.card")
        _collect_cards(page, products, seen_ids)
        if page_no == FIRST_PAGE + 1:
            step("次のページへ遷移(2ページ目)")

    # 最終ページまで到達し、全件が揃った状態
    step(f"抽出完了・最終ページ({LAST_PAGE}ページ目 / 全{len(products)}件)")

    return products


# --- 読者向けの単体実行・保存処理 ---

def _save_products(products, out_dir):
    """取得結果をCSVとJSONへ保存する。"""
    import csv
    import json
    from pathlib import Path

    output = Path(out_dir)
    output.mkdir(parents=True, exist_ok=True)
    fields = list(products[0].keys()) if products else []
    with (output / "products.csv").open("w", encoding="utf-8-sig", newline="") as file:
        writer = csv.DictWriter(file, fieldnames=fields)
        writer.writeheader()
        writer.writerows(products)
    (output / "products.json").write_text(
        json.dumps(products, ensure_ascii=False, indent=2), encoding="utf-8")


def _print_summary(products):
    """取得件数と、存在する列の簡単な集計を表示する。"""
    import json
    from collections import Counter

    print(f"取得件数: {len(products)}件")
    categories = [row.get("category") for row in products if row.get("category")]
    if categories:
        print("カテゴリ別件数:", json.dumps(dict(Counter(categories)), ensure_ascii=False))
    prices = [row.get("price") for row in products if isinstance(row.get("price"), (int, float))]
    if prices:
        print(f"価格: 最小={min(prices)} / 最大={max(prices)} / 平均={sum(prices) / len(prices):.2f}")


def main():
    """Chromiumを起動し、取得・保存・集計までを単体で実行する。"""
    import os
    from playwright.sync_api import sync_playwright

    headless = os.environ.get("TSUKU_HEADLESS", "").lower() in {"1", "true", "yes"}
    out_dir = os.environ.get("TSUKU_OUTPUT_DIR", ".")
    with sync_playwright() as playwright:
        browser = playwright.chromium.launch(headless=headless)
        try:
            page = browser.new_page(viewport={"width": 1440, "height": 900})
            products = run(page)
        finally:
            browser.close()
    _save_products(products, out_dir)
    _print_summary(products)


if __name__ == "__main__":
    main()

コード全文は上の折り畳みに入れてあるので、全部を上から読む必要はありません。ここでは特に重要な部分だけを抜き出して、何をしているのか順番に見ていきます。

Python Playwrightのexpect_navigationでログイン遷移を待つ

page.fill("input#username", USERNAME)
page.fill("input#password", PASSWORD)
with page.expect_navigation():
    page.click('button[type="submit"]')

page.fillで2つの入力欄を埋め、送信ボタンをクリックします。ポイントはwith page.expect_navigation():ブロックの中でクリックしていること。これでページ遷移が完了するまで処理が止まり、ログイン後の画面が確実に出来上がってから次に進めます。

Pythonでログイン後はpage.gotoだけでセッションが効く

page.goto(f"{BASE_URL}/products?page={FIRST_PAGE}", wait_until="domcontentloaded")
page.wait_for_selector("a.card")
_collect_cards(page, products, seen_ids)

認証が通るとブラウザコンテキストがCookieを保持するので、以降は普通にpage.gotoでURLを開くだけでログイン済み扱いになります。Cookieを自分で運ぶコードは一切要りません。wait_for_selectorで商品カードの描画を待ってから抽出に入ります。

Pythonのquery_selector_allで12枚のカードを取り出す

for card in page.query_selector_all("a.card"):
    href = card.get_attribute("href") or ""
    product_id = href.rstrip("/").split("/")[-1]
    if not product_id or product_id in seen_ids:
        continue
    seen_ids.add(product_id)

query_selector_allはセレクタに一致する要素を全部リストで返します。商品IDはhref(例:/product/1)の末尾を切り出して取得し、seen_idsという集合に入れて重複排除のキーにします。ページ送りで同じ商品が二度出ても、これで1件しか記録されません。

Pythonの正規表現で「¥1,280」をintに正規化する

def _parse_price(text):
    """「¥1,280」のような表示から数値だけを取り出してintにする。"""
    digits = re.sub(r"[^0-9]", "", text or "")
    return int(digits) if digits else None

画面に出ている価格は「¥1,280」という文字列で、そのままでは計算に使えません。re.subで数字以外を全部消してからintに変換します。空文字だった場合はNoneを返すので、表示が欠けている商品があってもクラッシュしません。

Pythonで評価を小数のまま取り出す

def _parse_rating(text):
    """「★ 4.5」のような表示から小数を取り出してfloatにする。"""
    m = re.search(r"\d+(?:\.\d+)?", text or "")
    return float(m.group()) if m else None

評価は「★ 4.5」のように記号付きで出ているので、re.searchで最初に見つかった数値部分だけを抜きます。\d+(?:\.\d+)?は「整数、あれば小数点以下も」という意味で、4でも4.5でも拾えます。

Pythonで要素が無いときに落ちないヘルパー

def _text_of(card, selector):
    """カード内の要素テキストを取り出す(無ければ空文字)。"""
    el = card.query_selector(selector)
    return el.inner_text().strip() if el else ""

カード内の要素は必ずしも全部揃っているとは限りません。見つからなければNoneが返るので、そのままinner_text()を呼ぶと例外になります。存在チェックを挟んで空文字を返すことで、1件の欠損で全体が止まる事故を防いでいます。

Pythonで1〜8ページを順に巡回して積み上げる

for page_no in range(FIRST_PAGE + 1, LAST_PAGE + 1):
    page.goto(f"{BASE_URL}/products?page={page_no}", wait_until="domcontentloaded")
    page.wait_for_selector("a.card")
    _collect_cards(page, products, seen_ids)

ページ送りは「次へ」ボタンをクリックしても良いのですが、URLに?page=Nがあるならループで直接開くほうが単純で確実です。2ページ目から最終ページまで同じ処理を繰り返し、productsリストに積み上げていきます。

参考:

©Playwright公式ドキュメント(Auto-waiting)

Playwright performs a range of actionability checks on the elements before making actions to ensure these actions behave as expected.


Python基礎・実践(Django)

企業・法人向けのPython研修では、基礎から応用まで体系的に学べます。

Python研修の詳細

DX社員研修

企業・法人向けのDX研修では、実務に繋がるリスキリングでITレベルを向上させます。

DX研修の詳細

Javaエンジニア育成研修

企業・法人向けのJavaエンジニア育成研修では、Javaの基礎から応用まで確実に習得できます。

Java研修の詳細

新卒・新入社員向け研修

企業・法人に新入社員・新卒社員に向けたプログラミング研修を提供しています。

新入社員研修の詳細

コードキャンプのIT研修を全て見る

Pythonで96件を取得できたPlaywrightの実行結果

実際に動かしたところ、ログイン画面から始まって全8ページを巡回し、96件のデータを取得できました。1ページ12件×8ページの計算がそのまま合っています。

ブラウザを表示したまま実行すると、ログインフォームが埋まり、送信され、一覧がめくられていく様子がそのまま見えます。うまく動かないときはこの「見える状態」で流すのが一番の近道です。

取得したデータは、商品IDをキーに名前・カテゴリ・価格・評価・詳細URLがひとまとまりになったdictの配列になります。ここまで来ればCSVにもJSONにも好きに書き出せます。

このセクションの用語

dict
Pythonの辞書型で、「キー:値」の組で情報をまとめて持てるデータ構造です。1商品を1つのdictで表しています。
ヘッドレス
ブラウザ画面を表示せずに裏側だけで動かすモードのこと。逆に画面を出して動かすと、動作確認がしやすくなります。
id name / category price rating
1 大容量Tシャツ / 衣料 5700 4.3
2 エコ加湿器 / 家電 550 3.4
ログイン画面
ログイン画面
ログイン後の商品一覧(1ページ目)
ログイン後の商品一覧(1ページ目)
次のページへ遷移(2ページ目)
次のページへ遷移(2ページ目)
抽出完了・最終ページ(8ページ目 / 全96件)
抽出完了・最終ページ(8ページ目 / 全96件)
実行中のブラウザ録画(実セッション・自動再生)
実行中のブラウザ録画(実セッション・自動再生)
id name category price
1 大容量Tシャツ 衣料 5700
2 エコ加湿器 家電 550
3 高耐久入門書 書籍 4450
4 軽量写真集 書籍 3300
5 折りたたみヘッドホン 家電 4000

Python Playwrightスクレイピングでよくあるエラーと対処

ここからは、この手のコードを書くときに一般に遭遇しやすいエラーと、その原因・対処をまとめます。多くは「待っていない」か「セレクタが違う」のどちらかに集約されます。

このセクションの用語

TimeoutError
待機処理が制限時間内に条件を満たせなかったときに出る例外です。Playwrightの既定は30秒です。
NoneType
値が「無い」状態を表すPythonの型です。要素が見つからなかったときに返ってくるので、そのまま操作すると例外になります。
エラー例 原因 対処
Executable doesn't exist at ... pip install playwrightはしたが、ブラウザ本体を入れていない playwright installを実行してブラウザをダウンロードする
TimeoutError: Timeout 30000ms exceeded waiting for selector セレクタ名が違う、またはログインできておらず要素が存在しない 検証ツールでclass名を再確認し、ログイン後のDOMを実際に見て確かめる
AttributeError: 'NoneType' object has no attribute 'inner_text' query_selectorが要素を見つけられずNoneを返したのに、そのままメソッドを呼んだ _text_ofのように存在チェックを挟み、無ければ空文字を返す
ValueError: invalid literal for int() with base 10: '¥1,280' 「¥」や「,」が混じった文字列をそのままint()に渡した re.sub(r"[^0-9]", "", text)で数字以外を除いてから変換する
ログイン後のはずが一覧が空になる expect_navigationで遷移を待たず、ログイン前のDOMを掴んでいる 送信クリックをwith page.expect_navigation():ブロックで囲む

Python初心者がつまずきやすい待機とセレクタの落とし穴

エラーメッセージが出ないのに件数が合わない、というのが一番厄介なパターンです。原因のほとんどは待機とセレクタなので、先に押さえておきましょう。

このセクションの用語

domcontentloaded
HTMLの解析が終わった時点を指すブラウザのイベントです。画像やJavaScriptによる後追いの描画はまだ終わっていない場合があります。
待機(wait)
目的の要素や状態が整うまでプログラムを一時停止させることです。スクレイピングの安定性はここでほぼ決まります。
POINT

ページ遷移は自動では待ってくれません。クリックの直後にDOMを読むコードを書くと、古い画面を掴んだまま進んでしまいます。遷移を伴う操作はwith page.expect_navigation():で囲むのが鉄則です。

wait_until="domcontentloaded"はHTMLの読み込み完了までしか待ちません。中身の描画を確実に待つには、page.wait_for_selector("a.card")のように「欲しい要素が出るまで」を明示的に待つのが安全です。

セレクタは検証ツールで自分の目で確認してください。.carda.cardのように、タグ名が付くかどうかだけで一致件数が変わることがあります。

重複排除のキーは、名前ではなく商品IDのように一意な値を使います。同名の別商品があると、名前をキーにした瞬間にデータが消えます。

巡回対象のページ数を決め打ちする場合は、サイト側のページ数が変わったときに取りこぼす可能性があります。件数が想定と合っているかを毎回チェックする癖をつけましょう。

Pythonのログイン付きスクレイピングが役立つ実務の場面

ログインの壁を越えられると、スクレイピングで扱える対象が一気に広がります。社内システムや管理画面など、「自分にアクセス権があるのに一括ダウンロード機能がない」データが対象になるからです。

対象は必ず自社サイトや権限のあるシステム、または今回のような練習用サイトに限ってください。他社サイトを無断で自動巡回するのは規約違反やサーバー負荷の問題につながります。

このセクションの用語

E2Eテスト
実際のブラウザ操作を通して、ユーザーと同じ経路でアプリが動くか確かめるテストです。Playwrightは元々この用途のライブラリです。
アサーション
「ここではこうなっているはず」という条件をコードで宣言し、違えばエラーにする仕組みです。
使える場面 具体的な使い方
自社ECの在庫・価格の定期チェック 今回のコードのループ部分をそのまま流用し、毎朝1回全件を取得してCSVに保存。前日分と差分を取れば、意図しない価格変更や在庫切れをすぐ検知できます。
管理画面からのデータ吸い出し CSVエクスポート機能がない社内ツールにexpect_navigationでログインし、一覧テーブルをquery_selector_allで全件取得。手作業のコピペを丸ごと置き換えられます。
Webアプリの動作確認(E2Eの入り口) ログイン→一覧表示→件数チェックという流れは、そのまま回帰テストになります。「96件取れるはず」をアサーションにすれば、画面崩れやログイン不具合に気づけます。
自作サイトのデータ移行 旧サイトの全ページを巡回してdictの配列を作り、JSONに書き出してから新システムへ流し込む。DBに直接触れない場合の現実的な移行手段になります。

PythonとPlaywrightで全件取得して分かったこと

ログイン付きサイトのスクレイピングは、身構えるほど難しくありませんでした。要は「遷移を待つ」「セッションはブラウザに任せる」「欲しい要素が出るまで待つ」の3つです。

今回はSample Martで全8ページ・96件をきちんと取り切れました。expect_navigationでログイン遷移を待ち、wait_for_selectorで描画を待ち、正規表現で表示テキストを数値に直す——このは他のサイトにもほぼそのまま持っていけます。

次の一歩としては、取得したdictの配列をCSVやJSONに書き出したり、pandasに流し込んでカテゴリ別の平均価格を出したりするのが面白いはずです。まずは同じサイトで手を動かしてみてください。

このセクションの用語

ブラウザコンテキスト
Playwrightが管理する、Cookieやセッションを共有するブラウザの作業空間のことです。ここが認証状態を持ち続けてくれます。
  • ログイン送信はwith page.expect_navigation():で囲んで遷移完了を待つ
  • 認証後のCookieはブラウザコンテキストが保持するので、あとはpage.gotoだけでよい
  • 抽出前にwait_for_selectorで対象要素の描画を待つ
  • 価格や評価は正規表現でintやfloatに正規化してから保存する
  • 商品IDを集合で管理し、ページ送りでの重複を防ぐ

参考にした一次情報

  1. ^ Sample Mart(自作の練習用EC)(この記事のスクレイピング練習サイト). https://sample-mart-practice.trends-cc.workers.dev, (参照26-07-13).
  2. ^ Playwright for Python — 公式ドキュメント. https://playwright.dev/python/docs/intro, (参照26-07-13).
  3. ^ Playwright Python: Navigations(expect_navigationと遷移待ち). https://playwright.dev/python/docs/navigations, (参照26-07-13).
  4. ^ Playwright Python: Auto-waiting(要素待ちの仕組み). https://playwright.dev/python/docs/actionability, (参照26-07-13).
  5. ^ Playwright Python APIリファレンス: Page. https://playwright.dev/python/docs/api/class-page, (参照26-07-13).

※内容は執筆時点のものです。ライブラリやサイトの仕様は変わる可能性があるため、公式ドキュメントもあわせてご確認ください。

ITやプログラミングに関するコラム


ITやプログラミングに関するニュース

ブログに戻る

コメントを残す

コメントは公開前に承認される必要があることにご注意ください。

企業・法人向けのIT・プログラミング・生成AI研修を探す、比較する - IT・プログラミングを知って学べるコネクトメディア CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプが提供する無料で学べるプログラミングスクール講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア 3.5日の研修で、年間1,600時間の削減効果が見込まれる。東京きらぼしフィナンシャルグループのDX人材育成事例 - IT・プログラミングを知って学べるコネクトメディア 配属3ヶ月で30%の生産性向上を実現するいよぎんコンピュータサービスの新人研修に迫る - IT・プログラミングを知って学べるコネクトメディア 金融業界の業務効率化を加速するニッセイアセットマネジメントの生成AI×GAS活用研修事例 - IT・プログラミングを知って学べるコネクトメディア 【製造業のDX人材育成事例】デジタル人材の即戦力化を実現する、日本ガイシ株式会社の異動者向オンボーディング研修 - ITやプログラミングを知って学べるコネクトメディア フューチャーアーキテクト株式会社が実現した新入社員向けIT研修プログラムでタスクフォース制度が主体的な学びと成長を生み出す - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【IT新入社員研修】オンラインとオフラインの最適バランスを実現したFutureOneの導入事例 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【新入社員研修】柔軟なハイブリッド型Java研修で実現した新卒20名の成長と成果|サークレイス株式会社 - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/現場により近いところにデジタルを根付かせるDX基礎講座研修|株式会社ブリヂストン - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/業務の効率化・DX推進に向けたIT人材育成への第一歩|株式会社カナエ - ITやプログラミングを知って学べるコネクトメディア 企業・法人向けのIT・プログラミング研修 - ITやプログラミングを知って学べるコネクトメディア

新着記事

対象者別で探す

子供(小学生・中学生・高校生)向け
プログラミング教室検索する

子供(小学生・中学生・高校生)がロボットやプログラミング言語を学ぶことができるオフラインからオンラインスクールを検索、比較することが可能です。

子供(小学生・中学生・高校生)
プログラミング教室検索する

ITやプログラムなどの
最新情報を検索する

日々、新しいITやプログラミング言語の情報が流れていきますが、特定の情報を時系列でニュースやコラムを確認することができます。

ITやプログラムなどの
最新情報を検索する