Pythonのマルチスレッドのクローラ

マルチスレッドのクローラ

時間のかかる操作であるため、このような画像のダウンロードなど、いくつかのケースでは、写真をダウンロードしてください。場合の方法の前に同期することをダウンロードしてください。その効率は、特に遅くなるために喜んでです。今回は、画像をダウンロードするには、マルチスレッドの方法の使用を考慮することができます。

マルチスレッドの説明:

マルチスレッドは、リソース使用の効率を改善することにより、システムの効率を改善するために複数のタスクを同期させることです。スレッドは、時間のタスクの数を完了するために同じ時間が必要で実装されています。
最も単純な類推は、列車の各キャリッジのためのマルチスレッドのようなものである、プロセスは列車です。列車は、キャリッジが実行されていないままに、そして共感も馬車を訓練することができますか。マルチスレッドは効率が発生し改善することです。同時に、また、いくつかの問題をもたらした表示されます。:もっと説明を参照してくださいhttps://baike.baidu.com/item/マルチスレッド/ 1190404 FR =アラジン?

モジュールの説明をスレッド:

threadingモジュールは、pythonマルチスレッドプログラミングモジュールを作るための特別提供。threading最も一般的なモジュールのクラスがありますThreadシンプルなマルチスレッドプログラムで次の見て:

import threading
import time

def coding(): for x in range(3): print('%s正在写代码' % x) time.sleep(1) def drawing(): for x in range(3): print('%s正在画图' % x) time.sleep(1) def single_thread(): coding() drawing() def multi_thread(): t1 = threading.Thread(target=coding) t2 = threading.Thread(target=drawing) t1.start() t2.start() if __name__ == '__main__': multi_thread() 

スレッドの数を表示します。

使用するthreading.enumerate()関数は、現在のスレッドの数を見ることができます。

現在のスレッドの名前を確認します。

使用すると、threading.current_thread()現在のスレッドの情報を見ることができます。

継承されthreading.Threadたクラス:

より良いカプセル化スレッドコードするためには。使用できるthreadingモジュールでThreadクラスクラスから継承し、次に実装run方法を、スレッドが自動的に実行されrunたコードの方法を。次のようにサンプル・コードは次のとおりです。

import threading
import time

class CodingThread(threading.Thread): def run(self): for x in range(3): print('%s正在写代码' % threading.current_thread()) time.sleep(1) class DrawingThread(threading.Thread): def run(self): for x in range(3): print('%s正在画图' % threading.current_thread()) time.sleep(1) def multi_thread(): t1 = CodingThread() t2 = DrawingThread() t1.start() t2.start() if __name__ == '__main__': multi_thread() 

グローバル変数を共有するマルチスレッド:

マルチスレッドは、同じプロセスで実行されています。そのため、グローバル変数のプロセスで共有することができるすべてのスレッドです。実行スレッドの順序が順序付けられていないので、これは、問題を作成します。これは、データエラーが発生することがあります。たとえば、次のコード:

import threading

tickets = 0

def get_ticket(): global tickets for x in range(1000000): tickets += 1 print('tickets:%d'%tickets) def main(): for x in range(2): t = threading.Thread(target=get_ticket) t.start() if __name__ == '__main__': main() 

これらの結果は、6の観点ではなく、複数のスレッドが不確実性を実行しているので、通常でなければなりません。だから、最終的な結果は、ランダムであってもよいです。

ロックメカニズム:

共有グローバル変数を使用して上記の問題を解決するために。threading提供しLock、彼らが治療に入る前に、ロックが解除されるが、現在のスレッドが処理を完了するまで、他のスレッドが、この時点でで来ることができない、他のスレッドをスレッドロック内の変数にアクセスすることができ、時間のクラスを。次のようにサンプル・コードは次のとおりです。

import threading

VALUE = 0

gLock = threading.Lock()

def add_value(): global VALUE gLock.acquire() for x in range(1000000): VALUE += 1 gLock.release() print('value:%d'%VALUE) def main(): for x in range(2): t = threading.Thread(target=add_value) t.start() if __name__ == '__main__': main() 

生産者と消費者モードのバージョンをロック:

生産者と消費者のモデルは、マルチスレッド開発のためのモデルがしばしば見られています。糸の生産は、データの一部を生成するように設計し、次に中間変数に格納されています。消費者は、その後、消費のための変数の途中からデータを削除します。しかし、中間変数の使用のために、中間変数は、多くの場合、いくつかのグローバル変数であるため、データの整合性を確保するためにロックを使用する必要があります。ここでthreading.Lock実装され、「生産者-消費者モデル」ロックの例:

import threading
import random
import time

