[Python クローラー] 12. クローラー軍団を構築する

序文

いつものように前のレベルの知識を復習しましょう!前のレベルでは、クローラーの結果を電子メールに送信し、クローラーを定期的に実行する方法を学びました。

メールに関しては以下のような流れになります。

ここに画像の説明を挿入します
使用するモジュールは smtplib と email で、前者はサーバーへの接続、ログイン、送信、終了のプロセスを担当します。後者は、電子メールのタイトルと本文を入力する責任があります。

ここに画像の説明を挿入します
最後のサンプル コードは次のようになります。

import smtplib 
from email.mime.text import MIMEText
from email.header import Header
#引入smtplib、MIMEText和Header

mailhost='smtp.qq.com'
#把qq邮箱的服务器地址赋值到变量mailhost上,地址应为字符串格式
qqmail = smtplib.SMTP()
#实例化一个smtplib模块里的SMTP类的对象,这样就可以调用SMTP对象的方法和属性了
qqmail.connect(mailhost,25)
#连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号。
#以上,皆为连接服务器。

account = input('请输入你的邮箱:')
#获取邮箱账号,为字符串格式
password = input('请输入你的密码:')
#获取邮箱密码,为字符串格式
qqmail.login(account,password)
#登录邮箱,第一个参数为邮箱账号,第二个参数为邮箱密码
#以上,皆为登录邮箱。

receiver=input('请输入收件人的邮箱:')
#获取收件人的邮箱。

content=input('请输入邮件正文:')
#输入你的邮件正文,为字符串格式
message = MIMEText(content, 'plain', 'utf-8')
#实例化一个MIMEText邮件对象,该对象需要写进三个参数,分别是邮件正文,文本格式和编码
subject = input('请输入你的邮件主题:')
#输入你的邮件主题,为字符串格式
message['Subject'] = Header(subject, 'utf-8')
#在等号的右边是实例化了一个Header邮件头对象,该对象需要写入两个参数,分别是邮件主题和编码,然后赋值给等号左边的变量message['Subject']。
#以上,为填写主题和正文。

try:
    qqmail.sendmail(account, receiver, message.as_string())
    print ('邮件发送成功')
except:
    print ('邮件发送失败')
qqmail.quit()
#以上为发送邮件和退出邮箱。

タイミングについては、スケジュール モジュールを選択しましたが、公式ドキュメントに記載されているように、その使用方法は非常に簡単です。

ここに画像の説明を挿入します
以下のコードは例です。

import schedule
import time
#引入schedule和time

def job():
    print("I'm working...")
#定义一个叫job的函数,函数的功能是打印'I'm working...'

schedule.every(10).minutes.do(job)       #部署每10分钟执行一次job()函数的任务
schedule.every().hour.do(job)            #部署每×小时执行一次job()函数的任务
schedule.every().day.at("10:30").do(job) #部署在每天的10:30执行job()函数的任务
schedule.every().monday.do(job)          #部署每个星期一执行job()函数的任务
schedule.every().wednesday.at("13:15").do(job)#部署每周三的13:15执行函数的任务

while True:
    schedule.run_pending()
    time.sleep(1)    
#15-17都是检查部署的情况,如果任务准备就绪,就开始执行任务。    

コルーチンとは

私たちは多くのクローラー プロジェクトを行ってきましたが、クロールしたデータはそれほど大きくありません。何千ものデータをクロールしたい場合は、プログラムが 1 行ずつ実行されるため、問題が発生します。シーケンシャルに実行されるため、必要なデータを取得するまでに長い時間待たなければなりません。

クローラーが大量のデータをクロールするには時間がかかるため、複数のクローラーを同時にクロールできますか?

これでクロールの効率が上がることは間違いなく、一人では終わらない仕事と同じように、チームを組んで一緒にやれば一気に完了します。

これは良いアイデアです。複数のクローラーに作業を行わせます。しかし、具体的に Python でこれを実装するにはどうすればよいでしょうか?

これを実装する方法については、今すぐ考えるのをやめてください。後で説明します。

