7


3

スパイダーリング中のURLの保存

URLを収集するために使用しているPythonで小さなWebスパイダーを作成しました。 私はコンテンツに興味がありません。 現時点では、スパイダーがURLに2回アクセスするのは望ましくないため、アクセスしたすべてのURLをメモリ内のセットに保持しています。 もちろん、これを達成するための非常に限られた方法です。

では、訪問したURLを追跡する最良の方法は何ですか?

データベースを使用する必要がありますか?

  • どれ? MySQL、SQLite、PostgreSQL?

  • URLを保存するにはどうすればよいですか? すべてを挿入しようとする主キーとして アクセスする前のURL

または、ファイルに書き込む必要がありますか?

  • 一つのファイル?

  • 複数のファイル? ファイル構造をどのように設計すればよいですか?

このトピックまたは類似のトピックに関する書籍や多くの論文があるはずです。 何を読むべきかアドバイスをいただけますか?

6 Answer


9


たくさんのクモを書きました。 私にとって、メモリ不足よりも大きな問題は、コードまたはマシンがクラッシュした場合、またはコードを微調整する必要があると判断した場合、すでにスパイダーしたすべてのURLを失う可能性があることです。 RAMが不足すると、最近のほとんどのマシンとOSがページングするため、速度は低下しますが機能します。 使用できなくなったURLは、生産性に大きな打撃を与える可能性があるため、数時間および数時間のランタイムで収集されたURLのセットを再構築する必要があります。

失いたくない情報をRAMに保持するのは悪いことです。 明らかに、URLが既に見つかっているかどうかを確認するために高速のランダムアクセスが必要なので、データベースがその時点で移動する方法です。 もちろん、メモリ内のルックアップは高速ですが、メモリ内に保持するURLを把握することのトレードオフによりオーバーヘッドが追加されます。 必要なURLと不要なURLを判別するコードを作成するのではなく、データベースに保管し、コードをクリーンで保守可能にし、SQLクエリとスキーマを適切なものにすることに集中します。 URLフィールドを一意のインデックスにすると、DBMは重複したリンクを自動的に回避しながら、それらをすぐに見つけることができます。

インターネットやアクセスしているサイトへの接続は、おそらく内部ネットワーク上のマシン上のデータベースへの接続よりもかなり遅くなります。 同じマシン上のSQLiteデータベースは最速かもしれませんが、DBM自体はPostgresほど洗練されていませんが、これは私のお気に入りです。 データベースを私のスパイダーマシンと同じスイッチの別のマシンに置くと、非常に高速になることがわかりました。 1台のマシンでスパイダー、解析、データベースの読み取り/書き込みを処理するのはかなり負荷がかかるため、古いマシンにLinuxを投入してPostgresをインストールし、街に出かけます。 より高速が必要な場合は、追加のRAMをボックスに入れます。 データベース用に別のボックスを用意することは非常に便利です。


7


これらは私にとって重要な側面のようです:

  1. RAMが大きくなりすぎるため、URLをメモリに保持できません

  2. 少なくともO(logn)の高速な存在検索が必要です

  3. 高速挿入が必要です

これを行うには多くの方法があり、データベースがどれだけ大きくなるかに依存します。 SQLデータベースは、問題の優れたモデルを提供できると思います。

おそらく、必要なのはSQLiteデータベースだけです。 通常、存在チェックのための文字列検索は遅い操作です。 これを高速化するには、URLのCRCハッシュを作成し、CRCとURLの両方をデータベースに保存します。 そのCRCフィールドにインデックスがあります。

  • 挿入するとき:URLとハッシュを挿入します

  • 存在検索を実行する場合:CRCを取得します 新しいURLの可能性があり、既にデータベースにあるかどうかを確認します。

もちろん、URLハッシュに衝突する可能性がありますが、100%のスパニングが重要でない場合は、衝突が発生したときにDBにURLがないというヒットを取ることができます。

多くの方法で衝突を減らすこともできます。 たとえば、CRCのサイズ(CRC4ではなくCRC8)を増やし、より大きなサイズのハッシュアルゴリズムを使用できます。 または、CRCとURLの長さを使用します。


4


これは、実行するスパイダーの規模と、実行するマシンの種類によって異なります。 典型的なURLが60バイト程度の文字列であると仮定すると、メモリ内のセットはURLごとに100バイト以上かかります(Pythonのセットと辞書は、速度上の理由で60%を超えることはできません)。 使用可能なRAMが約16 GBの64ビットマシン(およびPythonディストリビューション)がある場合、問題の重要なセットに10 GB以上を確実に割り当てることができ、約1億のURLを簡単にスパイダーできます。しかし、極端な例では、3GBのRAMを搭載した32ビットマシンを使用している場合は、明らかに1GBを超える重要なセットに割り当てることはできず、約1,000万のURLに制限されます。 Sqliteは、32ビットマシンでは作成できなかった同じサイズの範囲で役立ちますが、寛大な64ビットマシンでは1億または2億のURLを作成できます。

それ以外にも、PostgreSQLをお勧めします。PostgreSQLには、基本的に問題なく別のマシン(高速LAN)で実行できるという利点があり、メインマシンをスパイダー専用にすることができます。 MySQL&cもそれで問題ないと思いますが、PostgreSQLの標準準拠と堅牢性が大好きです;-)。 これにより、たとえば問題なく数十億個のURLが可能になります(高速なディスク、またはRAID配置の改善、そしてもちろん速度を上げるのに十分なRAM)。

非常に長いURLの代わりに固定長ハッシュを使用してメモリ/ストレージを節約しようとしても、実際の新しいURLのクロールを停止することがある場合があります。 そのような「衝突」は、必ずしもそうである必要はありません:ハッシュに8バイトしか使用しない場合でも、数十億のURLを見ているときに衝突の実質的なリスクがあるだけです(このための「平方根ヒューリスティック」既知の問題)。

URLを表す8バイトの文字列により、インメモリセットアーキテクチャは、上で概説したように、恵まれたマシンで10億以上のURLを簡単にサポートするはずです。

だから、おおよそいくつのURLをスパイダーしたいのか、そしてどれくらいのRAMを節約できるのか?)


2


URLのみを保存していますか? mongoDBをご覧ください。 実装が非常に簡単なNoSQLデータベースです。

Pythonバインディングもあります:


1


同じ時間に同じようなURLが表示される可能性が高いので(たとえば、Webサイトをスパイダーしている間、Webサイトのメインページへのリンクがたくさん表示されます)、記憶するまでURLを辞書に保存することをお勧めします制限され(10MのURLなどの合理的な数をハードコードするだけ)、辞書が大きくなりすぎたらhttp://pypi.python.org/pypi/python-cdb/0.32[CDBデータベースファイル]に辞書をフラッシュします。

そうすれば、URLチェックの大部分はメモリ内で行われ(高速)、メモリ内にないものはディスクを1〜2回読み取るだけでアクセスしたことを確認できます。


0


現時点ではhttp://docs.python.org/library/pickle.html[Pickling]を検討してください:単純な構造化ストレージ。

もちろん、他のレスポンダーが言ったように、RAMをすぐに使い果たすため、マイレージは異なります。