Claude Codeと5ファイルでScreamingFrog風SEOクローラーを自作した話

スポンサーリンク

「Screaming Frogって高いし、自分の用途に合わせて改造もできない。Claude Codeに話しかけたら自作できるんじゃないか」——そんな思いつきから始めて、1セッションで動くSEOクローラーが完成した。実際に競合サイトを200ページクロールして競合分析にも使えた話を書く。

作ったもの(ScreamingFrogLite)の全体像

構成はシンプルに5ファイル。analyzer.pyでデータクラスとHTML解析、crawler.pyでBFS非同期クロール、exporter.pyでCSV/JSON出力、main.pyでCLIと進捗表示。依存ライブラリはhttpxbeautifulsoup4richlxmlの4つだけ。

取得できる情報はtitle/meta description(文字数込み)/H1/canonical/noindex/リダイレクトチェーン/レスポンスタイム/インリンク数。CSVはUTF-8 BOMつきで出力するのでExcelでそのまま開ける。

cd ~/screaming-frog-lite
source venv/bin/activate
python main.py https://example.com --max-pages 200 --delay 0.5

非同期クロールの設計で考えたこと

同期的にリクエストを投げると200ページで数分かかる。httpx.AsyncClientasyncio.Queueの組み合わせで並列リクエストを制御した。ワーカー数はデフォルト5で、Semaphoreではなくキューベースのワーカープールにした。

BFSのキュー設計で一番気を使ったのはqueue.task_done()のタイミング。新しいURLをキューに追加してからtask_done()を呼ばないと、queue.join()が早期リターンしてクロールが途中で終わる。これは実際に一度やらかして気づいた。

ハマったポイント:base_netloc問題

最初の設計ではurlparse(start_url).netlocをbase_netlocとして使っていた。たとえばhttp://example.comを入力するとexample.comがbase_netlocになる。

問題はhttp://example.com → https://www.example.comのようにwwwつきにリダイレクトするサイト。この場合base_netlocがexample.comなのに実際のリンクは全部www.example.comで張られているため、内部リンクが一切収集できない。

対処として、クロール開始前にhttpxでHEADリクエストを送って最終URLを解決し、そちらのnetlocをbase_netlocとして使うようにした。

実際に参考ページをクロールして出てきた発見

競合のサイトを200ページクロールしてみたら面白いことがわかった。meta descriptionが未設定のページが200件中92件(46%)。H1が複数あるページが59件(30%)。大きなSaaSサービスでもSEO上の問題が積み上がっている。

さらにセクション別のインリンク数を分析すると、/scene/(活用シーン)は平均インリンク2件、/seminar/は平均1件しかない。これは内部リンクがほぼなく、SEO的には孤立しているということ。公開されているデータから競合の弱点が見えてくる。

まとめ

Claude Codeに「Screaming Frogを自作しようぜ」と話しかけてから設計の相談・実装・動作確認まで1セッションで完成した。コード自体は全部で300行程度。非同期クロールの設計とbase_netloc問題さえ押さえれば、自分のユースケースに合わせて自由にカスタマイズできる道具になる。ソースはGitHubには上げていないが、興味ある人は同じプロンプトで試してみてほしい。

コメント

タイトルとURLをコピーしました