walk — サイト巡回(BFS / DFS)
Paprika の walk() / Walker でサイトを BFS/DFS で巡回する完全リファレンス — start_url / target_pages / same_domain / allowed_domains / allow_paths / deny_paths / order / max_depth / per_page_timeout_s / persist_state / host_dedup / recrawl_patterns、Visit のフィールド、ベストプラクティス。
walk() はサイトを BFS / DFS で巡回しながら 1 ページずつ Visit を yield する非同期イテレータです。キュー・重複除去・ドメインとパスのフィルタ・オフスコープ redirect 対応まで内部で処理するので、自前の BFS ループより堅牢です。
全体像は Client インストール / ガイド / 目的別の例は ユースケース。
最小例
import asyncio
from paprika_client import paprika, walk
async def main():
async with paprika() as cli:
async with cli.session("https://example.com", parent_job_id="crawl") as page:
async for visit in walk(page, target_pages=50, same_domain=True):
print(f"[{visit.n}/{visit.target}] depth={visit.depth} {visit.url}")
await page.save_assets("out/images")
asyncio.run(main())
walk(page, **opts)はVisitを yield する AsyncIterator。クラス版はWalker(ステートを覗きたいとき)。- ループ中で
await page.save_assets(...)などを呼べば、そのページのアセットを保存できます。
walkは async のみ。sync_paprikaから直接は使えません(asyncio.run()でラップしてください)。
オプション一覧(完全版)
| 引数 | 型 | 既定 | 意味 |
|---|---|---|---|
start_url |
str |
現在 URL | 巡回の起点。絶対 URL が必須(省略時は page.url から取る) |
target_pages |
int |
100 |
訪問ページ数の上限(達したら停止) |
same_domain |
bool |
True |
開始 URL と同じドメインだけを巡回 |
allowed_domains |
Iterable[str] \| None |
None |
明示的に許可するドメインのリスト(指定すると same_domain より優先) |
allow_paths |
Iterable[str] \| None |
None |
パスの正規表現許可リスト |
deny_paths |
Iterable[str] \| None |
None |
パスの正規表現除外リスト |
deny_defaults |
bool |
True |
既定の除外パターンを使う(管理画面・ログイン・カートなどを除外) |
order |
"bfs" \| "dfs" \| "random" |
"bfs" |
探索順 |
max_depth |
int \| None |
None |
開始 URL からのリンク階層の上限(None = 無制限) |
per_page_timeout_s |
float |
30.0 |
1 ページあたりの上限(秒) |
handle_modal_each_page |
str \| None |
None |
各ページで割り込み画面に対処する agent ゴール |
persist_state |
bool \| str |
True |
進捗を parent job に永続化(attempt 跨ぎで再開可能)。str を渡すと保存キー名になる |
host_dedup |
bool |
False |
ホストを跨いだ既訪 URL の重複除去(同じ URL を別ホストで再訪しない) |
recrawl_patterns |
Iterable[str] \| None |
None |
host_dedup 有効時に再訪を許可する URL glob パターン(カテゴリ一覧などに使う) |
Visit のフィールド
walk() が yield するのは Visit インスタンス。ページの読み込みに成功したときだけ yield されます(失敗・オフドメイン・フィルタ除外は無音でスキップ)。
| フィールド | 型 | 説明 |
|---|---|---|
visit.n |
int |
1 始まりの成功訪問番号 |
visit.target |
int |
walker に渡した target_pages(進捗表示用) |
visit.url |
str |
実際今いる URL(リダイレクト後) |
visit.requested_url |
str |
walker が page.goto() に渡した URL |
visit.depth |
int |
開始 URL からのリンク階層(start_url は 0) |
visit.outline |
str |
ページのアウトライン(walker が事前取得済み・再フェッチ不要) |
visit.page |
Page |
そのページに居る Page ハンドル(ループ外から閉包しなくて良い) |
進捗を表示する典型:
async for visit in walk(page, target_pages=200):
print(f"[{visit.n}/{visit.target}] depth={visit.depth} {visit.url}")
巡回範囲の絞り込み
同じドメインだけ
walk(page, target_pages=200, same_domain=True)
複数ドメインを許可
walk(
page,
target_pages=500,
allowed_domains=["example.com", "cdn.example.com"],
)
パスを許可・除外(正規表現)
walk(
page,
target_pages=300,
allow_paths=[r"^/articles/"],
deny_paths=[r"^/admin/", r"\.pdf$"],
)
deny_defaults=True(既定)のときは、管理画面・ログイン・カート・印刷ビューなどの経験則的な除外もあわせて適用されます。
階層・順序
walk(page, target_pages=300, max_depth=2, order="dfs")
失敗の扱い
walk() は成功した訪問だけ yield します。失敗ページ・オフドメイン redirect・フィルタ除外は walker が無音でスキップするので、ループ内で握りつぶす必要はありません。
各ページ内の処理が落ちた場合だけ try で囲ってください:
async for visit in walk(page, target_pages=300):
try:
await page.save_assets("out/images")
except Exception as e:
print(f" skip {visit.url}: {e}")
attempt 跨ぎで再開
persist_state=True(既定)のとき、walker の進捗(crawled / queue)は parent job のディスクに保存されます。途中で止まっても、同じ parent_job_id で再開すれば続きから走ります。
async with cli.session("https://example.com", parent_job_id="crawl-001") as page:
async for visit in walk(page, target_pages=10000, persist_state=True):
...
クラス版 Walker
walk() は内部で Walker を作っています。ステートを覗きたい / 別の場所で組み立てたい場合に使います。
from paprika_client import Walker
walker = Walker(page, target_pages=200, same_domain=True)
async for visit in walker:
...
print(len(walker.crawled), "pages crawled")
print(len(walker.queue), "still in queue")
ベストプラクティス
target_pagesを明示する — 無限に走らせないための基本。max_depthで浅く保つ — フッタの外部リンクから無関係なドメインに迷い込まないように。- ループ内のサンプル数は
visit.nを使う — 自分でi++カウントしない(dedup でスキップされるとずれる)。 requested_urlとurlの食い違いに注意 — 強制リダイレクトされたページを記録するならvisit.urlを、リンク元 URL を残したいならvisit.requested_urlを。- 長時間クロールは
persist_state=True— 失敗時に最初からやり直さずに済みます。