gMoney = 1000
gLock = threading.Lock()
# 记录生产者生产的次数,达到10次就不再生产 gTimes = 0 class Producer(threading.Thread): def run(self): global gMoney global gLock global gTimes while True: money = random.randint(100, 1000) gLock.acquire() # 如果已经达到10次了,就不再生产了 if gTimes >= 10: gLock.release() break gMoney += money print('%s当前存入%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney)) gTimes += 1 time.sleep(0.5) gLock.release() class Consumer(threading.Thread): def run(self): global gMoney global gLock global gTimes while True: money = random.randint(100, 500) gLock.acquire() if gMoney > money: gMoney -= money print('%s当前取出%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney)) time.sleep(0.5) else: # 如果钱不够了,有可能是已经超过了次数,这时候就判断一下 if gTimes >= 10: gLock.release() break print("%s当前想取%s元钱,剩余%s元钱,不足!" % (threading.current_thread(),money,gMoney)) gLock.release() def main(): for x in range(5): Consumer(name='消费者线程%d'%x).start() for x in range(5): Producer(name='生产者线程%d'%x).start() if __name__ == '__main__': main() 

生産者と消費者モードのコンディション版:

Lock生産者と消費者のバージョンは、通常の動作モードすることができます。しかし、常にを通じて、消費者の間で不足し、そこにあるwhile True死のサイクルと十分なお金を判断するロック方法。ロックは非常にCPU集中型の動作です。だから、これは最善の方法ではありません。もっと良い方法があることを使用することでthreading.Condition達成します。threading.Conditionこれは、データが存在しない状態で待機状態にブロックすることができます。適切なデータたら、あなたも使用することができnotify、待機状態で他のスレッドに通知するために、関連する機能を。あなたは、いくつかの役に立たないロックとアンロック操作を行うことができないように。あなたは、プログラムのパフォーマンスを向上させることができます。最初のthreading.Condition関連する機能は、導入されるthreading.Condition同様のthreading.Lock修正が完了した後、修飾されたグローバルデータにロックすることができ、また、ロック解除することができます。いくつかの一般的に使用される機能は、簡単な紹介になります。

  1. acquire:ロックされました。
  2. release:ロックを解除します。
  3. wait:現在の待機状態のスレッドは、ロックを解除します。これは、他のスレッドかもしれnotifyおよびnotify_allウェイクアップ機能。目覚めた後、私たちは、ロックを待つ次のコードロックを継続していきます。
  4. notify:待機中のスレッドの通知は、デフォルトでは、スレッドを待っている最初のものです。
  5. notify_all:すべての待機中のスレッドに通知。notifyそして、notify_allロックを解除しません。そして、する必要性releaseの前に呼び出します。

Condition次のように生産者 - 消費者モデルコードのバージョンは以下のとおりです。

import threading
import random
import time

