最近一直的学习爬虫 ,进行基础的学习
性能相关 参考
1 # 目标:单线程实现并发HTTP请求 2 # 3 # socket 4 # IO多路复用 5 # HTTP协议 6 # 7 # 流程 8 # http://www.163.com/new/ 9 # 1. sk连接 IP 禾端口进行连接 10 # 2.请求信息 11 # 请求头 12 # k=v\r\n 13 # k=v\r\n 14 # k=v\r\n 15 # \r\n\r\n 16 # 请求体 17 18 import select 19 import socket 20 import time 21 22 23 class AsyncTimeoutException(TimeoutError): 24 """ 25 请求超时异常类 26 """ 27 28 def __init__(self, msg): 29 self.msg = msg 30 super(AsyncTimeoutException, self).__init__(msg) 31 32 33 class HttpContext(object): 34 """封装请求和相应的基本数据""" 35 36 def __init__(self, sock, host, port, method, url, data, callback, timeout=5): 37 """ 38 sock: 请求的客户端socket对象 39 host: 请求的主机名 40 port: 请求的端口 41 method: 请求方式 42 url: 请求的URL 43 data: 请求时请求体中的数据 44 callback: 请求完成后的回调函数 45 timeout: 请求的超时时间 46 """ 47 self.sock = sock #sock: 请求的客户端socket对象 48 self.callback = callback #callback: 请求完成后的回调函数 49 self.host = host #host: 请求的主机名 50 self.port = port # port: 请求的端口 51 self.method = method #method: 请求方式 52 self.url = url #url: 请求的URL 53 self.data = data #data: 请求时请求体中的数据 54 55 self.timeout = timeout #timeout: 请求的超时时间 56 57 self.__start_time = time.time() #当前时间 58 self.__buffer = [] #在buffer中写入响应内容 59 60 def is_timeout(self): 61 """当前请求是否已经超时""" 62 current_time = time.time() 63 if (self.__start_time + self.timeout) < current_time: 64 return True 65 66 def fileno(self): 67 """请求sockect对象的文件描述符,用于select监听""" 68 return self.sock.fileno() 69 70 def write(self, data): 71 """在buffer中写入响应内容""" 72 self.__buffer.append(data) 73 74 def finish(self, exc=None): 75 """在buffer中写入响应内容完成,执行请求的回调函数""" 76 if not exc: 77 response = b''.join(self.__buffer) 78 self.callback(self, response, exc) 79 else: 80 self.callback(self, None, exc) 81 82 def send_request_data(self): #发送请求 伪造请求头 请求体 83 content = """%s %s HTTP/1.0\r\nHost: %s\r\n\r\n%s""" % ( 84 # 请求方式 请求的URL 请求的主机名 请求时请求体中的数据 85 self.method.upper(), self.url, self.host, self.data,) 86 87 return content.encode(encoding='utf8') 88 89 class AsyncRequest(object): 90 def __init__(self): 91 self.fds = [] #用于存放 连接有返回值的请求 92 self.connections = []#用于存放需要连接的请求 93 94 def add_request(self, host, port, method, url, data, callback, timeout): 95 """创建一个要请求""" 96 client = socket.socket() 97 client.setblocking(False) 98 try: 99 client.connect((host, port))100 except BlockingIOError as e:101 pass102 # print('已经向远程发送连接的请求')103 req = HttpContext(client, host, port, method, url, data, callback, timeout)104 self.connections.append(req)105 self.fds.append(req)106 107 def check_conn_timeout(self):108 """检查所有的请求,是否有已经连接超时,如果有则终止"""109 timeout_list = [] #超时列表110 for context in self.connections:111 if context.is_timeout(): #进行超时检测 如果是超时112 timeout_list.append(context) #加入超时列表113 for context in timeout_list: #进行超时处理114 context.finish(AsyncTimeoutException('请求超时'))115 self.fds.remove(context) #进行移除 请求 待返回列表116 self.connections.remove(context) #进行移除 请求 待发送列表117 118 def running(self):119 """事件循环,用于检测请求的socket是否已经就绪,从而执行相关操作"""120 while True:121 if not self.fds: #如果没有请求 直接返回122 return123 r, w, e = select.select(self.fds, self.connections, self.fds, 0.05) #监测socket对象的变化124 125 for context in r:126 sock = context.sock #接收请求 连接127 while True:128 try:129 data = sock.recv(8096)# 取返回值130 if not data:#如果没有返回值131 self.fds.remove(context) #移除等待返回值 的请求132 context.finish()#完成请求133 break134 else:135 context.write(data)136 except BlockingIOError as e:137 break138 except TimeoutError as e: #如果超时,,移除 发送的请求和接收的请求 取消请求139 self.fds.remove(context)140 self.connections.remove(context)141 context.finish(e)142 break143 144 for context in w:145 # 已经连接成功远程服务器,开始向远程发送请求数据146 if context in self.fds:147 data = context.send_request_data()#请求头 请求体148 context.sock.sendall(data)#进行连接149 self.connections.remove(context) #移除已经连接成功的请求150 151 self.check_conn_timeout() #检测 是否超时152 153 154 if __name__ == '__main__':155 def callback_func(context, response, ex):156 """157 :param context: HttpContext对象,内部封装了请求相关信息158 :param response: 请求响应内容159 :param ex: 是否出现异常(如果有异常则值为异常对象;否则值为None)160 :return:161 """162 print(context, response, ex)163 164 obj = AsyncRequest()165 url_list = [166 { 'host': 'www.google.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,167 'callback': callback_func},168 { 'host': 'www.baidu.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,169 'callback': callback_func},170 { 'host': 'www.bing.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,171 'callback': callback_func},172 ]173 for item in url_list:174 print(item)175 obj.add_request(**item)176 177 obj.running()