さて、私と一緒にシナリオを想像してみてください。

ここに画像の説明を挿入します
あなたは間違いなくこれを実行すると信じています: クリックして 3 つの映画すべてをダウンロードします。どちらを先にダウンロードしても、それを先に視聴し、まだダウンロードされていない映画のダウンロード状況を保持します。

これをコンピュータの概念を使って説明すると、1 つのタスクが完了していない場合でも、他の複数のタスクは相互に影響を与えることなく実行できます (最初にダウンロードした映画を視聴しているとき、他の映画はダウンロードされ続けます)、相互に影響を受けることはありません)、それは非同期と呼ばれます。

非同期という概念があるなら、同期という概念もあるはずですよね。はい、同期とは、次のタスクを開始する前に 1 つのタスクを完了する必要があることを意味します (映画を観た後に次の映画を見るのと同じです)。

明らかに、タスクを非同期で実行すると、不必要な待機を減らすことができるため、同期よりも多くの時間を節約できます。時間を最適化する必要がある場合、非同期は検討する価値のあるソリューションです。

ここに画像の説明を挿入します
同期と非同期の概念を Web クローラーのシナリオに置き換えると、以前に学習したクローラーのメソッドはすべて同期になります。

クローラーはリクエストを開始するたびに、サーバーが応答を返すのを待ってから次のステップを実行します。多くの場合、ネットワークの不安定性とサーバー自体の応答時間により、クローラーは待機中に多くの時間を無駄にします。これは、大量のデータをクロールするときにクローラーの速度が遅くなる理由でもあります。

ここに画像の説明を挿入します

では、非同期クローラ方式を採用することで、複数のクローラが互いに干渉せずに比較的独立してタスクを実行でき、待ち時間を回避できるでしょうか。明らかに、クローラーの効率と速度が向上します。

非同期クローラメソッドを実装してクローラの効率を向上するにはどうすればよいでしょうか? この質問に答えるには、コンピューターの歴史について少し知る必要があります。

すべてのコンピューターが CPU (中央処理装置) に依存して動作していることはわかっています。従来、シングルコアCPUのコンピュータで複数のタスクを処理すると、各タスクがCPUを占有し、1つのタスクが完了するまで次のタスクが開始されないという問題が発生していました。結局のところ、CPU は 1 つしかないため、コンピューターの処理は非常に非効率になります。

ここに画像の説明を挿入します
この問題を解決するために、マルチコルーチンと呼ばれる非プリエンプティブな非同期テクノロジが作成されました (ここで、マルチは複数の意味です)。

その原理は、タスクの実行中に待機が発生した場合、最初に他のタスクを実行し、待機が終了すると戻って前のタスクを続行するというものです。コンピューターの世界では、この種のタスクの切り替えが非常に速く、複数のタスクが同時に実行されているように見えます。

これは、食卓を作りたいときに、炊飯器がご飯を蒸すのを待っている間に炒め物をするのと同じです。ご飯が炊き上がるのを待たずに、炒めてみましょう。あなたは同じ人間ですが、労働時間は短縮されました。マルチコルーチンが作業時間を短縮できる原理も同様です。

したがって、非同期クローラー メソッドを実装するには、複数のコルーチンを使用する必要があります。その助けを借りて、前述の「複数のクローラーに作業を行わせる」を実現できます。

そこで、マルチコルーチンをどのように使用するかという新たな疑問が生じます。

マルチコルーチンの使用法

ゲベントライブラリ

ここに画像の説明を挿入します
したがって、次に、gevent の使用法を理解し、8 つの Web サイト (Baidu、Sina、Sohu、Tencent、NetEase、iQiyi、Tmall、および Phoenix を含む) をクロールするマルチコルーチンのケースを実装します。

まず、前の同期クローラー方法を使用してこれら 8 つの Web サイトをクロールしてから、後で gevent の非同期クローラーと比較してみましょう。

実行する前に、左側のコードをよく読んでください。

