非同期爬虫類

4241 ワード

非同期爬虫類
従来、爬虫類を書くのは単一プロセスの単一スレッドであり、100兄のページを登ると仮定すると、1つのループが1つずつ登ることになる.しかし、次のキャプチャを実行するには、ネットワークIOリクエストの実行が完了するまで待つ必要があるため、効率は高くありません.最初に処理したデータは大きくなく、まだ意識していませんが、万ページを登ると、すぐに差が浮き彫りになります.爬虫類は同時に実行し、非同期でプログラミングしなければなりません.pythonでの同時プログラミングには、マルチプロセス、マルチスレッド、およびコラボレーションの3つの方法があります.もちろん、この3つは、マルチプロセス+マルチスレッドなど、組み合わせて使用することもできます.GILロックが存在するため、python内のマルチスレッドは実際には並列ではなく同時であり、特にCPU密集型のプログラムに影響を与える.だからpythonの老兵はあなたに言うかもしれません:pythonの下のマルチスレッドは鶏の肋骨で、マルチプロセスを使うことをお勧めします!実は爬虫類だけでもっと効果的な方法は協程です.
きょうてい
協程とは
コラボレーションは、ユーザーレベルの軽量レベルのスレッドです.協程は独自のレジスタコンテキストとスタックを持っている.コンシステントスケジューリング切替では、レジスタコンテキストとスタックを別の場所に保存し、切り取ったときに、以前に保存したレジスタコンテキストとスタックを復元します.したがって、コプロセッサは、前回の呼び出し時の状態(すなわち、すべてのローカル状態の特定の組合せ)を保持することができ、プロシージャが再入力されるたびに、前回の呼び出しの状態に入ることに相当し、言い換えれば、前回の呼び出し時に論理ストリームの位置に入ることになる.同時プログラミングでは、コプロセッサはスレッドと同様に、各コプロセッサは実行ユニットを表し、独自のローカルデータを持ち、他のコプロセッサとグローバルデータと他のリソースを共有します.
なぜ協程を使うのか
現在主流言語では,コンカレント施設としてマルチスレッドが基本的に選択されており,スレッドに関連する概念はプリエンプトマルチタスク(Preemptive multitasking)であり,コラボレーションに関連するマルチタスクである.プロセスでもスレッドでも、ブロック、切り替えのたびにシステムコール(system call)に陥る必要があります.まず、CPUにオペレーティングシステムのスケジューラを走らせ、その後、スケジューラによってどのプロセス(スレッド)を走るかを決定します.また,プリエンプトスケジューリングの実行順序が特定できないという特徴から,スレッドを使用する際に同期問題を非常に注意深く処理する必要があり,コヒーレンスにはこの問題は全く存在しない(イベントドライバや非同期プログラムにも同様の利点がある).コプロセッサはユーザ自身がスケジューリングロジックを記述するため、CPUにとって、コプロセッサは実際には単一スレッドであるため、CPUはどのようにスケジューリングし、コンテキストを切り替えるかを考慮する必要がなく、CPUのスイッチングオーバーヘッドを省くことができるので、コプロセッサはある程度マルチスレッドよりも優れている.コラボレーションはユーザ状態内のコンテキスト切替技術である
使用方法
pythonではどうやって協程を使いますか?答えはgeventを使うことです.したがって、最も推奨される方法は、マルチプロセス+コヒーレンス(各プロセスにおいて単一スレッドと見なすことができるが、この単一スレッドはコヒーレンス化されている)マルチプロセス+コヒーレンスでは、CPU切替のオーバーヘッドを回避し、複数のCPUを十分に利用することができ、データ量の大きい爬虫類やファイルの読み書きなどの効率の向上に大きなものである.
実例
#!/usr/bin/env python
# encoding: utf-8
"""    """
from gevent import monkey
monkey.patch_all()

import requests
from gevent.pool import Pool
import time
import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s  -  %(message)s ', datefmt='[%Y-%m-%d %H:%M:%S]',
                    )

crawl_urls = ["http://www.baidu.com"] * 100


def run_asynchronous():
    pool = Pool(size=10)  #      
    for i in range(len(crawl_urls)):
        pool.spawn(crawl, crawl_urls[i], i)
    pool.join()


def crawl(url, id):
    requests.get(url)
    # logging.info("asynchronous finish id :{}".format(id))


def run_synchronous():
    for i in range(len(crawl_urls)):
        requests.get(crawl_urls[i])
        # logging.info("synchronous finish id :{}".format(i))


if __name__ == "__main__":

    start = time.time()
    run_synchronous()
    end = time.time()
    logging.info("synchronous time: {}".format(end - start))

    start = time.time()
    run_asynchronous()
    end = time.time()
    logging.info("asynchronous time: {}".format((end - start)))

非同期後、baiduのトップページを100回も登っても1秒もかからないのが見えます.また,同期の爬虫類の順序は順次行われ,非同期爬虫類は同時実行である.