概要
Spider Rsは、Rustで実装された高速Webクローラーフレームワーク。非同期I/O処理とメモリ効率を両立させ、数千〜数万のページを効率的にクロール可能。サイト全体の構造を自動マッピングし、リンク抽出、メタデータ収集、SEO監査などの用途に適合。PyspiderやScrapyといったPythonクローラーの速度制限を回避する選択肢として機能する。
主な機能
- 非同期クロール処理:tokioランタイムを活用した並列化により、I/O待機時間を最小化。複数URLを同時処理でスループット向上
- 自動サイトマッピング:robots.txt解析と爬虫類フラグの尊重で、クロール対象範囲を自動判定
- レスポンス型フィルタリング:ステータスコード、Content-Type、リダイレクト先を条件に処理フロー分岐
- カスタムヘッダ・ユーザーエージェント設定:HTTPリクエスト層でのカスタマイズにより、Webサーバー側の検出回避に対応
- メモリ効率的なストリーミング解析:レスポンスボディ全体をメモリに読み込まず、ストリーム処理でヒープ圧迫を回避
- 出力形式の柔軟性:JSON、CSV、SQLite等の複数フォーマット対応で、ダウンストリームツールの連携が容易
- レート制限制御:遅延設定とコネクションプール管理で、ターゲットサーバーへの負荷を調整可能
クイックスタート
インストール
Cargoを使用したインストール(Rust 1.70以上推奨):
cargo add spider
またはプロジェクトのCargo.tomlに直接記述:
[dependencies]
spider = "1.2"
tokio = { version = "1", features = ["full"] }
最小構成の動作確認
以下のコードで、単一ドメイン配下のURLを全て抽出:
use spider::website::Website;
use spider::tokio;
#[tokio::main]
async fn main() {
let mut website = Website::new("https://example.com");
website.crawl().await;
for page in website.get_pages() {
println!("URL: {}, Status: {}", page.url, page.status_code);
}
}
詳細設定の例
クロール対象の制限、レート制定、カスタムヘッダを指定:
use spider::website::Website;
use std::time::Duration;
#[tokio::main]
async fn main() {
let mut website = Website::new("https://example.com")
.with_delay(Duration::from_millis(500)) // リクエスト間隔
.with_max_pages(1000) // クロール上限
.with_respect_robots_txt(true); // robots.txt準拠
website.crawl().await;
// ステータスコード200のみ処理
for page in website.get_pages() {
if page.status_code == 200 {
println!("Title: {:?}", page.title);
}
}
}
アーキテクチャ
graph LR
A["入力URL"] --> B["キューイング<br/>(BFS探索)"]
B --> C["HTTPリクエスト<br/>(tokio非同期)"]
C --> D["HTML解析<br/>(html5ever)"]
D --> E["リンク抽出<br/>&フィルタリング"]
E --> F{"クロール対象<br/>判定"}
F -->|対象内| B
F -->|対象外| G["スキップ"]
E --> H["メタデータ抽出<br/>(タイトル、説明など)"]
H --> I["出力フォーマット変換<br/>(JSON/CSV/SQLite)"]
I --> J["結果出力"]
フローの特徴:
- B→C間:キューに登録された全URLが非同期タスクとしてスポーンされ、I/O待機中に他タスクが進行
- D→E間:HTMLパーサーがストリーム処理でメモリ効率を確保
- F分岐:robots.txt、URLパターン、ステータスコードで対象判定、不要なクロールを削減
競合ツールとの比較
| 項目 | Spider Rs | Scrapy(Python) | Colly(Go) |
|---|---|---|---|
| 言語 | Rust | Python | Go |
| 非同期モデル | tokio(async/await) | Twisted(コールバック) | goroutine |
| メモリ効率 | 極低 | 中程度 | 低 |
| 起動速度 | 高速(ネイティブバイナリ) | 遅い(インタプリタ) | 高速 |
| 学習曲線 | 急(Rustの型安全が必須) | 緩(Pythonの知識で対応) | 中程度 |
| robots.txt自動準拠 | ○ | △(プラグイン依存) | △(手動実装) |
| 出力形式 | JSON/CSV/SQLite | JSONカスタマイズ | JSON手動実装 |
| プロダクション実績 | 中程度 | 豊富 | 中程度 |
Spider Rsが優れている点:
- メモリ使用量の削減が必須の環境(エッジサーバー、埋込系)
- 数万ページ規模で速度を重視するケース
- 単一バイナリ配布が要件の場合
欠点:
- Rustの習熟が必須(型チェック、ライフタイム管理)
- データ抽出ロジックが複雑な場合、Pythonの方がプロトタイピングが速い
実践的な使い方
ユースケース1:SEOサイト監査の全ページ収集
特定ドメイン配下の全URLを抽出し、HTTPステータス・レスポンスタイムを記録。404ページを検出するシナリオ。
use spider::website::Website;
use std::collections::HashMap;
#[tokio::main]
async fn main() {
let mut website = Website::new("https://example.com")
.with_max_pages(10000)
.with_respect_robots_txt(true);
website.crawl().await;
let mut status_counts: HashMap<u16, usize> = HashMap::new();
for page in website.get_pages() {
*status_counts.entry(page.status_code).or_insert(0) += 1;
if page.status_code == 404 {
println!("[404] {}", page.url);
}
}
println!("\n=== ステータスコード集計 ===");
for (code, count) in status_counts.iter() {
println!("{}: {}", code, count);
}
}
実行結果の例:
[404] https://example.com/old-page
[404] https://example.com/deleted
=== ステータスコード集計 ===
200: 9850
301: 45
404: 105
ユースケース2:複数サイトの並列クロールと結果集約
複数ドメインを同時クロールし、結果をJSONで出力。異なるドメインへのクロール跳躍を防止。
use spider::website::Website;
use tokio::task;
#[tokio::main]
async fn main() {
let domains = vec![
"https://site1.com",
"https://site2.com",
"https://site3.com",
];
let mut handles = vec![];
for domain in domains {
let handle = task::spawn(async move {
let mut website = Website::new(domain)
.with_max_pages(500);
website.crawl().await;
let page_count = website.get_pages().count();
(domain, page_count)
});
handles.push(handle);
}
for handle in handles {
if let Ok((domain, count)) = handle.await {
println!("Domain: {}, Pages: {}", domain, count);
}
}
}
ユースケース3:特定URLパターンのフィルタリング
API エンドポイント(/api/)を除外し、公開ページのみクロール。カスタムフィルター関数で選別。
use spider::website::Website;
#[tokio::main]
async fn main() {
let mut website = Website::new("https://example.com");
// 手動でフィルタリング(Spider Rs標準機能)
website.crawl().await;
let mut public_pages = vec![];
for page in website.get_pages() {
// /api/ を除外
if !page.url.contains("/api/") && page.status_code == 200 {
public_pages.push(page.clone());
}
}
println!("Public pages: {}", public_pages.len());
for page in public_pages.iter().take(10) {
println!(" {}", page.url);
}
}
まとめ
Spider Rsは、大規模サイトのクロール速度がボトルネックとなっているチーム向けのツール。メモリ効率と並列処理性能で、Pythonベースのクローラーを圧倒。特に以下の環境で価値を発揮:
- SEO監査自動化:数千ページ規模のサイトマップ生成が分単位で完了
- エッジコンピューティング:組込サーバーやCloudflareWorkers環境での軽量動作
- CI/CDパイプライン:単一バイナリ配布で依存関係なし
- 高頻度クロール:リソース制約下での定期実行
注意点:
- Rustの型安全性の習熟が必須。型エラーは開発初期に多発
- 非同期処理のデバッグは複雑(トレース情報の取得にtracing crateが必要)
- データ抽出ロジックが複雑な場合、Scrapyのミドルウェア機構の方が柔軟
選定基準:「速度とメモリが最優先」ならSpider Rsを、「保守性と学習コストの低さ」ならScrapyを推奨。両方の長所を活かすハイブリッド戦略(Rust製Webサーバー上でPythonスクリプトを実行)も検討価値がある。