import requests,time
#导入requests和time
start = time.time()
#记录程序开始时间

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']
#把8个网站封装成列表

for url in url_list:
#遍历url_list
    r = requests.get(url)
    #用requests.get()函数爬取网站
    print(url,r.status_code)
    #打印网址和抓取请求的状态码

end = time.time()
#记录程序结束时间
print(end-start)
#end-start是结束时间减去开始时间,就是最终所花时间。
#最后,把时间打印出来。

操作結果:

https://www.baidu.com/ 200
https://www.sina.com.cn/ 200
http://www.sohu.com/ 200
https://www.qq.com/ 200
https://www.163.com/ 200
http://www.iqiyi.com/ 200
https://www.tmall.com/ 200
http://www.ifeng.com/ 200
1.7253923416137695

プログラムを実行すると、同期クローラー メソッドが表示されます。このメソッドは、Web サイトを順番にクロールし、サーバーの応答 (ステータス コード 200 は正常な応答を示します) を待ってから、次の Web サイトにクロールします。たとえば、最初の URL は、最初に Baidu の URL をクロールし、サーバーの応答を待ち、次に Sina の URL をクロールするというように、すべてのクロールが完了するまで続けます。

クローラーがタスクを完了するのにかかる時間をより直観的に確認できるようにするために、プログラムの開始時間と終了時間を記録するための時間モジュールをインポートしました。最終的な出力は、クローラーがクロールするのにかかった時間です。この8つのウェブサイトです。

クロールに複数のコルーチンを使用すると何が変わるでしょうか?

次のコードを実行して最初に確認できます (直接実行して体験してください)。

from gevent import monkey
monkey.patch_all()
import gevent,time,requests

start = time.time()

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

def crawler(url):
    r = requests.get(url)
    print(url,time.time()-start,r.status_code)

tasks_list = []

for url in url_list:
    task = gevent.spawn(crawler,url)
    tasks_list.append(task)
gevent.joinall(tasks_list)
end = time.time()
print(end-start)

操作結果:

https://www.baidu.com/ 0.13728904724121094 200
https://www.sina.com.cn/ 0.14090418815612793 200
https://www.163.com/ 0.1483287811279297 200
https://www.qq.com/ 0.1953425407409668 200
https://www.tmall.com/ 0.24243402481079102 200
http://www.ifeng.com/ 0.2494034767150879 200
http://www.sohu.com/ 0.3105161190032959 200
http://www.iqiyi.com/ 0.7928042411804199 200
0.7928953170776367

プログラムが実行されると、URL、各リクエストの実行時間、ステータス コード、および 8 つの Web サイトのクロールにかかった最終時間が出力されます。

各リクエストの実行時間から、各リクエストの完了時間が順序どおりではないため、クローラーが 8 つの Web サイトを非同期でクロールしたことがわかります。たとえば、このコードをテストして実行したとき、最初にクロールされた Web サイトは Sohu で、次に Phoenix であり、Baidu や Sina ではありませんでした。

また、各リクエストの完了時間の間隔は非常に短いため、これらのリクエストはほぼ「同時に」開始されると考えることができます。

同期クロールと非同期クロールに費やされた最終時間を比較すると、複数のコルーチンを使用した非同期クロール方法が同期クロール方法よりも実際に高速であることがわかります。

実際、このケースでクロールされたデータの量はまだ比較的少量であり、大きな速度差を直接反映することはできません。大量のデータをクロールする場合は、複数のコルーチンを使用すると速度が大幅に向上します。

たとえば、8 つの Web サイトのクロールから 80 の Web サイトのクロールに変更してテストを行ったところ、同期クロール方法を使用したクロールには約 17.3 秒かかりましたが、マルチコルーチン非同期クロールを使用したクロールには約 4.5 秒しかかかりませんでした。効率が 280% 以上向上しました。

ここに画像の説明を挿入します
ここで、gevent で使用したコードを 1 行ずつ見てみましょう。

