Seleniumでリンク切れを自動検出|PythonでQA自動化

テスト自動化

📌 この記事はこんな人向けです

  • 🔧 QAエンジニア → リンク切れ検出を自動化してテスト工数を削減したい人
  • 🔍 SEO担当者 → リンク切れを放置してSEO評価が下がるのを防ぎたい人
  • ⚙️ テスト自動化エンジニア → SeleniumとPythonで実務レベルのツールを作りたい人

🔥 この記事を読むと得られること

  • SEOに悪影響を与える「リンク切れ」を自動で検出できる
  • 手動チェックが完全に不要になる
  • 404だけでなく4xx/5xx全ステータスに対応したチェックが実装できる
  • QA・テスト自動化・SEO対策の3つ全部に使える実務レベルのコードが手に入る

👤

この記事を書いた人:QAエンジニアとしてSelenium・Python・テスト自動化に実務で取り組んでいます。本記事のコードは実際の業務で使用しているツールをベースにしており、GitHubで全コードを公開中です。

Webサイトの運用で地味に困るのがリンク切れ(404エラー)の存在です。手動でチェックするのは時間がかかりすぎる、でも放置するとSEO評価や信頼性にも影響する——そんな悩みを解決するために作ったQA自動化ツールが LinkChecker です。

この記事では Selenium × Python で実装したリンクチェッカーのコードを、設計の意図・各メソッドの役割・ハマりポイントまで含めて徹底解説します。実行結果のサンプルやCSV出力例も掲載しているので、すぐに動かしてみてください。



00. リンク切れがSEOに与える影響

「リンク切れはユーザーが困るだけ」と思われがちですが、実はSEOへの影響も深刻です。壊れたリンクはサイトの品質・検索評価・ユーザー体験の全てに悪影響を与えます。

⚠️ Googleの公式見解: クロールできないページや壊れたリンクが多いサイトは、サイト全体の品質評価が下がる可能性があります。リンク切れの放置はSEOにとってリスクです。
📉
クロール効率の低下

Googlebot がリンク切れに遭遇するとクロールバジェットを無駄消費し他のページが巡回されにくくなる

サイト評価の低下

404ページが多いサイトはGoogleから「品質が低い」と判断されSEO評価が下がる可能性がある

😞
UX悪化・直帰率上昇

リンク切れに遭遇したユーザーはすぐ離脱。直帰率の上昇が間接的にSEOにも悪影響を与える

⚠️ 対策: リンク切れを定期的に自動チェックして即座に修正することで、SEO評価の低下を未然に防げます。このツールはその自動化を実現します。

01. 実行結果サンプル(動くか一目で確認)

実際に動かすと、URL・ステータス・結果が一覧で出力されます。リンク切れをパッと見で判断できる形式です。

URL ステータス 結果
https://example.com 200 ✅ OK
https://example.com/about 200 ✅ OK
https://example.com/recruit 404 ❌ Not Found
https://example.com/old-page 410 ❌ Gone
https://example.com/contact 200 ✅ OK

ターミナルにも同様の形式でリアルタイム出力されます。

=== 実行結果例 ===
[チェック中] /top    → 200 OK
[チェック中] /about  → 200 OK
[チェック中] /404    → 404 Not Found  ← リンク切れ検出!
[チェック中] /old    → 410 Gone       ← 削除済みページ検出!

=== サマリー ===
総リンク数: 89  /  エラーリンク数: 3
📸 スクリーンショット保存済み
📊 CSV出力済み → Desktop/LinkChecker/

実際に筆者の環境で動かした結果がこちらです。総リンク数128件中、エラーリンク2件を検出し、CSVとスクリーンショットが自動保存されました。

Seleniumリンクチェッカー ターミナル実行結果 総リンク数128 エラーリンク数2

▲ 実際のターミナル出力。128件チェックし2件のエラーリンクを検出。CSVとSSが自動保存される

エラー検出時は以下のようにエラーページのスクリーンショットが自動保存されます。

404エラー検出時のスクリーンショット例

▲ 404エラー検出時に自動保存されるスクリーンショット

💡 実務での運用: 実務ではこの結果をログやCSVとして保存し、定期的にチェックするケースが多いです。週次・月次で自動実行することで、リンク切れを早期発見→即修正のサイクルが回せます。

自動生成されるCSVレポート(保存して使える)

