zoukankan      html  css  js  c++  java
  • 爬虫基础--IO多路复用单线程异步非阻塞

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

    性能相关 参考

    https://www.cnblogs.com/wupeiqi/p/6229292.html

      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
    
     13 # k=v
    
     14 # k=v
    
     15 # 
    
    
     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
    Host: %s
    
    %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             pass
    102             # 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                 return
    123             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                             break
    134                         else:
    135                             context.write(data)
    136                     except BlockingIOError as e:
    137                         break
    138                     except TimeoutError as e: #如果超时,,移除 发送的请求和接收的请求 取消请求
    139                         self.fds.remove(context)
    140                         self.connections.remove(context)
    141                         context.finish(e)
    142                         break
    143 
    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()
  • 相关阅读:
    桥牌笔记:双挤
    打造GTD style的办公环境 V1.0
    《Two Dozen Short Lessons in Haskell》学习(四)
    跑步机到了,看能坚持多久
    《Two Dozen Short Lessons in Haskell》学习(十) Private Definitions — the whereclause
    赶在世界末日前完成的2012年全年总结
    《Two Dozen Short Lessons in Haskell》学习(三)
    《Two Dozen Short Lessons in Haskell》学习(六)
    《Two Dozen Short Lessons in Haskell》学习(二)
    桥牌笔记:进手张
  • 原文地址:https://www.cnblogs.com/uge3/p/9136054.html
Copyright © 2011-2022 走看看