HTTPリクエストをシミュレート選択+ +コールバックイベントのポーリングを使用します

# 1. epoll 不代表一定比select好
# 在并发高的情况下, 连接活跃度不高, epoll比select好
# 并发性不高, 同时连接很活跃, select比epoll好

# 通过非阻塞io发起http请求

# DefaultSelector会自动选择平台是使用poll还是epoll  win就是poll unix就是epoll

# select + 回调 + 事件轮询

import socket
from urllib.parse import urlparse
# EVENT_READ 读事件  EVENT_WRITE写事件
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE

selector = DefaultSelector()
# 使用select完成http请求

# 声明一个全局变量用来结束事件轮询
stop = False


class Fetcher:

    def __init__(self, urls):
        self.urls = urls
        self.data = b''

    def connected(self, key):
        # 将监听取消注册
        selector.unregister(key.fd)
        # 证明已经连接好了
        self.client.send('GET {} HTTP/1.1\r\nHOST:{}\r\nConnection:close\r\n\r\n'.format(self.path, self.host).encode('utf8'))
        # 监听读的事件
        selector.register(self.client.fileno(), EVENT_READ, self.read_func)

    def read_func(self, key):



        # 如果没有连接正常 recv同样会报错BlockingIOError
        d = self.client.recv(1024)
        if d:
            self.data += d
        else:
            selector.unregister(key.fd)
            self.data = self.data.decode('utf8')
            print(self.data)
            self.client.close()
            print(self.urls)
            self.urls.remove(self.spider_url)
            print(self.urls)
            if not self.urls:
                global stop
                stop = True

    def get_url(self, url):
        self.spider_url = url
        url = urlparse(url)
        self.host = url.netloc
        self.path = url.path
        if self.path == '':
            self.path = '/'

        # 建立socket连接
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client.setblocking(False)  # 非阻塞 不用等待返回继续执行
        try:
            # connect 需要判断是否连接完成负责会发生BlockingIOError异常
            self.client.connect((self.host, 80))
        except BlockingIOError:
            pass
        # 使用select注册  self.client.fileno()获取client的文件描述符
        # register(self, fileobj, events, data=None):
        # fileobj 文件对象 events 事件  data 回调函数

        selector.register(self.client.fileno(), EVENT_WRITE, self.connected)


def loop():
    # 事件循环,不停的请求socket的状态并调用回调函数
    # 回调 + 事件循环 + select(poll、epoll) 模式
    # 1. select本身是不支持register模式
    # 2. select状态变化以后的回调是由程序员来完成的
    # 3. 不停的监听是否有
    while 1:
        if not stop:
            ready = selector.select()
            for key, mask in ready:
                call_back = key.data  # 找到当时注册的函数是什么
                call_back(key)  # 拿到方法只会调用传递参数
        else:
            break


if __name__ == '__main__':
    import time
    time1 = time.time()
    url_list = ['https://www.baidu.com'] * 20
    for url in url_list:
        fectch = Fetcher(url_list)
        fectch.get_url(url)
    loop()
    time2 = time.time()
    print(time2 - time1)
11560533-f0b326b5bed235a5.png
QQのスクリーンショット20190531163407.png

11560533-f5e780e6351596fb.png
QQのスクリーンショット20190531163659.png

おすすめ

転載: blog.csdn.net/weixin_33816946/article/details/91000563