実務では「結果を残すこと」が必須です。このツールはチェック完了後にCSVを自動生成するので、バグチケットへの添付・SEO担当者への共有・修正作業の優先度付けにそのまま使えます。

リンクテキスト,URL,ステータスコード,キャプチャパス,チェック時刻
トップページ,https://example.com,200,,2026-03-19 14:30:01
会社概要,https://example.com/about,200,,2026-03-19 14:30:03
採用情報,https://example.com/recruit,404,screenshots/404_採用情報.png,2026-03-19 14:30:22
旧ページ,https://example.com/old-page,410,screenshots/410_旧ページ.png,2026-03-19 14:30:24
お問い合わせ,https://example.com/contact,200,,2026-03-19 14:30:25

実際に生成されたCSVをExcelで開くとこのように表示されます。リンクテキスト・URL・ステータスコード・スクリーンショットのパスが一覧で確認でき、そのままバグチケットに添付できます。

Seleniumリンクチェッカー CSVレポート Excelで開いたエラーリンク一覧 ステータスコード404

▲ 自動生成されたCSVをExcelで開いた様子。エラーリンクのURL・ステータス・SSパスが整理されている

📊 全件CSV
全リンクの結果を一覧で保存。統計・分析に活用できる
❌ エラー専用CSV
エラーのみ抽出して別ファイルで保存。修正作業がすぐ始められる
💡 本記事では1ページを対象にリンクチェックを行いますが、このロジックは複数ページやサイト全体のチェックにも応用可能です。実務ではクローラーと組み合わせることでサイト全体のリンクチェックにも発展させることができます。

02. なぜ Selenium + requests の組み合わせなのか?

「Seleniumだけでいいのでは?」と思う方も多いかもしれません。実はこれ、Selenium単体ではHTTPステータスコードを直接取得できないという問題があります。

💡 実務での正解はこれ

Seleniumはリンク抽出・DOM操作ステータス確認はrequestsを使うのが実務的な分担です。この使い分けができているだけで「分かっているエンジニア」の印象になります。

ツール 得意なこと 苦手なこと
Selenium DOM操作・JS実行・Cookie処理・ページ描画 HTTPステータスコードの直接取得
requests HTTPステータスの高速チェック・軽量 JS認証・Cookie処理・動的コンテンツ
💡 初心者向けポイント: SeleniumはブラウザのDOM操作ツールです。「ページを開いてボタンをクリックする」のは得意ですが「このURLのHTTPステータスは?」という質問には直接答えられません。それを補うのがrequestsライブラリです。

01. 対応しているエラーステータス一覧

「リンク切れ = 404」と思われがちですが、実務では4xx/5xx全体を対象にするのが一般的です。このツールは以下のステータスを検出します。

ステータス 意味 対応
404 ページが存在しない(典型的なリンク切れ) ✅ 検出・SS撮影
410 ページが恒久的に削除された ✅ 検出・SS撮影
500 サーバー内部エラー ✅ 検出・SS撮影
502/503/504 ゲートウェイ・サービス利用不可 ✅ 検出・SS撮影
403 アクセス制限(ページ自体は存在する) ⏭️ スキップ(正常扱い)
200/301/302 正常・リダイレクト ✅ 正常と判定

02. 環境構築と必要なライブラリ

# 必要なライブラリを一括インストール
pip install selenium requests
🐍
Python 3.8+

Windows/Mac両方で動作。クロスプラットフォーム対応

🌐
selenium 4.6+

ChromeDriver自動検出に対応。Service()引数なしで起動できる

📡
requests

HTTPステータスの高速チェックに使用。大量リンクに対応


03. クラス構造と責務の設計

# 使い方はたったの3行
checker = LinkChecker("https://example.com")
results = checker.run_check()
checker.close()
class LinkChecker:
    __init__            # 初期化・出力先・WebDriver起動・エラーリスト初期化
    │
    ├── setup_output_directory   # フォルダ作成
    ├── setup_driver             # Chrome オプション設定・ドライバー起動
    │
    ├── run_check                # ★ メインループ(全体制御)
    │   ├── get_all_links        # Selenium でリンク収集
    │   ├── check_link_status    # HTTP ステータス確認
    │   └── take_screenshot      # エラーページ SS 撮影
    │
    ├── save_results             # CSV 保存
    └── close                    # WebDriver 終了

04. __init__ と初期設定

コンストラクタ

