博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
爬虫基础--IO多路复用单线程异步非阻塞
阅读量:4587 次
发布时间:2019-06-09

本文共 5992 字,大约阅读时间需要 19 分钟。

最近一直的学习爬虫  ,进行基础的学习

性能相关 参考

 

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()

 

转载于:https://www.cnblogs.com/uge3/p/9136054.html

你可能感兴趣的文章
[机器学习]-Adaboost提升算法从原理到实践
查看>>
AOP概念
查看>>
memset函数详细用法说明【转】
查看>>
php解析xml字符串
查看>>
SFTP客户端与服务端
查看>>
Modbus协议
查看>>
复位自动ID的问题有兩種方法
查看>>
CentOS 5.5 Samba服务器安装总结
查看>>
博客园评价
查看>>
Apache commons-io实现多文件读取和写入
查看>>
Bash快捷键整理
查看>>
不写代码也能爬虫Web Scraper
查看>>
转: C#+MAPX 添加线、文本、符号等图元
查看>>
管理机--Jumpserver由docker搭建
查看>>
bzoj2212 Tree Rotations 线段树合并+动态开点
查看>>
SAP CRM 通过调试观察CL_CRM_BOL_ENTITY中的数据
查看>>
2016NOI冬令营day5
查看>>
JavaScript正则表达式-字符
查看>>
php 不等待返回的实现方法(异步调用)
查看>>
Oracle成长点点滴滴(2)— 权限管理
查看>>