注意: gevent ライブラリをインポートする前に、まずそれをインストールする必要があります。ローカル コンピュータで動作させたい場合は、ローカルにインストールする必要があります。(インストール方法: Windows コンピュータ: ターミナルにコマンドを入力: pip install gevent、Enter キーを押す; Mac コンピュータ: ターミナルにコマンドを入力: pip3 install gevent、Enter キーを押す)

from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests。

start = time.time()
#记录程序开始时间。

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']
#把8个网站封装成列表。

def crawler(url):
#定义一个crawler()函数。
    r = requests.get(url)
    #用requests.get()函数爬取网站。
    print(url,time.time()-start,r.status_code)
    #打印网址、请求运行时间、状态码。

tasks_list = [ ]
#创建空的任务列表。

for url in url_list:
#遍历url_list。
    task = gevent.spawn(crawler,url)
    #用gevent.spawn()函数创建任务。
    tasks_list.append(task)
    #往任务列表添加任务。
gevent.joinall(tasks_list)
#执行任务列表里的所有任务,就是让爬虫开始爬取网站。
end = time.time()
#记录程序结束时间。
print(end-start)
#打印程序最终所需时间。

上記のコードには gevent の構文が含まれていますが、まだ理解していない部分もあるかもしれません。パニックにならないでください。1 つずつ詳しく説明します。

ここに画像の説明を挿入します
コードの 1 行目と 3 行目: Monkey モジュールは gevent ライブラリからインポートされ、プログラムを非同期プログラムに変換できます。Monkey.patch_all() の機能は、実際にはコンピュータに「脆弱性を修正するためにパッチを使用しますか? それとも更新しますか?」というポップアップが表示されるのと似ています。プログラムにパッチを適用して、同期モードではなく非同期モードにすることができます。「モンキーパッチング」とも呼ばれます。

他のライブラリとモジュールをインポートする前に、monkey モジュールをインポートし、monkey.patch_all() を実行する必要があります。このようにして、プログラムに最初にパッチを適用できます。これが標準化された書き方であることも理解できます。

コードの 5 行目: マルチコルーチンの実装に役立つ gevent ライブラリ、クロールに必要な時間の記録に役立つ time モジュール、および 8 つの Web サイトのクロールに役立つ request モジュールをインポートしました。

ここに画像の説明を挿入します
コードの 21、23、25 行目: クローラー関数を定義しました。この関数が呼び出されると、[requests.get() で Web サイトをクロール] と [URL、リクエストの実行時間、ステータス コードを印刷] が実行されます。 ]タスク。

ここに画像の説明を挿入します
コードの 33 行目: gevent は gevent のタスク オブジェクトのみを処理でき、通常の関数を直接呼び出すことができないため、gevent.spawn() を使用してタスク オブジェクトを作成する必要があります。

ここで注意すべき点が 1 つあります。gevent.spawn() のパラメータは、呼び出される関数の名前とその関数のパラメータである必要があります。たとえば、gevent.spawn(crawler,url) は、クローラ関数を実行するタスクを作成します。パラメータは、クローラ関数名と独自のパラメータ URL です。

ここに画像の説明を挿入します
コードの 35 行目: append 関数を使用して、tasks_list のタスク リストにタスクを追加します。

コードの 37 行目: gevent ライブラリの joinall メソッドを呼び出すと、すべてのタスクの実行を開始できます。gevent.joinall(tasks_list) は、tasks_list タスク リスト内のすべてのタスクを実行し、クロールを開始します。

ここに画像の説明を挿入します
gevent を使用してマルチコルーチン クロールを実装する際の重要なポイントを要約します。

ここに画像の説明を挿入します
この時点で、gevent を実際に使用して 8 つの Web サイトをキャプチャすることが完了し、gevent の基本的な構文も大体理解できました。

では、8 つの Web サイトではなく 1,000 の Web サイトをクロールしたい場合はどうすればよいでしょうか?

学習したばかりの gevent 構文を使用すると、gevent.spawn() を使用して 1000 個のクロール タスクを作成し、gevent.joinall() を使用してこれらの 1000 個のタスクを実行できます。