def __init__(self, base_url, output_dir=None):
    if output_dir is None:
        desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
        output_dir = os.path.join(desktop_path, "LinkChecker")

    self.base_url = base_url
    self.output_dir = output_dir
    self.setup_output_directory()
    self.setup_driver()
    self.error_links = []

setup_driver — Chrome オプション

カテゴリ オプション例 目的
ボット検出回避 --disable-blink-features=AutomationControlled 自動操作であることを悟られないようにする
ログ完全抑制 --log-level=3 / --silent ターミナル出力をツール自身のログのみにする
UA偽装 Windows Chrome の UserAgent を設定 クローラーブロックを回避
WebDriver隠蔽 navigator.webdriver を undefined に書き換え JSレベルのボット検出を無効化
⚠️ 注意: ボット検出回避の手法は、サイトの利用規約によっては問題になる場合があります。自社サイトや許可を得たサイトへの使用に限定してください。

05. get_all_links — リンクの収集戦略

  1. ページアクセス & 初期待機time.sleep(2) で動的コンテンツのロードを待機
  2. Cookie ポップアップ処理handle_cookie_popup() でGDPR同意ダイアログを自動クリック
  3. 全 a タグを取得find_elements(By.TAG_NAME, "a") でHTTPで始まるURLのみをフィルタリング
  4. 要素情報を事前保存(Stale Element対策) — location/size/XPathなどを dict として保存
  5. テキストなしリンクの補完title → alt → aria-label の順にフォールバック取得

Stale Element 対策

💡 Stale Element とは: Selenium で要素を取得した後にDOMが更新されると、以前取得した要素参照が無効化される現象です。要素の属性情報を dict に前もってコピーしておくことで回避しています。
# ❌ ダメな例
elements = driver.find_elements(By.TAG_NAME, "a")
do_something()
elements[0].click()  # ← StaleElementReferenceException 発生!

# ✅ 良い例:情報を先にdictに退避
element_data = {
    'location': element.location,
    'classes':  element.get_attribute("class") or "",
    'id':       element.get_attribute("id") or "",
    'xpath':    self.get_element_xpath(element)
}

06. check_link_status — 二段階ステータス確認

手法 メリット デメリット
requests.head() ボディ取得なしで高速・軽量 HEAD を拒否するサーバーがある
requests.get() ほぼ全サーバーが対応 ボディ取得分で低速
check_with_selenium() JS認証・リダイレクトも正確に判定 最も低速
# まず HEAD リクエストで軽量チェック
response = requests.head(url, timeout=8, allow_redirects=True, headers=headers)
status_code = response.status_code

# 400番台(403/404以外)はGETで再確認
if 400 <= status_code < 500 and status_code not in [403, 404]:
    response = requests.get(url, timeout=8, allow_redirects=True, headers=headers)
    return response.status_code

07. take_screenshot — エビデンス収集

📸
エラーページ SS

404/500等のエラーページをキャプチャ。404_テキスト_タイムスタンプ.png で保存

🔴
エラー前 SS(BEFORE)

元ページに戻り問題リンクを赤枠ハイライト+「ERROR LINK」バナーをJS注入してキャプチャ

# 要素に赤枠・グローエフェクトを適用
element.style.cssText += `
    border: 3px solid #ff0000 !important;
    box-shadow: 0 0 15px rgba(255, 0, 0, 0.8) !important;
`;

# 画面上部に固定バナーを追加
var label = document.createElement('div');
label.innerHTML = 'ERROR LINK: ' + linkText.substring(0, 30);
label.style.cssText = `
    position: fixed; top: 20px; left: 50%;
    background: #ff0000; color: white; padding: 10px 20px;
`;

実際に生成されるBEFOREスクリーンショットがこちらです。問題のあるリンクが赤枠でハイライトされ、上部に「ERROR LINK」バナーが自動挿入されるため、どのリンクが原因かが一目でわかります。

Seleniumリンクチェッカー エラーリンク赤枠ハイライト ERROR LINKバナー自動生成 BEFORE SS

▲ エラーリンクを赤枠でハイライト+「ERROR LINK」バナーをJS注入してキャプチャ。証拠として残せる


08. handle_cookie_popup — GDPR 対応

cookie_selectors = [
    "//button[contains(text(), 'Accetta tutti i cookie')]",  # イタリア語
    "//button[contains(text(), 'Accept')]",                   # 英語
    "//button[contains(text(), '同意')]",                     # 日本語
    "//button[contains(@class, 'accept')]",                   # クラスベース
]
self.driver.execute_script("arguments[0].click();", button)

