NPO の Web サイトやコンテンツをクロールしてその結果のリストを作成する単純なクローラーを作成しようと考えました。
これを行う方法について誰か考えがありますか?クローラーを開始するためにどこに向けますか? 調査結果を送り返し、クロールを続けるにはどうすればよいでしょうか? 見つけたものをどのように知るかなど。
NPO の Web サイトやコンテンツをクロールしてその結果のリストを作成する単純なクローラーを作成しようと考えました。
これを行う方法について誰か考えがありますか?クローラーを開始するためにどこに向けますか? 調査結果を送り返し、クロールを続けるにはどうすればよいでしょうか? 見つけたものをどのように知るかなど。
確かに、車輪を再発明することになります。しかし、ここに基本があります:
これらを永続ストレージに配置して、状態を失うことなくクローラーを停止および開始できるようにします。
アルゴリズムは次のとおりです。
while(list of unvisited URLs is not empty) {
take URL from list
remove it from the unvisited list and add it to the visited list
fetch content
record whatever it is you want to about the content
if content is HTML {
parse out URLs from links
foreach URL {
if it matches your rules
and it's not already in either the visited or unvisited list
add it to the unvisited list
}
}
}
クローラーの複雑な部分は、クローラーを膨大な数のWebサイト/リクエストに拡張する場合です。この状況では、次のようないくつかの問題に対処する必要があります。
すべての情報を1つのデータベースに保持することは不可能です。
巨大なインデックスを処理するのに十分なRAMがありません
マルチスレッドのパフォーマンスと並行性
クローラートラップ(URL、カレンダー、セッションIDを変更することによって作成される無限ループ...)および複製されたコンテンツ。
複数のコンピューターからクロールする
不正な形式のHTMLコード
サーバーからの一定のhttpエラー
圧縮されていないデータベースでは、必要なスペースが約8倍大きくなります。
ルーチンと優先順位を再クロールします。
圧縮(Deflate / gzip)でリクエストを使用します(あらゆる種類のクローラーに適しています)。
そしていくつかの重要なこと
robots.txtを尊重する
また、Webサーバーを窒息させないように要求するたびにクローラーが遅延します。
マルチスレッド Web クローラー
大規模な Web サイトをクロールする場合は、マルチスレッド クローラーを作成する必要があります。クロールされた情報をファイル/データベースに接続、取得、書き込み - これらはクロールの 3 つのステップですが、シングル スレッドを使用すると CPU とネットワークの使用率が高くなります。
マルチスレッド Web クローラーには、2 つのデータ構造が必要です。
Web クローラーは、BFS を使用してワールド ワイド Web をトラバースします。
基本的な Web クローラーのアルゴリズム:-
キューが linksToBeVisited が空になるまで、ステップ 2 から 5 を繰り返します。
スレッドを同期する方法のコード スニペットを次に示します。
public void add(String site) {
synchronized (this) {
if (!linksVisited.contains(site)) {
linksToBeVisited.add(site);
}
}
}
public String next() {
if (linksToBeVisited.size() == 0) {
return null;
}
synchronized (this) {
// Need to check again if size has changed
if (linksToBeVisited.size() > 0) {
String s = linksToBeVisited.get(0);
linksToBeVisited.remove(0);
linksVisited.add(s);
return s;
}
return null;
}
}
クローラーの概念は単純です。
HTTP GET 経由でルート ページを取得し、それを解析して URL を見つけ、まだ解析されていない限り、それらをキューに入れます (そのため、既に解析したページのグローバル レコードが必要です)。
Content-type ヘッダーを使用して、コンテンツのタイプを確認し、クローラーを HTML タイプのみの解析に制限できます。
HTMLタグを取り除いてプレーンテキストを取得し、テキスト分析を実行できます(タグなどを取得して、ページの肉を取得します)。高度な知識があれば、画像の alt/title タグでそれを行うこともできます。
バックグラウンドでは、キューから URL を食べて同じことを行うスレッドのプールを持つことができます。もちろん、スレッドの数を制限したい。
NPO のサイトが比較的大きいか複雑である場合 (「翌日」リンクのあるカレンダーのような「ブラック ホール」を効果的に作成する動的ページがある場合)、Heritrix などの実際の Web クローラーを使用することをお勧めします。
サイトの合計ページ数が少ない場合は、curl、wget、または独自のものを使用するだけで済みます。それらが大きくなり始めたり、実際のクローラーを使用するためにスクリプトをより複雑にしたりし始めたり、少なくともそのソースを見て、それらが何をしているのか、そしてその理由を確認してください.
いくつかの問題 (他にもあります):
ウィキペディアにはWeb クローラーに関する優れた記事があり、アルゴリズムと考慮事項の多くをカバーしています。
ただし、わざわざ独自のクローラーを作成するつもりはありません。たいへんな作業ですし、必要なのは「単純なクローラー」だけなので、本当に必要なのは既製のクローラーだけだと思います。無料でオープンソースのクローラーがたくさんあり、ユーザーがほとんど手を加えることなく、必要なすべてのことを実行してくれる可能性があります。
単語のリストを作成し、Google で検索された単語ごとにスレッドを作成できます。
次に、各スレッドは、ページ内で見つかったリンクごとに新しいスレッドを作成します。
各スレッドは、データベースで見つけたものを書き込む必要があります。各スレッドは、ページの読み取りを完了すると終了します。
そして、データベースには非常に大きなリンクのデータベースがあります。
wget を使用し、すべてのファイルをハードドライブにダンプする再帰的な Web サックを実行してから、別のスクリプトを記述して、ダウンロードしたすべてのファイルを調べて分析します。
編集:または、wgetの代わりにcurlかもしれませんが、curlに慣れていないため、wgetのように再帰的なダウンロードを行うかどうかわかりません。
私は会社の内部検索にオープン検索サーバーを使用しています。これを試してください:http: //open-search-server.comもオープンソースです。
.net でリアクティブ拡張機能を使用して単純な Web クローラーを作成しました。
https://github.com/Misterhex/WebCrawler
public class Crawler
{
class ReceivingCrawledUri : ObservableBase<Uri>
{
public int _numberOfLinksLeft = 0;
private ReplaySubject<Uri> _subject = new ReplaySubject<Uri>();
private Uri _rootUri;
private IEnumerable<IUriFilter> _filters;
public ReceivingCrawledUri(Uri uri)
: this(uri, Enumerable.Empty<IUriFilter>().ToArray())
{ }
public ReceivingCrawledUri(Uri uri, params IUriFilter[] filters)
{
_filters = filters;
CrawlAsync(uri).Start();
}
protected override IDisposable SubscribeCore(IObserver<Uri> observer)
{
return _subject.Subscribe(observer);
}
private async Task CrawlAsync(Uri uri)
{
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromMinutes(1) })
{
IEnumerable<Uri> result = new List<Uri>();
try
{
string html = await client.GetStringAsync(uri);
result = CQ.Create(html)["a"].Select(i => i.Attributes["href"]).SafeSelect(i => new Uri(i));
result = Filter(result, _filters.ToArray());
result.ToList().ForEach(async i =>
{
Interlocked.Increment(ref _numberOfLinksLeft);
_subject.OnNext(i);
await CrawlAsync(i);
});
}
catch
{ }
if (Interlocked.Decrement(ref _numberOfLinksLeft) == 0)
_subject.OnCompleted();
}
}
private static List<Uri> Filter(IEnumerable<Uri> uris, params IUriFilter[] filters)
{
var filtered = uris.ToList();
foreach (var filter in filters.ToList())
{
filtered = filter.Filter(filtered);
}
return filtered;
}
}
public IObservable<Uri> Crawl(Uri uri)
{
return new ReceivingCrawledUri(uri, new ExcludeRootUriFilter(uri), new ExternalUriFilter(uri), new AlreadyVisitedUriFilter());
}
public IObservable<Uri> Crawl(Uri uri, params IUriFilter[] filters)
{
return new ReceivingCrawledUri(uri, filters);
}
}
次のように使用できます。
Crawler crawler = new Crawler();
IObservable observable = crawler.Crawl(new Uri("http://www.codinghorror.com/"));
observable.Subscribe(onNext: Console.WriteLine,
onCompleted: () => Console.WriteLine("Crawling completed"));