2

django-rqを使用して、Python で Web サイト クローラーを作成しようとしています。これまでのところ、私のワーカーは次のようになります。

  1. キューから次のページを取得します。
  2. データベースにページ レコードを作成します。設定しstatus=1ます。
  3. ページの内容をダウンロードして処理します。1 分ほどかかる場合があります。
  4. ページ内の各リンクについて
    1. リンクがすでにデータベースに登録されているかどうかを確認してください。
    2. そうでない場合は、新しいページ レコードを作成します。status=0リンクを設定してキューに追加します。
  5. for ループが終了したら、status=0 のページ数が 0 かどうかを確認します。0 の場合、ジョブは完了です。

status=1ページが処理されたことを意味します。status=0ページがまだ処理されていないことを意味します。

現在、このアルゴリズムは単一のワーカーで問題なく機能します。ただし、ジョブルーチンの終了が本来よりも早くトリガーされることがあるため、より多くのワーカーがいる場合はそうではありません。

このワーカーを実装する正しい方法は何ですか?

4

2 に答える 2

3

1. 現在のシステムを微調整する

  • ページの処理が完了するまでページのステータスを 1 に設定するのを延期する場合、ワーカーは早期に「ジョブ完了」を宣言すべきではありません。
  • ステップ 2 は、クロールを開始する最初のページにのみ必要です。

したがって、システムは次のようになります。

start job:
1. Create a page record in the database. Set status=0. Add page to queue.

worker:
1. Get the next page from the queue.
2. Download the page contents and process. Might take up to a minute or so.
3. For each link in the page
    1. Check if the link is already registered in the database.
    2. If not, create a new page record. Set status=0 and add the link to the queue.
4. After the for loop ends, set status=1 for this page.
5. Check whether the count of pages with status=0 is 0. If yes, the job is done.

前の Web クローリング ジョブが終了する前に次の Web クローリング ジョブが開始された場合、最後の Web クローリング ジョブの最後にのみ「ジョブが完了」するという問題があります。おそらく、ジョブ ID をデータベース ページ レコードに追加して、「完了したジョブ」を次のように再定義できます。count(status=0 and job-id=x) = 0

2.RQのジョブクラスを活用する

RQ ドキュメントから:

ジョブがキューに入れられると、 queue.enqueue() メソッドは Job インスタンスを返します。...ジョブがまだ終了していない場合はNoneを返し、ジョブが終了した場合はNone以外の値を返す便利な結果アクセサープロパティがあります(もちろん、ジョブに最初の戻り値があると仮定します) .

2 つの異なるタイプのジョブをキューに入れることができます。1 つは「Web ページの取得」で、もう 1 つはクロール プロセスを管理するためのものです。

管理ジョブは、すべての「Web ページのフェッチ」ジョブを開始して追跡します。すべてのサブジョブが完了したため、いつ「ジョブが完了した」かがわかります。

クロール プロセスを管理するために、必ずしもデータベースに何かを書き込む必要はありません。

おそらく別々のキューで同時に作業できるようにcrawl、 2 つ以上のワーカーを実行する必要があります。fetch

def something_web_facing():
    ...
    queue.enqueue(crawl, 'http://url.com/start_point.html')
    ...

def crawl(start_url):
    fetch_jobs = []
    seen_urls = set()

    seen_urls.add(start_url)
    fetch_jobs.append( queue.enqueue(fetch, start_url) )

    while len(fetch_jobs) > 0:

        # loop over a copy of fetch_jobs
        for job in list(fetch_jobs):

            # has this job completed yet?
            if job.result:

                # a fetch job returns a list of the next urls to crawl
                for url in job.result:

                    # fetch this url if we haven't seen it before
                    if url not in seen_urls:
                        seen_urls.add(url)
                        fetch_jobs.append( queue.enqueue(fetch, url) )

                fetch_jobs.remove(job)

        time.sleep(1)

    return "Job done!"

def fetch(url):
    """Get web page from url, return a list of links to follow next"""

    html_page = download_web_page(url)
    links_to_follow = find_links_to_follow(html_page)
    return links_to_follow

3. 他人の Web クローラー コードを使用する

スクレイピー

スクレイピーを使用するジョブをキューに入れることができます。スクリプトからスクレイピーを実行する

于 2014-10-03T10:20:12.633 に答える
2

考えただけですが、ワーカーのステータスを保持するテーブルを追加できませんか?

つまり、10 個のワーカーと次の状態の場合: 0-8 - 「保持」 9 - 「作業中」

この状態から、9 はおそらく 0 から 8 が作業できるキューにさらにページを追加します (そして、テーブル内のステータスを「作業中」に変更します)。

注意すべきもう 1 つの点は、ワーカーがその状態を決定する順序を正確にする必要があることです。

  1. キューから次のページを取得します。
  2. データベースにページ レコードを作成します。status=1 に設定します。
  3. ページの内容をダウンロードして処理します。1 分ほどかかる場合があります。
  4. ページ内の各リンクについて
    1. リンクがすでにデータベースに登録されているかどうかを確認してください。2.そうでない場合は、新しいページ レコードを作成します。status=0 を設定し、リンクをキューに追加します。
  5. キューにページがあるかどうかを確認します。
  6. そうでない場合は、待機状態に入ります。

ワーカーが待機状態にある場合、アクションの順序は次のようになります。

  1. 起きろ
  2. キューにジョブがある場合は、「作業中」状態になります
  3. そうでない場合は、すべてのワーカーが「ホールド」状態にあるかどうかを確認します。
  4. その場合は、終了してください。

「ホールド」状態にあるすべてのワーカーは、定期的にチェックして、「作業中」ステータスのワーカーがあるかどうかを確認する必要があります。その場合は、キューを確認してからスリープします。そうでない場合は、終了します。

もう 1 つの方法は、他のワーカーを監視するコマンド アンド コントロール ワーカーを配置することです。それらがすべて「ホールド」状態にある場合は、すべてが監視するフラグをトリガーして終了します。

于 2014-10-01T20:16:17.523 に答える