09. save_results — CSV レポート出力

ファイル 内容 用途
link_check_result_*.csv 全リンクのチェック結果 全体確認・統計
error_links_*.csv エラーのあったリンクのみ バグレポート添付・修正作業
# utf-8-sig = BOM付きUTF-8(Excelで文字化けを防止)
with open(csv_path, 'w', newline='', encoding='utf-8-sig') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=results[0].keys())
    writer.writeheader()
    writer.writerows(results)

error_count = sum(1 for r in results if r['ステータスコード'] in [404, 410, 500, 502, 503])
print(f"✅ 正常リンク : {len(results) - error_count}件")
print(f"❌ エラーリンク: {error_count}件")
💡 実務ポイント: エラーカウントをログとして残しておくことで、前回チェックとの比較が簡単になります。「先週より404が3件増えた」といったトレンド管理もできるようになります。

10. よくあるエラーと対処法

① ChromeDriver のバージョン不一致

# 対処法:selenium 4.6+ にアップグレードで自動解決
pip install --upgrade selenium

② TimeoutException — ページが読み込めない

self.driver.set_page_load_timeout(30)  # 15秒 → 30秒に延長

③ aタグが0件しか取得できない

time.sleep(5)  # 2秒 → 5秒に増やして再試行

11. 大量URLをチェックする場合の高速化

現状のツールは1件ずつ順番にチェックするため、100件を超えてくると処理時間が長くなります

① concurrent.futures で並列処理

from concurrent.futures import ThreadPoolExecutor, as_completed

def check_links_parallel(links, max_workers=10):
    results = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        future_to_link = {
            executor.submit(check_link_status, link['url']): link
            for link in links
        }
        for future in as_completed(future_to_link):
            link = future_to_link[future]
            status = future.result()
            results.append({'url': link['url'], 'status': status})
    return results

② タイムアウト設定とリトライ処理

def check_with_retry(url, max_retries=3, timeout=8):
    for attempt in range(max_retries):
        try:
            response = requests.head(url, timeout=timeout, allow_redirects=True)
            return response.status_code
        except requests.exceptions.Timeout:
            if attempt < max_retries - 1:
                time.sleep(2)
    return 0
💡 目安: 100件なら通常約5分 → 並列10スレッドで約30秒に短縮できます。

12. さらに良くするための改善アイデア

並列処理で高速化

concurrent.futures で並列実行。1000件のリンクを高速チェック可能に

🌐
全ページクロール機能

内部リンクを再帰的にたどることでサイト全体を一括チェック可能に

🔁
リトライ処理の強化

タイムアウト時に自動リトライ。一時的なサーバーエラーによる誤検知を防ぐ

🚫
除外リンクの設定

mailto:tel: をスキップリストに追加して不要なチェックを省く


13. ハマりポイント

実装中に実際につまずいた箇所をまとめました。同じところでハマる方の参考になれば嬉しいです。


① Seleniumだけではステータスコードが取れない

「Seleniumだけでリンク切れチェックできるはず!」と思ってコードを書き始めましたが、SeleniumはDOM操作は得意でもHTTPステータスコードを直接取得する機能がありません。

解決策として requests ライブラリと組み合わせる方法にたどり着きました。

# ❌ SeleniumだけではHTTPステータスが取れない
# ✅ requestsと組み合わせて解決
response = requests.head(url, timeout=8, allow_redirects=True)
status_code = response.status_code

💡 ポイント: Seleniumとrequestsをそれぞれの得意領域で使い分けることが実務の正解です。


② StaleElementReferenceException が発生する

リンクを取得した後にDOMが更新されると、取得済みの要素が無効になり StaleElementReferenceException が発生します。最初はなぜエラーになるのか全く理解できませんでした。

解決策は要素の情報を事前にdictに保存しておくことです。

# ❌ ダメな例:後から要素を操作しようとするとエラー
elements = driver.find_elements(By.TAG_NAME, "a")
do_something()
elements[0].click()  # ← StaleElementReferenceException!

# ✅ 良い例:先にdictに情報を退避
element_data = {
    'href': element.get_attribute("href"),
    'text': element.text,
}

💡 ポイント: 要素参照を後から使うのではなく、必要な情報を取得した直後にdictへ保存する習慣をつけると安定します。


