zoukankan      html  css  js  c++  java
  • 异步非阻塞爬虫框架的设计思路

     1.socket 实现http 请求

    1.阻塞情况

    #  1 阻塞
    client = socket.socket()
    
    client.connect(('14.215.177.39',80))  # 阻塞  ,  '14.215.177.39'  为百度ip  默认端口
    
    data=b'GET / HTTP/1.0
    host:www.baidu.com
    
    '   # 所有web框架都是通过 
    
     进行分割
    client.sendall(data)
    
    response=client.recv(8096) # # 阻塞
    print(response)
    client.close()

    2. 非阻塞情况

    import time
    client = socket.socket()
    client.setblocking(False)  # 设置非阻塞
    # 连接已经发送出去了
    try:
    
        client.connect(('14.215.177.39',80))  # 非阻塞  ,  '14.215.177.39'  为百度ip  默认端口
    except BlockingIOError as e:
        print(e)
    
    time.sleep(5)  # 在等的过程可能连接成功
    # 等待阶段  可以做其他事情
    while True:
        # 计算或数据库取数据,取完 再去检查是否连接成功,,如果成功就退出循环继续往下走
        pass
    
        # if  某个条件成立时就退出循环  比如所有的请求都返回时,就退出循环
        break
    
    data=b'GET / HTTP/1.0
    host:www.baidu.com
    
    '   # 所有web框架都是通过 
    
    进行分割
    client.sendall(data)
    response=client.recv(8096) # # 非阻塞
    print(response)
    client.close()
    
    # 总结:
        # 1. 非阻塞   会报错    用try
        # 2.  没连接成功或数据还没收到前,让线程去做其他的事 : 计算或去数据库取数据  可以用while
            # 定义一些操作

    2.select 事件监听

    IO多路复用,检测多个socket对象是否有变化?
    
    r,w,e =select.select([sk1,sk2,sk3,...100],[],[],0.05)
    
    # r ,是什么?  谁发生变化 可读,如果socket中返回内容了,表示可读,表示要收数据了
    
    # w ,是什么?[sk2,sk3] ,连接成功了
    for obj in w:
        obj.send('GET /HTTP/1.0 ')
    
    # r,是什么?  [sk2,sk3],要收数据了
    
    for obj in r:
        response=obj.recv(..)
        print(response)

    3.结合socket的非阻塞和事件监听IO 多路复用 完成异步非阻塞模块的基本框架设计

    思路步骤:

    1. 思路先从 用户端入手

      url_list=[

    {'host':'www.baidu.com','port':80,'path':'/',"callback":do1}, 
    {'host':'www.bing.com','port':80,'path':'/index.html',"callback":do3},]

    ]   

    2. 循环 url_list

    #   实例化    对象

    xiake = XiaKe()

    for url in list:

      # 思路  把url 做为参数 传给一个对象或方法   ,逻辑通过该对象或方法实现

      # 这里用一个类封装好 ,处理逻辑      实例化该类, 在调用该类中的某个方法   ,把功能运行起来

      # add_request(url)   #  此处开始的思路是放一个函数, 但思路可以转变到,类中可以包含函数 

      xieke.add_request(url)

      #   再通过类下某个方法启动:

    xieke.run()

    3.  该类的大致雏形:

    import select
    import socket
    
    
    class XiaKe():
    
        def __init__(self):
            self.sock_list=[] # 可以接收消息的对象
            self.conns=[]  # 已经连接 可发送消息的对象
    
        def add_request(self,req_info):
            '''
            创建请求
            :param req: {'host':'www.bing.com','port':80,'path':'/index.html'},]
            :return:
    
            '''
            sock=socket.socket()
            sock.setblocking(False) # 设置为非阻塞
            try:
                sock.connect((req_info['host'],req_info['post']))
    
            except BlockingIOError as e:
                pass
    
            self.sock_list.append(sock)
            self.conns.append(sock)
    
        def run(self):
    
            '''
            进行事件监测 监听是否连接成功
            :return:
            '''
            #  监听  
            while True:
                r,w,e = select.select(self.sock_list,self.conns,[],0.05)
                
                # 连接成功发消息
                for sock in w:
                    # sock.send('GET /index.html http/1.0
    host:
    
    ') # 这里
                    sock.send(b'GET /index.html http/1.0
    host:
    
    ')   
                    # 信息发送完  从监测列表移除
                    self.conns.remove(sock)
                    
                
                # 等待接收信息
                for sock in r :
                    response  =sock.recv(8096)  # 接收信息  或将接收后的信息  给外部的回调函数
                    
                    # func=req_info['callback']  # 外部传过来的 通过字典    {'callback':callback} 
                    # func(response)
                    
                    # 此处可以对数据进行处理  
                    self.sock_list.remove(sock)
                    
                # 所有请求已经返回时,退出循环
                
                if not self.sock_list:
                    break

    4. 对基本的类进行进一步的优化处理:

      需要注意的是:  select.select(rlist,wlist,[],0.05)  

      1.参数 : rlist,wlist 默认里面放的是sock 对象

      2. 但其实只要rlist 和wlist 中的对象,含有fileno 方法就可以。

      3. 所有改进思路: 重新 构建一个类,让该类中含有该fileno 方法,   

    class Request(object):
    
        def __init__(self,sock,re_info):    # 
            self.sock=sock
            self.info=re_info
    
        def fileno(self):
    
            return self.sock.fileno()

    最后的异步非阻塞框架的 大致框为:

    import socket
    import select
    class Request(object):
    
        def __init__(self,sock,re_info):
            self.sock=sock
            self.info=re_info
    
        def fileno(self):
    
            return self.sock.fileno()
    
    
    class XiaKe(object):
    
        def __init__(self):
    
            self.socket_list=[]
            self.conns=[]
    
        def add_request(self,req_info):
            '''
            创建请求
            req_info:{'host':'www.baidu.com','port':80,'path':'/'},
            :return:
    
            '''
            sock =socket.socket()
            sock.setblocking(False)
            try:
                sock.connect((req_info["host"],req_info['port']))
    
            except BlockingIOError as e:
                pass
            obj=Request(sock,req_info)   #  此处本来是加socket 对象, 但只要对象中含有fileno 方法就可以,加入到select监听对象中。
            self.socket_list.append(obj)
            self.conns.append(obj)
    
        def run(self):
            '''
            开始事件循环: 检查连接成功了吗,数据是否返回
            :return:
            '''
            while True:
                r,w,e =select.select(self.socket_list,self.conns,[],0.05)
                # r 可以接收消息
                # w 连接成功后的对象
    
                #循环连接成功的列表对象  w
                for obj in w:
                    # obj 为request 对象
                    # obj.sock.send('GET /index.html http/1.0
    host:
    
    ')
                    data='GET %s http/1.0
    host:%s
    
    '%(obj.info["path"],obj.info['host'])
                    obj.sock.send(data.encode('utf-8')) # 发消息
                    self.conns.remove(obj)  # 一次请求一次响应  移除已经发连接成功发了数据的对象
    
                # 接收消息
                for obj in r:
                    response=obj.sock.recv(8096)  # 此处数据可能比较大需要进行处理,或用循环
    
                    # print(obj.info["host"],response)
                    obj.info["callback"](response)
                    # 移除接收完消息的对象
                    self.socket_list.remove(obj)
                # 所有请求的已经返回
                if not self.socket_list:
                    break
    
    
    # 回调函数 用户处理返回结果的回调函数 def do1(response): print(response) def do2(response): print(response) def do3(response): print(response)
    url_list
    =[ {'host':'www.baidu.com','port':80,'path':'/',"callback":do1}, # callback 函数可以是一个[c1,c2,] {'host':'www.conblogs.com','port':80,'path':'/',"callback":do2}, {'host':'www.bing.com','port':80,'path':'/index.html',"callback":do3},] xiake=XiaKe() for item in url_list: xiake.add_request(item) xiake.run()
  • 相关阅读:
    透视校正插值
    投影矩阵推导
    编程思想-小即是美
    Win10使用小技巧
    TotalCommander 之 快捷键
    TotalCommander 之 配置
    TotalCommander 之 日常使用技巧
    上士闻道,勤而行之;中士闻道,若存若亡;下士闻道,大笑之。不笑不足以为道。
    《诫子书》
    青春不是年华,而是心境
  • 原文地址:https://www.cnblogs.com/knighterrant/p/10487836.html
Copyright © 2011-2022 走看看