阻塞: 在单线程下,当要执行两个相同的操作时,必须等前一个操作完成后,后一个操作才能执行
非阻塞: 在单线程下,当要执行两个相同的操作时,不必等待前一个操作完成,直接继续执行下去,也就是可以并发
异步:当达到某种状态后自动调用回调函数,我们在向服务端发送请求时,给它传递一个函数,当它处理完请求时,执行这个函数,站在客户端的角度上就是异步
阻塞和非阻塞 关键看是否等待前一个操作完成
异步:看是否有回调函数
自定义异步非阻塞
这个是基于socket设置setblocking(ffalse)和io多路复用实现的
爬虫发送HTTP的本质就是在创建socket
io多路复用循环监听socket是否发生变化,一旦发生变化,可以触发我们自定义的操作
##实现单线程下可以发送很多请求 import socket import select class Request(object): def __init__(self,sk,callback): self.sk=sk self.callback=callback def fileno(self): #这个函数是为了多路复用用的 return self.sk.fileno() class Async: def __init__(self): self.fds=[]#监听是socket, self.conn=[]#监听连接成功 def add(self,url,callback): """ 创建socket连接 :param url: :return: """ sk=socket.socket() sk.setblocking(False)#这样写就是非阻塞了 # sk.callback=callback try: sk.connect((url,80)) except BlockingIOError as e:#当没连接成功就会报错 pass req=Request(sk,callback)#把socket和callback封装到Request的对象中 self.fds.append(req) self.conn.append(req) def run(self): """ '监听socket的状态变化' :return: """ while True: #监听是否连接成功 r,w,e=select.select(self.fds,self.conn,[],0.05)#一旦连接成功后w就会有值了,#w表示已经连接成功的socket,多路复用其实是监听的socket.fileno()函数 # w表示已经连接成功的socket for req in w: #连接成功后发数据 req.sk.sendall( b'GET /wupeiqi HTTP/1.1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' b'Chrome/63.0.3239.132 Safari/537.36\r\n\r\n') #连接成功后,不用再监听了. self.conn.remove(req) #如果服务端给用户返回数据了, r里面有值表示服务端发回来数据了 for req in r: #获取到响应数据 data=req.sk.recv(8096) req.callback(data)#调用回调函数 #获取完数据后,断开连接 req.sk.close() #断开监听 self.fds.remove(req) if not self.fds: break ah=Async() def callback1(data): print('callback1',data) def callback2(data): print('callback2',data) def callback3(data): print('callback3',data) ah.add('www.cnblogs.com',callback1) # sk1 ah.add('www.baidu.com',callback2) # sk2 ah.add('www.luffycity.com',callback3) # sk3 ah.run()
结果:
callback2 b'HTTP/1.1 302 Found\r\nCache-Control: max-age=86400\r\nConnection: Keep-Alive\r\nContent-Length: 222\r\nContent-Type: text/html; charset=iso-8859-1\r\nDate: Thu, 07 Jun 2018 08:20:48 GMT\r\nExpires: Fri, 08 Jun 2018 08:20:48 GMT\r\nLocation: https://www.baidu.com/search/error.html\r\nServer: Apache\r\n\r\n<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>302 Found</title>\n</head><body>\n<h1>Found</h1>\n<p>The document has moved <a href="http://www.baidu.com/search/error.html">here</a>.</p>\n</body></html>\n' callback3 b'HTTP/1.1 400 Bad Request\r\nDate: Thu, 07 Jun 2018 08:20:48 GMT\r\nContent-Type: text/html\r\nContent-Length: 242\r\nConnection: close\r\n\r\n<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\r\n<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body bgcolor="white">\r\n<h1>400 Bad Request</h1>\r\n<p>Your browser sent a request that this server could not understand.</body>\r\n</html>\r\n' callback1 b'HTTP/1.1 400 Bad Request\r\nDate: Thu, 07 Jun 2018 08:20:48 GMT\r\nContent-Type: text/html\r\nContent-Length: 242\r\nConnection: close\r\n\r\n<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\r\n<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body bgcolor="white">\r\n<h1>400 Bad Request</h1>\r\n<p>Your browser sent a request that this server could not understand.</body>\r\n</html>\r\n'
提高并发的方式
1.多进程
2.多线程
3.利用异步非阻塞模块来实现单线程的下的并发
浙公网安备 33010602011771号