③ aタグが0件しか取得できない

find_elements(By.TAG_NAME, "a") を実行しても0件しか取れないケースが発生しました。原因はJavaScriptで動的に生成されるコンテンツのロード待ちが足りていなかったことでした。

# ❌ 0件になることがある
driver.get(url)
elements = driver.find_elements(By.TAG_NAME, "a")

# ✅ 待機時間を増やして解決
driver.get(url)
time.sleep(5)  # 2秒 → 5秒に増やす
elements = driver.find_elements(By.TAG_NAME, "a")

⚠️ 注意: time.sleep() の値はサイトの読み込み速度によって調整が必要です。重いサイトでは10秒以上必要なケースもあります。


④ HEADリクエストを拒否するサーバーがある

requests.head() で軽量チェックをしようとしたところ、サーバーによってはHEADメソッドを拒否して405エラーを返すことがありました。

解決策はHEADが失敗したらGETで再確認する二段階チェックにすることです。

# まずHEADで軽量チェック
response = requests.head(url, timeout=8)

# 400番台が返ってきたらGETで再確認
if 400 <= response.status_code < 500:
    response = requests.get(url, timeout=8)

💡 ポイント: HEAD → GET のフォールバック構成にすることで、サーバーの違いによる誤検知を防げます。


⑤ ChromeDriverのバージョン不一致

Chromeをアップデートした後にスクリプトを実行したら突然動かなくなりました。原因はChromeとChromeDriverのバージョンが合わなくなったことでした。

# ✅ seleniumを4.6以上にアップグレードで自動解決
pip install --upgrade selenium

💡 ポイント: Selenium 4.6以降はChromeDriverを自動で管理してくれるので、この問題が根本解決されます。バージョン管理の手間がなくなるため、まず最新版にアップグレードすることをおすすめします。

14. まとめ

スクリプトを実行すると、screenshotsフォルダにエラーページSS(404_)とエラー前SS(BEFORE_)がセットで自動保存されます。ファイル名にリンクテキストとタイムスタンプが入るので、後から見返す際も迷いません。

Seleniumリンクチェッカー screenshotsフォルダ 404エラーSS BEFORE画像 自動保存 ファイル一覧

▲ 自動保存されるscreenshotsフォルダの中身。エラーページSS(404_)とエラー前SS(BEFORE_)がセットで保存される

  • Seleniumはリンク抽出・DOM操作requestsはステータス確認という役割分担が実務の正解
  • 404だけでなく 4xx/5xx全体 を対象にするのが実務的なアプローチ
  • エビデンスとしてエラー前後のスクリーンショットを自動生成できる
  • 結果はExcel対応CSVで自動出力されるのでバグチケットにそのまま添付できる
  • SEO改善 → リンク切れを定期監視して検索評価の低下を防げる
  • CI/CDに組み込む → リリース前に自動でリンクチェックが走る品質ゲートが作れる
  • 定期監視 → cronやタスクスケジューラと組み合わせて週次・月次で自動実行できる

このツールの活用シーンと拡張例

🔍
SEO改善に使える

リンク切れはGoogleの評価を下げる要因。週次・月次で定期実行し常に健全なサイトを維持できる

🧪
QAテストに使える

リリース前のリンク品質チェックをテストスイートに組み込み。エビデンスのSSも自動生成される

⚙️
CI/CDに組み込める

GitHub ActionsやJenkinsに組み込めばリリース前に自動でリンクチェックが走る品質ゲートが作れる

📅
定期監視できる

cronやWindowsタスクスケジューラと組み合わせて週次・月次で自動実行。問題を早期発見できる

🚀 今後の拡張例

  • 全ページクロール対応 → 内部リンクを再帰的にたどり、サイト全体を一括チェック
  • 並列処理(concurrent.futures) → 1000件のURLを高速処理
  • 定期実行 + Slack通知 → cronで自動実行し、エラー検出時にSlackへ即時通知
💡 QAエンジニアとして一言: このツールは「リンク切れを見つける」という単一目的に特化した設計が素晴らしいです。Seleniumの複雑さをクラスに閉じ込め、呼び出し側はシンプルに保たれているため、チームへの展開や改修もしやすい構造です。SEO・QA・CI/CDの全てに使えるので、ぜひGitHubのコードも参考にしてみてください!
タイトルとURLをコピーしました