若是說socket是全部web操做的本質,爬蟲在web操做中扮演的角色,應該就是一個「客戶端」。python
obj=socket() obj.connect((192.168.1.1,80)) # socket 鏈接是一個耗時阻塞的操做 obj.send('get /url http1.1 \r\n host:...\r\n content-type \r\n\r\n') # 經過\r\n將請求參數分割開按照必定格式發送給服務端
下面是一段用socket向百度發送請求的實例:web
import socket client=socket.socket() client.connect(('61.135.169.125',80)) # 阻塞 data=b'GET / HTTP/1.0\r\nhost: www.baidu.com\r\n\r\n' # 請求,訪問的url(這裏無,因此用/)而後是協議:http1.1 # 最後必定要記得用\r\n分割開,由於服務器靠這個來split信息 client.sendall(data) response=client.recv(8096) # 阻塞 print(response) client.close()
能夠看到我在上面兩個地方標註了「阻塞」。服務器
若是想要讓這個socket不發生「阻塞」,其實只須要app
client.setblocking(False)
可是這種操做帶來的問題是:異步
BlockingIOError: [WinError 10035] 沒法當即完成一個非阻止性套接字操做。
暫時不考慮這個錯誤,咱們其實已經將這個socket作成了一個「非阻塞」的socket程序了,想要讓他正常的運行,能夠經過time模塊來模擬一些其餘計算耗時或者叫作「異步任務」。
代碼以下:socket
client=socket.socket() client.setblocking(False) try: client.connect(('61.135.169.125',80)) # 阻塞 except BlockingIOError as e: print(e) import time time.sleep(3) # 假設這裏有一個耗時3秒的操做,操做完後client.connect已經完成 data=b'GET / HTTP/1.0\r\nhost: www.baidu.com\r\n\r\n' client.sendall(data) time.sleep(3) # 假設這裏又有一個操做,結束後client已經獲取了服務器返回值 response=client.recv(8096) # 阻塞 print(response) client.close()
觀察上面代碼的結果,能夠發現鏈接一開始被判斷不成功的時候的確返回了錯誤信息,可是最終咱們仍是獲取到了網頁的信息,說明最後鏈接完成後的整個流程是被正確地走完了的。其中的sleep能夠替換成任何其餘操做,能夠去讀取獲取文件,進行計算等等……url
經過合理安排各個任務,既節約了任務的時間,而且完成了多任務的效果。這就是異步IOcode
可是咱們怎麼樣判斷「第一個任務(鏈接)阻塞的期間,咱們能夠完成多少別的任務」,「假如咱們的支線任務作完後,主線任務仍然沒有結束,咱們該幹什麼」。
利用while循環判斷主線任務是否返回,若是返回了值,就break進行下一步,若是沒有,就繼續進行支線任務對象
總結:
非阻塞---報錯---利用try讓程序繼續運行
定義一些操做(把全部的請求,在第一個請求發送的等待期間,所有發送過去。)utf-8
用於檢測【多個】IO對象(socket對象)是否有變化。
r,w,e=selcet.select([socket,socket.....],[],[],0.5) # 第一個傳入參數爲socket對象列表 #
完成代碼:
import socket import select class ReqIO(object): def __init__(self,sock,info): self.sock=sock self.info=info def fileno(self): return self.sock.fileno() class IOtest(object): def __init__(self): self.socklist=[] self.conns=[] def add_request(self,req_info): sock=socket.socket() sock.setblocking(False) try: sock.connect((req_info['host'],req_info['port'])) except Exception as e: pass obj=ReqIO(sock,req_info) self.socklist.append(obj) self.conns.append(obj) def run(self): while True: r,w,e=select.select(self.socklist,self.conns,[],0.05) """ 補充:select中添加的能夠是任何對象,可是這個對象必定要有fileno方法 w 是否鏈接成功 檢查循環到的i是哪一個字典 """ for i in w: data = 'GET %s HTTP/1.0\r\nhost: %s\r\n\r\n'%(i.info['path'],i.info['host']) i.sock.send(data.encode('utf-8')) self.conns.remove(i) """ 這時候w中的元素是ReqIO對象,可是他仍然可以鏈接成功, 而且他會利用封裝過的info來獲取當前i的info """ for j in r: """ 數據返回接收數據 """ response = j.sock.recv(8096) print(j.info['host'],':\r\n',response) self.socklist.remove(j) if not self.socklist: # 當全部請求都已經返回 break url_list=[ {'host':'www.baidu.com','IP':'61.135.169.125','port':80,'path':'/'}, {'host':'dig.chouti.com','IP':'111.206.193.95','port':80,'path':'/'}, {'host':'www.bing.com','IP':'118.178.213.186','port':80,'path':'/'} ] test=IOtest() for item in url_list: test.add_request(item) test.run()