惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
博客园 - 【当耐特】
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 三生石上(FineUI控件)
B
Blog
有赞技术团队
有赞技术团队
J
Java Code Geeks
云风的 BLOG
云风的 BLOG
Recent Announcements
Recent Announcements
Jina AI
Jina AI
Vercel News
Vercel News
博客园 - 叶小钗
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
F
Fortinet All Blogs
The Cloudflare Blog
V
V2EX
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
腾讯CDC

博客园 - 昕

Egret飞行模拟-开发记录03-LoadingUI界面 Egret飞行模拟-开发记录02 Egret飞行模拟-开发记录01 python学习笔记之五 中岛美雪——疑似穿越人物 tensorflow学习001——MNIST 机器学习笔记之三-yolov3+win7+vs2017+gpu+opencv编译 机器学习笔记之二-win10+cuda9.1+CUDNN7+Anaconda3+VS2017+tensorflow1.5+opencv3.4 机器学习笔记之一 python网页爬虫开发之七-多线程爬虫示例01 python网页爬虫开发之六-Selenium使用 python网页爬虫开发之五-反爬 python网页爬虫开发之四-串行爬虫代码示例 python学习笔记之三-计算运行时间 python网页爬虫开发之三 python网页爬虫开发之二 U3D学习14-一阶段学习总结 U3D学习13-数据存储 U3D学习12-黑暗之光实例
python学习笔记之四-多进程&多线程&异步非阻塞
· 2018-10-25 · via 博客园 - 昕

ProcessPoolExecutor对multiprocessing进行了高级抽象,暴露出简单的统一接口。

异步非阻塞 爬虫

对于异步IO请求的本质则是【非阻塞Socket】+【IO多路复用】:

import select

import socket

import time

class AsyncTimeoutException(TimeoutError):

    """

    请求超时异常类

    """

    def __init__(self, msg):

        self.msg = msg

        super(AsyncTimeoutException, self).__init__(msg)

class HttpContext(object):

    """封装请求和相应的基本数据"""

    def __init__(self, sock, host, port, method, url, data, callback, timeout=5):

        """

        sock: 请求的客户端socket对象

        host: 请求的主机名

        port: 请求的端口

        port: 请求的端口

        method: 请求方式

        url: 请求的URL

        data: 请求时请求体中的数据

        callback: 请求完成后的回调函数

        timeout: 请求的超时时间

        """

        self.sock = sock

        self.callback = callback

        self.host = host

        self.port = port

        self.method = method

        self.url = url

        self.data = data

        self.timeout = timeout

        self.__start_time = time.time()

        self.__buffer = []

    def is_timeout(self):

        """当前请求是否已经超时"""

        current_time = time.time()

        if (self.__start_time + self.timeout) < current_time:

            return True

    def fileno(self):

        """请求sockect对象的文件描述符,用于select监听"""

        return self.sock.fileno()

    def write(self, data):

        """在buffer中写入响应内容"""

        self.__buffer.append(data)

    def finish(self, exc=None):

        """在buffer中写入响应内容完成,执行请求的回调函数"""

        if not exc:

            response = b''.join(self.__buffer)

            self.callback(self, response, exc)

        else:

            self.callback(self, None, exc)

    def send_request_data(self):

        content = """%s %s HTTP/1.0\r\nHost: %s\r\n\r\n%s""" % (

            self.method.upper(), self.url, self.host, self.data,)

        return content.encode(encoding='utf8')

class AsyncRequest(object):

    def __init__(self):

        self.fds = []

        self.connections = []

    def add_request(self, host, port, method, url, data, callback, timeout):

        """创建一个要请求"""

        client = socket.socket()

        client.setblocking(False)

        try:

            client.connect((host, port))

        except BlockingIOError as e:

            pass

            # print('已经向远程发送连接的请求')

        req = HttpContext(client, host, port, method, url, data, callback, timeout)

        self.connections.append(req)

        self.fds.append(req)

    def check_conn_timeout(self):

        """检查所有的请求,是否有已经连接超时,如果有则终止"""

        timeout_list = []

        for context in self.connections:

            if context.is_timeout():

                timeout_list.append(context)

        for context in timeout_list:

            context.finish(AsyncTimeoutException('请求超时'))

            self.fds.remove(context)

            self.connections.remove(context)

    def running(self):

        """事件循环,用于检测请求的socket是否已经就绪,从而执行相关操作"""

        while True:

            r, w, e = select.select(self.fds, self.connections, self.fds, 0.05)

            if not self.fds:

                return

            for context in r:

                sock = context.sock

                while True:

                    try:

                        data = sock.recv(8096)

                        if not data:

                            self.fds.remove(context)

                            context.finish()

                            break

                        else:

                            context.write(data)

                    except BlockingIOError as e:

                        break

                    except TimeoutError as e:

                        self.fds.remove(context)

                        self.connections.remove(context)

                        context.finish(e)

                        break

            for context in w:

                # 已经连接成功远程服务器,开始向远程发送请求数据

                if context in self.fds:

                    data = context.send_request_data()

                    context.sock.sendall(data)

                    self.connections.remove(context)

            self.check_conn_timeout()

if __name__ == '__main__':

    def callback_func(context, response, ex):

        """

        :param context: HttpContext对象,内部封装了请求相关信息

        :param response: 请求响应内容

        :param ex: 是否出现异常(如果有异常则值为异常对象;否则值为None)

        :return:

        """

        print(context, response, ex)

    obj = AsyncRequest()

    url_list = [

        {'host': 'www.google.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,

         'callback': callback_func},

        {'host': 'www.baidu.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,

         'callback': callback_func},

        {'host': 'www.bing.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,

         'callback': callback_func},

    ]

    for item in url_list:

        print(item)

        obj.add_request(**item)

    obj.running()