何回した後、私たちは非常に重要なポイントがクロールされるスピードのある場所を、改善するために多くのことを見つけるの要件を達成するために、爬虫類を書きました。この記事では、通過コードを使用する方法について説明し、マルチプロセス、マルチスレッド、コルーチン クロール速度を向上させます。注意:私たちはあなたのコード内の理論と原則、すべてのものへの洞察をしません。
第二に、同期
まず、我々は簡略爬虫類、各機能セグメント、意識行動関数型プログラミングを書き込みます。次のコードは、アクセスオブジェクト300 Baiduのページであると前記状態コードを返す parse_1
関数は、サイクル数、現在のサイクル数(0から始まる)、着信のURLの各サイクル設定できる parse_2
機能。
インポート要求 デフparse_1(): URL = 'https://www.baidu.com' のための私の範囲内(300): parse_2(URL) デフparse_2(URL): 応答= requests.get(URL) プリント(応答。 STATUS_CODE) もし__name__ == '__main__': parse_1()
シングルスレッドモードを使用して、リクエストURLが必然的に待ち時間につながる場合、パフォーマンスは主に、IO要求で消費されます
サンプルコードは、一般的なシリアル・ロジック、ある parse_1
転送およびURLサイクルの数 parse_2
、 parse_2
要求と戻りステータスコードを parse_1
反復するステップの前にもう一度繰り返した続けます
第三に、マルチスレッド
プロセスが実際に改善されたCPU使用率が得られる使用量を増加させるマルチスレッドので、プログラムの実装では、各スケール上の唯一のスレッドCPU時間があるため
マルチスレッドライブラリの多くがあり、ここ concurrent.futures
で ThreadPoolExecutor
実証します。はじめに ThreadPoolExecutor
ライブラリには、他のライブラリのコードよりも簡潔であるため、
説明の便宜のために、それは新たに部品を追加された場合、次のコードは、コードの最初の行はプラス>シンボルは、説明の観察を容易にする、実際の操作が必要で除去します
インポート要求 > concurrent.futuresからはThreadPoolExecutorインポート :DEF parse_1() = 'https://www.baidu.com' URLを #建立线程池 >プール= ThreadPoolExecutor(6) :Iの範囲内(300)のために > pool.submit (parse_2、URL) > pool.shutdown(待機= TRUE) デフparse_2(URL): 応答= requests.get(URL) プリント(response.status_code) の場合__name__ == '__main__': parse_1を()
これは、比較的に同期され、非同期 。非同期は、互いに独立して、イベントを待っている間に自分のことをし続ける、あなたが作業の完了後に、このイベントを待つ必要はありません。非同期スレッドは時々私たちは、あなたが使用することができ、結果を知っている必要があり、その結果を知らない非同期処理非同期のマルチスレッド手段である方法を、達成することであるコールバックを
インポート要求 concurrent.futuresからはThreadPoolExecutorインポート #增加回调函数 :> DEFコールバック(未来) >印刷(future.result()) :デフparse_1() のurl = 'https://www.baidu.com' (プール= ThreadPoolExecutorを6) (300)は、iの範囲内のため: >結果= pool.submit(parse_2、URL)# 回调的关键步骤 > results.add_done_callback(コールバック) pool.shutdown(待機= TRUE) デフparse_2(URL): 応答=リクエスト.get(URL) プリント(response.status_code) の場合__name__ == '__main__': parse_1()
Pはython無数の人々が批判しているマルチスレッドGIL(グローバルインタプリタロック) が、IO集約型のマルチスレッドタスクに属しても、この大多数のページをクロールするために非常に適しています。
第四に、マルチプロセス
マルチプロセスは、2つの方法で実装さ: ProcessPoolExecutor
と multiprocessing
1. ProcessPoolExecutor
そして、マルチスレッド ThreadPoolExecutor
に類似
インポート要求 > concurrent.futuresからはProcessPoolExecutorインポート :DEF parse_1() = 'https://www.baidu.com' URLを #建立线程池 >プール= ProcessPoolExecutor(6) :Iの範囲内(300)のために > pool.submit (parse_2、URL) > pool.shutdown(待機= TRUE) デフparse_2(URL): 応答= requests.get(URL) プリント(response.status_code) の場合__name__ == '__main__': parse_1を()
2つのクラス名の変更を探し、コードはまだ非常にシンプルで、共感も追加することができますコールバック 関数
インポート要求 concurrent.futuresからはProcessPoolExecutorインポート :> DEFコールバック(未来) >印刷(future.result()) :デフparse_1() のurl = 'https://www.baidu.com' プール= ProcessPoolExecutor(6) のiについて範囲内(300): >結果= pool.submit(parse_2、URL) > results.add_done_callback(コールバック) pool.shutdown(待機= TRUE) デフparse_2(URL): 応答= requests.get(URL) プリント(応答。 STATUS_CODE) もし__name__ == '__main__': parse_1()
2.マルチプロセッシング
コードで直接見て、コメント内のすべて。
リクエストのインポート プールマルチプロセッシングから>インポート :DEFのparse_1を() URL = 'https://www.baidu.com' #建てプール > =プールプール(=プロセス5) #ストア結果 > res_lst = [] (I範囲内のために300): タスクプールに#の追加 > pool.apply_async RES =(FUNC = parse_2、引数=(URL)) #完全な結果を得る(必要とら) > res_lst.append(RES) #ストア最終結果(5月直接記憶又は印刷) > good_res_lst = [] res_lstにおけるRESのため>: #GET処理結果を用いて取得 >()= res.get good_res #品質決意結果 > good_res IF: > good_res_lst.append(good_res) #閉じると完了待ち > pool.close() > pool.join() デフparse_2(URL): 応答= requests.get(URL) プリント(response.status_code) の場合__name__ == '__main__': parse_1()
あなたは見ることができ multiprocessing
、コードベースを少し退屈を、より多くの拡張をサポートしています。マルチプロセスとマルチスレッドは本当に加速の目的を達成することができますが、彼らはIOスレッドやプロセスをブロックしている場合は、廃棄物となりますので、より良い方法があります......
第五に、非ブロック非同期
コルーチン+コールバック我々は目的を達成することができ、ダイナミック、ノンブロッキングの非同期のコラボレーションで、自然はとても資源の大規模な範囲での使用に、一つのスレッドだけを取りました
クラシックは、非同期の非ブロッキングを使用することである asyncio
ライブラリー+ yield
パッケージの上層が徐々に出現の使用を容易にするために、 aiohttp
より良いの非同期の非ブロッキングをよりよく理解理解するために、 asyncio
ライブラリを。そして、 gevent
コルーチンライブラリを実装するのは非常に簡単です
リクエストのインポート >インポートgeventサルから #猿のパッチがコラボレーション魂で実行される > monkey.patch_all() > [インポートgevent DEF parse_1(): URL = 'https://www.baidu.com' #は、タスクリストを作成する > tasks_list = [ ] の範囲でI(300)のために: > = gevent.spawnタスク(parse_2、URL) > tasks_list.append(タスク) > gevent.joinall(tasks_list) DEF parse_2(URL): 応答= requests.get(URL) を印刷( response.status_code) IF __name__ == '__main__': parse_1()
geventが大幅にスピード、また新たな問題を紹介することができます。私たちは、サーバー上であまりにも多くの負担を引き起こすことが速すぎたくない場合はどのように行うには? それは、マルチプロセス、マルチスレッド化ビルドプールされている場合は、プールの数を制御することができます。あなたはgeventで速度を制御したい場合は、良好なアプローチがあります。 ビルドキュー。 geventも提供 Queneクラスを 、以下のコードより大きな変化を
リクエストのインポート GEVENTインポートモンキーから monkey.patch_all() インポートGEVENT gevent.queueキューから>インポート :DEF parse_1() URL = 'https://www.baidu.com' tasks_list = [] #インスタンス化されたキュー > quene =キュー() 範囲でI(300)用: #は、すべてのURLキューに押し込ま > quene.put_nowait(URL) #2つのキュー :(2)に_について>範囲 > = gevent.spawnタスク(parse_2) > tasks_list.append(タスク) gevent.joinall(tasks_list) #パラメータはキューに、導入する必要がある :> DEF parse_2() #は、キューが空のループであるか否かを判断する >しばらくquene.emptyない(): #キューポップ > URL = quene.get_nowait() レスポンス= requests.get(URL) #判断队列状态 >印刷(quene.qsize()、response.status_code) であれば__name__ == '__main__': parse_1()
結論
これらは、いくつかの一般的に使用される加速方法です。あなたはタイムコード・テスト・モジュールを利用することができる興味を持っている場合は、実行時間を決定します。〜さようなら、サーバーに過度の圧力を与えていない、爬虫類は重要なスキルですが、速度が適切に良い習慣爬虫類の労働者を制御して加速