gMoney = 1000
gCondition = threading.Condition()
gTimes = 0 gTotalTimes = 5 class Producer(threading.Thread): def run(self): global gMoney global gCondition global gTimes while True: money = random.randint(100, 1000) gCondition.acquire() if gTimes >= gTotalTimes: gCondition.release() print('当前生产者总共生产了%s次'%gTimes) break gMoney += money print('%s当前存入%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney)) gTimes += 1 time.sleep(0.5) gCondition.notify_all() gCondition.release() class Consumer(threading.Thread): def run(self): global gMoney global gCondition while True: money = random.randint(100, 500) gCondition.acquire() # 这里要给个while循环判断,因为等轮到这个线程的时候 # 条件有可能又不满足了 while gMoney < money: if gTimes >= gTotalTimes: gCondition.release() return print('%s准备取%s元钱,剩余%s元钱,不足!'%(threading.current_thread(),money,gMoney)) gCondition.wait() gMoney -= money print('%s当前取出%s元钱,剩余%s元钱' % (threading.current_thread(), money, gMoney)) time.sleep(0.5) gCondition.release() def main(): for x in range(5): Consumer(name='消费者线程%d'%x).start() for x in range(2): Producer(name='生产者线程%d'%x).start() if __name__ == '__main__': main() 

スレッドセーフなキューをキュー:

スレッドでは、グローバル変数へのアクセスは、ロックは、通常のプロセスです。あなたはいくつかのデータがキューに格納されている場合は、それはスレッドセーフと呼ばれるPythonモジュール内蔵しqueue、モジュールを。PythonのキューモジュールはFIFO(ファーストインファーストアウト)キューのキューを含む、同期キューベースのスレッドセーフを提供し、LIFO(最初に出て、内の最後の)キューLifoQueue。これらのキューは、(または実行しないことをアトミック動作として理解することができる、または行っている)プリミティブをロック実装されている複数のスレッドに直接使用することができます。あなたは、スレッド間の同期を達成するためにキューを使用することができます。関連する次のように機能します。

  1. 初期キュー(MAXSIZE)は:FIFOキューを作成します。
  2. QSIZEは():キューのサイズを返します。
  3. 空の():キューが空であるかどうか。
  4. フル():キューが満杯であるかどうか。
  5. 取得():キューから最後のデータを取ります。
  6. )(置く:データをキューに。

生産者と消費者は、マルチスレッドダウンロードモード式パッケージを使用するには:

import threading
import requests
from lxml import etree
from urllib import request import os import re from queue import Queue class Producer(threading.Thread): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' } def __init__(self,page_queue,img_queue,*args,**kwargs): super(Producer, self).__init__(*args,**kwargs) self.page_queue = page_queue self.img_queue = img_queue def run(self): while True: if self.page_queue.empty(): break url = self.page_queue.get() self.parse_page(url) def parse_page(self,url): response = requests.get(url,headers=self.headers) text = response.text html = etree.HTML(text) imgs = html.xpath("//div[@class='page-content text-center']//a//img") for img in imgs: if img.get('class') == 'gif': continue img_url = img.xpath(".//@data-original")[0] suffix = os.path.splitext(img_url)[1] alt = img.xpath(".//@alt")[0] alt = re.sub(r'[,。??,/\\·]','',alt) img_name = alt + suffix self.img_queue.put((img_url,img_name)) class Consumer(threading.Thread): def __init__(self,page_queue,img_queue,*args,**kwargs): super(Consumer, self).__init__(*args,**kwargs) self.page_queue = page_queue self.img_queue = img_queue def run(self): while True: if self.img_queue.empty(): if self.page_queue.empty(): return img = self.img_queue.get(block=True) url,filename = img request.urlretrieve(url,'images/'+filename) print(filename+' 下载完成!') def main(): page_queue = Queue(100) img_queue = Queue(500) for x in range(1,101): url = "http://www.doutula.com/photo/list/?page=%d" % x page_queue.put(url) for x in range(5): t = Producer(page_queue,img_queue) t.start() for x in range(5): t = Consumer(page_queue,img_queue) t.start() if __name__ == '__main__': main() 

GILグローバルインタプリタロック:

Pythonは、通訳が付属していますCPythonCPythonマルチスレッドインタプリタは(マルチコアCPU、あなただけの1つのコアを使用することができ、マルチコアの利点を取ることができない)、実際に偽のマルチスレッドです。同時に一つだけのスレッドの実行時に、実行中にその一つだけのスレッドを保証するために、にCPython通訳というものGIL(Global Intepreter Lock)、グローバルインタプリタロックと呼ばれます。インタプリタロックが必要です。そのためCPython、インタプリタのメモリ管理はスレッドセーフではありません。もちろん、他にCPython通訳だけでなく、他の通訳、いくつかの説明は一切ありませんGILロック、以下を参照してください。

  1. Jython:PythonインタプリタのJava実装。GILロックは存在しません。詳細については、以下を参照してください。https://zh.wikipedia.org/wiki/Jython
  2. IronPython.netインタプリタはPythonで実装されています。GILロックは存在しません。詳細については、以下を参照してください。https://zh.wikipedia.org/wiki/IronPython
  3. PyPyPythonインタプリタはPythonで実装されています。GILロックが存在します。詳細については、以下を参照してください。https://zh.wikipedia.org/wiki/PyPyを
    マルチスレッドGILそれは偽物ですが。しかし、(そのようなファイルの読み取りおよび書き込み要求やネットワークなど)いくつかのIO操作を扱うにはまだ大幅に効率を向上させることができます。効率を向上させるマルチスレッドIOオペレーションの使用に関する勧告。いくつかのCPUコンピューティング操作上では、マルチスレッド、マルチプロセスの使用と提案された使用を推奨していません。

ベストではないマルチスレッドダウンロードスクリプト姉妹操作:

import requests
from lxml import etree
import threading
from queue import Queue import csv class BSSpider(threading.Thread): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' } def __init__(self,page_queue,joke_queue,*args,**kwargs): super(BSSpider, self).__init__(*args,**kwargs) self.base_domain = 'http://www.budejie.com' self.page_queue = page_queue self.joke_queue = joke_queue def run(self): while True: if self.page_queue.empty(): break url = self.page_queue.get() response = requests.get(url, headers=self.headers) text = response.text html = etree.HTML(text) descs = html.xpath("//div[@class='j-r-list-c-desc']") for desc in descs: jokes = desc.xpath(".//text()") joke = "\n".join(jokes).strip() link = self.base_domain+desc.xpath(".//a/@href")[0] self.joke_queue.put((joke,link)) print('='*30+"第%s页下载完成!"%url.split('/')[-1]+"="*30) class BSWriter(threading.Thread): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' } def __init__(self, joke_queue, writer,gLock, *args, **kwargs): super(BSWriter, self).__init__(*args, **kwargs) self.joke_queue = joke_queue self.writer = writer self.lock = gLock def run(self): while True: try: joke_info = self.joke_queue.get(timeout=40) joke,link = joke_info self.lock.acquire() self.writer.writerow((joke,link)) self.lock.release() print('保存一条') except: break def main(): page_queue = Queue(10) joke_queue = Queue(500) gLock = threading.Lock() fp = open('bsbdj.csv', 'a',newline='', encoding='utf-8') writer = csv.writer(fp) writer.writerow(('content', 'link')) for x in range(1,11): url = 'http://www.budejie.com/text/%d' % x page_queue.put(url) for x in range(5): t = BSSpider(page_queue,joke_queue) t.start() for x in range(5): t = BSWriter(joke_queue,writer,gLock) t.start() if __name__ == '__main__': main()

おすすめ

転載: www.cnblogs.com/csnd/p/11469326.html