ただし、この方法には問題があります。1,000 個のタスクを実行すると、一度に 1,000 個のリクエストが開始されることになり、そのような悪意のあるリクエストによって Web サイトのサーバーがダウンしてしまいます。

ここに画像の説明を挿入します
1,000 個のタスクを直接作成するこの方法はお勧めできません。5 つのタスクだけを作成して、各タスクが 200 個の Web サイトをクロールすることはできますか?

1,000 個のタスクがあり、5 つのタスクを作成すると、200 個の Web サイトをクロールする各タスクのコードは次のように記述できます (このコードは表示専用であり、実行できません)。

from gevent import monkey
monkey.patch_all()
import gevent,time,requests

start = time.time()
url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/'
……
#假设有1000个网址
]

def crawler(url_list):
#定义一个crawler()函数。
    for url in url_list:
        r = requests.get(url)
        print(url,time.time()-start,r.status_code)

tasks_list = [ ]
#创建空的任务列表。
for i in range(5):
    task = gevent.spawn(crawler,url_list[i*200:(i+1)*200])
    #用gevent.spawn()函数创建5个任务。
    tasks_list.append(task)
    #往任务列表添加任务。

gevent.joinall(tasks_list)
end = time.time()
print(end-start)

残念ながら、これにはまだ問題があるでしょう。gevent.spawn() を使用して 200 個の Web サイトをそれぞれクロールする 5 つのタスクを作成した場合でも、これら 5 つのタスクは非同期で実行されますが、各タスク (200 個の Web サイトをクロール) は内部的に同期されます。

これは、タスクが実行中で、クロールしたい Web サイトが応答を待っている場合、他のタスクが 200 個の Web サイトのクロールを完了したとしても、そのタスクはまだ 200 個の Web サイトのクロールを完了できないことを意味します。

ここに画像の説明を挿入します

この方法はうまくいきません。では、他にどのような方法があるでしょうか?

現時点では、実際の事例からインスピレーションを得ることができます。銀行が 1 日に 1,000 人の顧客をどのように処理するかを考えてみましょう。

銀行は業務を処理するために複数の窓口を開き、顧客が番号を取得するために列に並ぶことができるようにし、銀行の電話システムは業務を処理するために顧客を異なる窓口に割り当てます。

gevent ライブラリには、この関数キュー モジュールを実装できるモジュールもあります。

キューモジュール

マルチコルーチンを使用してクロールし、多数のタスクを作成する必要がある場合は、キュー モジュールを使用できます。

キューは中国語に訳すとキューとなります。キュー モジュールを使用すると、銀行窓口での番号付け方法と同じように、タスクを保存し、それらをきちんとしたキューに変えることができます。キューは実際には、データにアクセスするために使用できる順序付けされたデータ構造であるためです。

このようにして、コルーチンはキューからタスクを抽出し、キューが空になりタスクが完了するまで実行できます。銀行の窓口の職員が番号体系の番号に基づいて顧客の業務を処理するのと同じで、新しい番号がなければ、顧客の業務は処理されたことになります。

ここに画像の説明を挿入します

次に、キュー モジュールとコルーチンを連携させて使用する方法を実際に見てみましょう。例として 8 つの Web サイトをクロールします。

まず以下のコードを実行してください。

from gevent import monkey
monkey.patch_all()
import gevent,time,requests
from gevent.queue import Queue

start = time.time()

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

work = Queue()
for url in url_list:
    work.put_nowait(url)

def crawler():
    while not work.empty():
        url = work.get_nowait()
        r = requests.get(url)
        print(url,work.qsize(),r.status_code)

tasks_list  = [ ]

for x in range(2):
    task = gevent.spawn(crawler)
    tasks_list.append(task)
gevent.joinall(tasks_list)

end = time.time()
print(end-start)

操作結果:

https://www.sina.com.cn/ 6 200
https://www.baidu.com/ 5 200
https://www.qq.com/ 4 200
http://www.sohu.com/ 3 200
https://www.163.com/ 2 200
https://www.tmall.com/ 1 200
http://www.ifeng.com/ 0 200
http://www.iqiyi.com/ 0 200
0.9640278816223145

