# 1. epoll 不代表一定比select好
# 在并发高的情况下, 连接活跃度不高, epoll比select好
# 并发性不高, 同时连接很活跃, select比epoll好
# 通过非阻塞io发起http请求
# DefaultSelector会自动选择平台是使用poll还是epoll win就是poll unix就是epoll
# select + 回调 + 事件轮询
import socket
from urllib.parse import urlparse
# EVENT_READ 读事件 EVENT_WRITE写事件
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
selector = DefaultSelector()
# 使用select完成http请求
# 声明一个全局变量用来结束事件轮询
stop = False
class Fetcher:
def __init__(self, urls):
self.urls = urls
self.data = b''
def connected(self, key):
# 将监听取消注册
selector.unregister(key.fd)
# 证明已经连接好了
self.client.send('GET {} HTTP/1.1\r\nHOST:{}\r\nConnection:close\r\n\r\n'.format(self.path, self.host).encode('utf8'))
# 监听读的事件
selector.register(self.client.fileno(), EVENT_READ, self.read_func)
def read_func(self, key):
# 如果没有连接正常 recv同样会报错BlockingIOError
d = self.client.recv(1024)
if d:
self.data += d
else:
selector.unregister(key.fd)
self.data = self.data.decode('utf8')
print(self.data)
self.client.close()
print(self.urls)
self.urls.remove(self.spider_url)
print(self.urls)
if not self.urls:
global stop
stop = True
def get_url(self, url):
self.spider_url = url
url = urlparse(url)
self.host = url.netloc
self.path = url.path
if self.path == '':
self.path = '/'
# 建立socket连接
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client.setblocking(False) # 非阻塞 不用等待返回继续执行
try:
# connect 需要判断是否连接完成负责会发生BlockingIOError异常
self.client.connect((self.host, 80))
except BlockingIOError:
pass
# 使用select注册 self.client.fileno()获取client的文件描述符
# register(self, fileobj, events, data=None):
# fileobj 文件对象 events 事件 data 回调函数
selector.register(self.client.fileno(), EVENT_WRITE, self.connected)
def loop():
# 事件循环,不停的请求socket的状态并调用回调函数
# 回调 + 事件循环 + select(poll、epoll) 模式
# 1. select本身是不支持register模式
# 2. select状态变化以后的回调是由程序员来完成的
# 3. 不停的监听是否有
while 1:
if not stop:
ready = selector.select()
for key, mask in ready:
call_back = key.data # 找到当时注册的函数是什么
call_back(key) # 拿到方法只会调用传递参数
else:
break
if __name__ == '__main__':
import time
time1 = time.time()
url_list = ['https://www.baidu.com'] * 20
for url in url_list:
fectch = Fetcher(url_list)
fectch.get_url(url)
loop()
time2 = time.time()
print(time2 - time1)
QQ截图20190531163407.png
QQ截图20190531163659.png
网友评论