• Python Socket请求网站获取数据


     Python Socket请求网站获取数据

    ---阻塞 I/O           ->收快递,快递如果不到,就干不了其他的活

    ---非阻塞I/0       ->收快递,不断的去问,有没有送到,有没有送到,...如果送到了就接收

    ---I/O多路复用      ->找个代理人(select), 去收快递。快递到了,就通知用户.  

    一 . 阻塞方式 

    blocking IO 会一直block 对应的进程,直到操作完成

    # 客户端请求网站-阻塞实现(一次一次的请求)
    import socket
    import time
    
    # 访问网站
    ACCESS_URL = 'www.baidu.com'
    # 端口
    ACCESS_PORT = 80
    
    
    # socket阻塞请求网站
    def blocking(pn):
        sock = socket.socket()
        sock.connect((ACCESS_URL, ACCESS_PORT))  # 连接网站 ,发出一个HTTP请求
        request_url = 'GET {} HTTP/1.0
    Host: www.baidu.com
    
    '.format('/s?wd={}'.format(pn))
        sock.send(request_url.encode())
        response = b''
        chunk = sock.recv(1024)
        while chunk:  # 循环接收数据,因为一次接收不完整
            response += chunk
            chunk = sock.recv(1024)
        # print(response.decode())
        return response
    
    
    def block_way():
        for i in range(5):
            blocking(i)
    
    
    if __name__ == '__main__':
        start = time.time()
        block_way()
        print('请求5次页面耗时{}'.format(time.time() - start))
        """
        请求5次页面耗时2.4048924446105957
        """

    二. 非阻塞方式

    non-blcoking在kernel还没准备好数据的情况下,会立即返回(会抛出异常)

    # 客户端请求网站-非阻塞实现
    import socket
    import time
    
    # 访问网站
    ACCESS_URL = 'www.baidu.com'
    # 端口
    ACCESS_PORT = 80
    
    
    # socket非阻塞请求网站(时间消耗在不断的while循环中,和阻塞的时间差不多)
    def blocking(pn):
        sock = socket.socket()
        sock.setblocking(False)  # 设置为非阻塞
        try:
            # connect连接需要一定时间,非阻塞发送完请求,立即返回,如果没有数据会报异常
            sock.connect((ACCESS_URL, ACCESS_PORT))  # 连接网站 ,发出一个HTTP请求
        except BlockingIOError:  # 非阻塞套接字捕获异常
            pass
        request_url = 'GET {} HTTP/1.0
    Host: www.baidu.com
    
    '.format('/s?wd={}'.format(pn))
        while True:  # 不断发送http请求
            try:
                sock.send(request_url.encode())
                break
            except OSError:
                pass
        response = b''
        while True:  # 不断地接收数据
            try:
                chunk = sock.recv(1024)  # 没有数据返回时会收到异常
                while chunk:  # 循环接收数据,因为一次接收不完整
                    response += chunk
                    chunk = sock.recv(1024)
                break
            except BlockingIOError:  # 处理非阻塞异常
                pass
        # print(response.decode())
        return response
    
    
    def block_way():
        for i in range(5):
            blocking(i)
    
    
    if __name__ == '__main__':
        start = time.time()
        block_way()
        print('请求5次页面耗时{}'.format(time.time() - start))
        """
        请求5次页面耗时2.681565046310425
        时间消耗在不断的while循环中,和阻塞的时间差不多
        """

    三. 多线程方式

    # 客户端请求网站-线程池实现
    import socket
    from multiprocessing.pool import ThreadPool
    import time
    
    # 访问网站
    ACCESS_URL = 'www.baidu.com'
    # 端口
    ACCESS_PORT = 80
    
    
    def blocking(pn):
        sock = socket.socket()
        sock.connect((ACCESS_URL, ACCESS_PORT))  # 连接网站 ,发出一个HTTP请求
        request_url = 'GET {} HTTP/1.0
    Host: www.baidu.com
    
    '.format('/s?wd={}'.format(pn))
        sock.send(request_url.encode())
        response = b''
        chunk = sock.recv(1024)
        while chunk:  # 循环接收数据,因为一次接收不完整
            response += chunk
            chunk = sock.recv(1024)
        # print(response.decode())
        return response
    
    
    def block_way():
        pool = ThreadPool(5)
        for i in range(10):
            pool.apply_async(blocking, args=(i,))
        pool.close()  # close()执行后不会有新的进程加入到pool
        pool.join()  # join函数等待子进程结束
    
    
    
    if __name__ == '__main__':
        start = time.time()
        block_way()
        print('请求10次页面耗时{}'.format(time.time() - start))
        """
        请求10次页面耗时1.1231656074523926 
        """

    四. 多进程方式

    # 客户端请求网站-进程池实现
    import socket
    from multiprocessing import Pool
    import time
    
    # 访问网站
    ACCESS_URL = 'www.baidu.com'
    # 端口
    ACCESS_PORT = 80
    
    
    def blocking(pn):
        """
        发送请求,接收数据
        :param pn: 
        :return: 
        """
        sock = socket.socket()
        sock.connect((ACCESS_URL, ACCESS_PORT))  # 连接网站 ,发出一个HTTP请求
        request_url = 'GET {} HTTP/1.0
    Host: www.baidu.com
    
    '.format('/s?wd={}'.format(pn))
        sock.send(request_url.encode())
        response = b''
        chunk = sock.recv(1024)
        while chunk:  # 循环接收数据,因为一次接收不完整
            response += chunk
            chunk = sock.recv(1024)
        # print(response.decode())
        return response
    
    
    def block_way():
        pool = Pool(5)
        for i in range(10):
            pool.apply_async(blocking, args=(i,))
        pool.close()  # close()执行后不会有新的进程加入到pool
        pool.join()  # join函数等待子进程结束
    
    
    
    if __name__ == '__main__':
        start = time.time()
        block_way()
        print('请求10次页面耗时{}'.format(time.time() - start))
        """
        请求10次页面耗时1.1685676574707031 略慢于线程池实现方式,因为进程相对于线程开销比较大
        """

    五. 协程方式

    # 客户端请求网站-协程实现
    from gevent import monkey;monkey.patch_all()  # 加补丁,实现非阻塞
    import socket
    import gevent
    import time
    
    # 访问网站
    ACCESS_URL = 'www.baidu.com'
    # 端口
    ACCESS_PORT = 80
    
    
    def blocking(pn):
        """
        发送请求,接收数据
        :param pn: 
        :return: 
        """
        sock = socket.socket()
        sock.connect((ACCESS_URL, ACCESS_PORT))  # 连接网站 ,发出一个HTTP请求
        request_url = 'GET {} HTTP/1.0
    Host: www.baidu.com
    
    '.format('/s?wd={}'.format(pn))
        sock.send(request_url.encode())
        response = b''
        chunk = sock.recv(1024)
        while chunk:  # 循环接收数据,因为一次接收不完整
            response += chunk
            chunk = sock.recv(1024)
        # print(response.decode())
        return response
    
    
    def block_way():
        tasks = [gevent.spawn(blocking,i) for i in range (10)] #启动协程
        gevent.joinall(tasks)  # 阻塞等待所有操作都执行完毕
    
    
    if __name__ == '__main__':
        start = time.time()
        block_way()
        print('请求10次页面耗时{}'.format(time.time() - start))
        """
        请求10次页面耗时0.6231002807617188 效率高于线程方式,协程相当于单线程并发(微线程),开销小于线程
        """

    六. IO多路复用

    I/O多路复用就是通过一种机制,操作系统通过一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

    # 客户端请求网站-I/O多路复用
    import socket
    import selectors
    import time
    
    # 访问网站
    ACCESS_URL = 'www.baidu.com'
    # 端口
    ACCESS_PORT = 80
    
    sel = selectors.DefaultSelector()
    flag = False  # 结束标志
    num_list = [0] * 5
    
    
    class Crawler(object):
        def __init__(self,pn):
            self.pn = pn
            self.response = b''
    
    
        def req_connect(self):
            """
            请求建立连接
            :return:
            """
            sock = socket.socket()
            sock.setblocking(False)
            try:
                sock.connect((ACCESS_URL, ACCESS_PORT))  # 连接网站 ,发出一个HTTP请求
            except BlockingIOError:  # 非阻塞套接字捕获异常
                pass
            sel.register(sock,selectors.EVENT_WRITE, self.conn_send)  # 监听socket,向服务端发送数据WRITE
    
    
        def conn_send(self,sock):
            """
            发送请求数据
            :param sock:
            :return:
            """
            # 取消上面注册监听的套接字
            sel.unregister(sock)
            request_url = 'GET {} HTTP/1.0
    Host: www.baidu.com
    
    '.format('/s?wd={}'.format(self.pn))
            sock.send(request_url.encode())  # 当我们发送数据之后,就等接收数据
            sel.register(sock,selectors.EVENT_READ, self.read)
    
        def read(self, sock):
            global flag
            chunk = sock.recv(1024)
            if chunk:
                self.response += chunk
            else:
                # 没有数据可读
                print(self.response)
                sel.unregister(sock)
                num_list.pop()
                if not num_list:
                    flag = True
    
    
    # 事件循环
    def loop():
        while not flag:
            events = sel.select() # 监听发生了变化的套接字
            for key, mask in events:
                callback = key.data
                callback(key.fileobj)
    
    
    if __name__ == '__main__':
        start = time.time()
        # print(num_list)
        for i in range(5):
            crawler = Crawler(i)  # 实例
            crawler.req_connect()
        loop()
        print('请求5次页面耗时{}'.format(time.time() - start))
        """
        请求5次页面耗时0.5865745544433594 多路复用非阻塞效率要高于非阻塞方式
        """
  • 相关阅读:
    批量启动application pool
    sql server文件另存为的时候,选择文件编码和换行
    insert into 和 where not exists
    tcp slowstart (TCP 慢启动)
    如何在CentOS7上改变网络接口名
    Window系统命令行调用控制面板程序
    Using Let’s Encrypt for free SSL Certs with Netscaler
    端口相关知识学习笔记
    win7下KiWi Syslog服务器的安装与配置
    MPS添加管理设备实例NS的过程
  • 原文地址:https://www.cnblogs.com/xiao-apple36/p/8673356.html
Copyright © 2020-2023  润新知