URL の後の数字は、キューに残っているタスクの数を示します。たとえば、最初の URL の後の数字 6 は、他の URL をクロールするためのタスクがキューに 6 つ残っていることを意味します。

さて、今実行したコードを 4 つの部分に分割して説明しましょう。最初の部分はモジュールのインポートです。

from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests
from gevent.queue import Queue
#从gevent库里导入queue模块

gevent ライブラリにはキューがあるため、[from gevent.queue import Queue] を使用してキュー モジュールをインポートできます。他のモジュールやコードについてはgeventの説明の際に説明しましたので、理解していただけると思います。

パート 2 では、キューを作成し、タスクをキューに格納する方法について説明します。

start = time.time()
#记录程序开始时间

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

work = Queue()
#创建队列对象,并赋值给work。
for url in url_list:
#遍历url_list
    work.put_nowait(url)
    #用put_nowait()函数可以把网址都放进队列里。

Queue() を使用してキュー オブジェクトを作成できます。これは、ストレージ量に制限のない空のキューを作成するのと同じです。Queue(10) などのパラメータを Queue() に渡す場合、このキューには 10 個のタスクしか保存できないことを意味します。

キュー オブジェクトを作成した後、このオブジェクトの put_nowait メソッドを呼び出して、作成したばかりの空のキューに各 URL を保存できます。

このコード行 work.put_nowait(url) は、通過した 8 つの Web サイトすべてをキューに保存します。

パート 3 では、クローリング関数と、キューに保存された URL を抽出する方法を定義します。

def crawler():
    while not work.empty():
    #当队列不是空的时候,就执行下面的程序。
        url = work.get_nowait()
        #用get_nowait()函数可以把队列里的网址都取出。
        r = requests.get(url)
        #用requests.get()函数抓取网址。
        print(url,work.qsize(),r.status_code)
        #打印网址、队列长度、抓取请求的状态码。

ここで定義されているクローラー関数には、理解できない可能性のあるさらに 3 つのコードがあります:
1.while not work.empty():
2.url = work.get_nowait()
3.work.qsize()。

これらの 3 つのコードには、キュー オブジェクトの 3 つのメソッドが含まれています。empty
メソッドは、キューが空かどうかを判断するために使用される get_nowait メソッドです。qsize
メソッドは、queue からデータを抽出するために使用され
、キューの量を判断するために使用されます。キューに残っている数量。

もちろん、キュー オブジェクトにはこれらのメソッドだけではなく、たとえば、キュ​​ーが空かどうかを判断する empty メソッドや、キューがいっぱいかどうかを判断する対応する full メソッドもあります。

キュー オブジェクトにはメソッドが多すぎて、一度にすべてを覚えることができないと思いませんか? 実は丸暗記する必要はなく、キューオブジェクトのメソッドテーブルが付属しているので、使用するときに参照するだけで済みます。

ここに画像の説明を挿入します
コードの最初の 3 つの部分の説明が終わりました。キューの作成方法、キューにデータを格納する方法、キューからデータを抽出する方法を理解できれば、キュー モジュールの主要な内容を習得したことになります。

ここに画像の説明を挿入します
パート 3 のコードに従うと、クローラーがマルチコルーチンを使用してタスクを実行し、キュー内の 8 つの Web サイトのコードをクロールできるようになります (コメントされたコードに注目してください)。

def crawler():
    while not work.empty():
        url = work.get_nowait()
        r = requests.get(url)
        print(url,work.qsize(),r.status_code)

tasks_list  = [ ]
#创建空的任务列表
for x in range(2):
#相当于创建了2个爬虫
    task = gevent.spawn(crawler)
    #用gevent.spawn()函数创建执行crawler()函数的任务。
    tasks_list.append(task)
    #往任务列表添加任务。
gevent.joinall(tasks_list)
#用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。
end = time.time()
print(end-start)

このプロセスは図で説明できます。

