最近拜讀瑞安·米切爾的書關于并行抓取問題有很通俗介紹:
“網頁抓去的速度很快,起碼通常比雇傭幾十個實習生手動網上復制資料要快很多,當然隨著技術的不斷進步和享樂適應,人們還是在某個時刻覺得‘不夠快’,于是把目光轉向分布式計算,
和其他領域不同的是,網頁抓取不能單純依靠‘給問題增加更多行程’來提升速度,雖然運行一個process很快,但是兩個行程未必能讓速度提升一倍,而當運行三個乃更多時,可能你的所有請求都會被遠程服務器封殺,因為他認為你是在惡意攻擊,”
然而,某些場景里使用網頁并行抓取或者并行執行緒(thread)/行程仍然有些好處:
1.從多個資料源(多個遠程服務器),而不只是在一個資料源收集資料,
2.在已經收集到的資料上執行更加復雜/執行時間更長的操作(例如影像分析或者OCR處理),
3.從大型web服務器收集資料,如果你已經付費,或者創建多個連接是使用協議允許的行為,
一.pythom3.x 使用的是_thread,而thread模塊已經廢棄
1.1下面用_thread實作一個小例子
1 import time 2 import _thread 3 def print. time(threadName, delay, iterations): 4 5 start = int(time . time()) 6 7 for i in range(0 ,iterations): 8 9 time .sleep(delay) 10 11 seconds_ elapsed = str(int(time.time()) - start) 12 13 print ("[] []". format(seconds_ elapsed, threadName)) 14 15 try: 16 _thread.start_new_thread(print_time, ('Fizz', 3, 33)) 17 _thread.start_new_thread(print_time, ('Buzz',5,20)) 18 _thread.start_new_thread(print_time,('Counter',1,100)) 19 except: 20 21 print ('Error:unable to start_thread') 22 23 while 1: 24 25 pass
上面的例子開啟了三個執行緒,分別3,5,1秒列印不同次數的名字
1.2 threading 模塊
_thread是python相當底層的模塊,雖然可以有詳細的管理操作,但是苦于沒有高級函式,使用起來不太方便,
threadign模塊是一個高級介面,更加便捷使用執行緒,與此同時也體現了_thread模塊的特性,
在threading與os模塊里,分別呼叫了current_thread()和getpid() 該函式來獲取當前的執行緒/行程號,能有更好的執行效果,
1 import os 2 import threading 3 from threading import Thread 4 5 import time 6 7 userlist = ["黎明", "張學友", "劉德華"] 8 9 10 def work(n): 11 print(f"開始給{n}打電話,行程號:{os.getpid()},執行緒號是:{threading.current_thread()}") # 這里加f 是字串格式化,作用類似于format 12 time.sleep(3) 13 print(f"給{n}打電話結束,行程號:{os.getpid()}執行緒號是:{threading.current_thread()}") 14 15 16 if __name__ == '__main__': 17 # 單執行緒 18 # for d in userlist: 19 # work(d) 20 plist = [] 21 # # 回圈創建多行程部門,類似增加 22 # for d in userlist: 23 # # 行程物件 24 # p = Process(target=work, args=(d,)) 25 # # 生成行程 26 # p.start() 27 # # 生成的行程加入串列 28 # plist.append(p) 29 # 30 # # 阻塞終止行程的執行 31 # [i.join() for i in plist] 32 33 # 多執行緒類似部門增加人手 34 for d in userlist: 35 # 利用for來生成執行緒物件,然后再為它們傳入你想要傳入的資料(可以不一樣) 36 p = Thread(target=work, args=(d,)) 37 # 生成執行緒 38 p.start() 39 # 生成的執行緒加入串列 40 plist.append(p) 41 42 # 阻塞終止執行緒的執行 43 [i.join() for i in plist]
你也可以單個的創建,threading的優勢是創建的執行緒其他執行緒無法訪問的執行緒區域資料(local thread data),這樣的優勢對于爬蟲尤其明顯,它們抓取不同的網站,那么每個執行緒都可以
專注于自己要抓取的目標,
import threading def crawler(url): data = threading.local() data.visited = [] #爬去目標網站 threading.Thread(target=crawler, args=('http://www.douban.com')).start()
這樣就解決了執行緒之間的共享物件導致競爭條件問題,目標很明顯:不需要共享就不要共享(使用local data),為了安全的共享,就需要用到Queue模塊(后面會有示例)
threading的保姆職責甚至可以達到高度定制:isAlive函式的默認行為查看是否有執行緒仍處于活躍狀態,當一個執行緒崩潰或者重啟后才會回傳True:
threading.Thread(target=crawler) t.start() while True: time.sleep(1) if not t.isAlive:
t = Thread(target=crawler, args=(d,))
t.start()
這樣可以達到簡單的監控方法,
1.3 還有創建執行緒池的方式,
明天補上,這回宿舍兄弟叫我嘮嗑了,嘿嘿,
2021-04-01
19:36:13
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/271132.html
標籤:其他
