一覧の一ページ目だけ取得できても、二ページ目のURLが分からなければ収集は途中で止まります。ページ番号を推測して増やす前に、HTMLに次ページのリンクがあるかを確認します。
次ページリンクをたどる例では、終端の条件と、同じURLへ戻ったときの停止条件を両方決めます。表示上は次へ進んでいても、リンクの誤りで同じページを巡回する場合があるためです。
次ページのhrefを探す
次は架空の一ページ目に置くリンクです。relのnextは、このページに続く文書を示すリンク関係です。全てのサイトがこの属性を使うわけではありません。
<h2 class="entry-title">資料A</h2>
<a rel="next" href="page-2.html">次へ</a>
二ページ目には次の内容を置き、次ページリンクを付けません。
<h2 class="entry-title">資料B</h2>
一ページ目をpage-1.html、二ページ目をpage-2.htmlとしてUTF-8で保存します。先頭にはmeta要素などでUTF-8を指定したHTMLの文書構造を付けて使えます。ローカルで試すなら、そのフォルダーでHTTPサーバーを起動します。
py -m http.server 8000 --bind 127.0.0.1
同じオリジンのページだけをたどる
requestsとbeautifulsoup4が使える環境で、次を実行します。最大三ページまで、リダイレクトを自動で追わない例です。
import time
from urllib.parse import urljoin, urlsplit, urldefrag
import requests
from bs4 import BeautifulSoup
url = "http://127.0.0.1:8000/page-1.html"
origin = urlsplit(url)
visited = set()
for _ in range(3):
url = urldefrag(url).url
parts = urlsplit(url)
if parts.scheme != origin.scheme or parts.netloc != origin.netloc:
raise ValueError("別のオリジンへの移動を止めました")
if url in visited:
raise ValueError("同じページを再訪しようとしました")
visited.add(url)
response = requests.get(url, timeout=(3, 10), allow_redirects=False)
if response.status_code != 200:
raise RuntimeError(f"取得を止めました: HTTP {response.status_code}")
soup = BeautifulSoup(response.content, "html.parser")
titles = [item.get_text(strip=True) for item in soup.select("h2.entry-title")]
print(url, titles)
next_link = soup.select_one("a[rel~='next'][href]")
if next_link is None:
break
url = urljoin(response.url, next_link["href"])
time.sleep(1)
else:
raise RuntimeError("次ページが残ったまま上限に達しました")
入力例では資料A、資料Bの順に表示して終了します。最後のページでnextが見つからないことを、終端として扱っています。途中の構造変更でリンクが消えても同じ終了になるため、実運用では件数や既知のページ構造との照合も必要です。
相対URLを結合しても取得先は保証されない
urljoinはpage-2.htmlのような相対URLを取得したページのURLと結合します。hrefが絶対URLなら、結合先のホストが置き換わることもあります。結合できたことだけでは、意図した取得先とは判断できません。
この例では次のループでschemeとnetlocを開始URLに照合し、別のホストやポート、方式へ進む前に止めています。フラグメントを外すのは、同じ文書内の位置だけが違うURLで再訪判定をすり抜けないためです。
一方、クエリの順序違いや別名URLまで同一視する処理は入れていません。ページごとの識別方法は取得先の仕様に合わせます。HTMLのbase要素で基準URLが指定されるサイトも、この例の対象には含めていません。
三ページと一秒はこの例の設定
上限三ページと一秒の間隔は、手元の小さな例で動作を追うための値です。取得許可や十分な負荷対策を保証する値ではありません。実際の利用条件と応答に合わせて、停止や待機を判断します。
動作確認では、二ページ目から一ページ目へ戻すリンク、外部URLへのnext、終端のない四ページを用意すると、再訪・取得先・上限の各条件を確かめられます。途中まで表示できても、例外で止まった結果を全件取得済みとして扱わないことも確認します。
関連する作業
次ページ要素を絞るセレクタと、アクセス間隔の考え方は次の記事で確認できます。