ここに画像の説明を挿入します
非同期にクロールできる 2 つのクローラーを作成しました。彼らはキューから URL を取得し、クロール タスクを実行します。ある URL が 1 つのクローラによって取得されると、別のクローラによってその URL を取得することはできなくなり、別のクローラが次の URL を取得します。すべての URL が取得され、キューが空になるまで、クローラーは動作を停止します。

コルーチン テクノロジーとキューを使用して 8 つの Web サイトをクロールするための完全なコードは次のとおりです。

from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests
from gevent.queue import Queue
#从gevent库里导入queue模块

start = time.time()

url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']

work = Queue()
#创建队列对象,并赋值给work。
for url in url_list:
#遍历url_list
    work.put_nowait(url)
    #用put_nowait()函数可以把网址都放进队列里。

def crawler():
    while not work.empty():
    #当队列不是空的时候,就执行下面的程序。
        url = work.get_nowait()
        #用get_nowait()函数可以把队列里的网址都取出。
        r = requests.get(url)
        #用requests.get()函数抓取网址。
        print(url,work.qsize(),r.status_code)
        #打印网址、队列长度、抓取请求的状态码。

tasks_list  = [ ]
#创建空的任务列表
for x in range(2):
#相当于创建了2个爬虫
    task = gevent.spawn(crawler)
    #用gevent.spawn()函数创建执行crawler()函数的任务。
    tasks_list.append(task)
    #往任务列表添加任务。
gevent.joinall(tasks_list)
#用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。
end = time.time()
print(end-start)

上記のコードを入力してみてください (クロールを練習しない人は単なる不正者です)。

行動を起こせば必ず利益が得られます。おめでとうございます。このレベルの核となる知識、つまりマルチコルーチンを実装するための gevent ライブラリと Queue モジュールの学習が完了しました。

拡張レビュー

しかし、私はまた、あなたと一緒にいくつかの新しい知識を広げたいと思っています。

料理に関しては、最初に料理をしてから料理するよりも、待っている間に料理をする方が良い方法であることはすでにわかっています。しかし、実際にはもっと早い解決策があります。それは、1 人が料理を担当し、もう 1 人が料理を担当するというものです。

コンピューターの歴史についてのわずかな知識を続けます。その後、CPU は最終的にシングルコアからマルチコアに進化し、各コアは独立して動作できるようになりました。コンピューターは、複数のタスクを行ったり来たりする (この用語は同時実行と呼ばれます) のではなく、実際に複数のタスクを同時に実行できるようになりました (この用語は並列実行と呼ばれます)。

たとえば、ブラウザを開いてクローラーコースを視聴しているときに、音楽プレーヤーを開いて曲を聴くこともできますし、Excel を開くこともできます。マルチコア CPU の場合、これらのタスクはすべて同時に実行されます。

現在、私たちのコンピュータにはマルチコア CPU が搭載されているのが一般的です。マルチコルーチンは、実際には実行に CPU の 1 つのコアのみを使用し、他のコアを完全には利用しません。CPUの複数のコアを使って同時にタスクを実行する技術を「マルチプロセッシング」といいます。

したがって、真に大規模なクローラー プログラムは、クロール速度を向上させるために複数のコルーチンだけに依存することはありません。たとえば、Baidu 検索エンジンは非常に大規模なクローラー プログラムであると言え、複数のコルーチンに依存するだけでなく、複数のプロセスや分散クローラーにも依存します。

マルチプロセス クローラーと分散クローラーは比較的複雑なので、ここでは詳しく説明しません。高度な学習が必要な学生は、自分で研究を行うことができます。

最後にこのレベルのレビューです。

レビュー

同期と非同期 -
ここに画像の説明を挿入します
マルチコルーチンは、非プリエンプティブな非同期メソッドです。マルチコルーチンを使用すると、複数のクローリング タスクを交互に非同期で実行できます。

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
これらの知識ポイントは以上です。次のレベルでお会いしましょう!

おすすめ

転載: blog.csdn.net/qq_41308872/article/details/132662106