【问题标题】:Python: simple async download of url content?Python:简单的异步下载 url 内容?
【发布时间】:2009-03-20 22:40:42
【问题描述】:

我有一个响应各种用户请求的 web.py 服务器。其中一个请求涉及下载和分析一系列网页。

是否有一种简单的方法可以在 web.py 中设置基于异步/回调的 url 下载机制?低资源使用率尤为重要,因为每个用户发起的请求都可能导致下载多个页面。

流程如下所示:

用户请求 -> web.py -> 并行或异步下载 10 个页面 -> 分析内容,返回结果

我认识到 Twisted 会是一个很好的方法,但我已经在 web.py 中,所以我对 web.py 中可以容纳的东西特别感兴趣。

【问题讨论】:

    标签: python asynchronous


    【解决方案1】:

    这是一段有趣的代码。我自己没用过,但是看起来不错;)

    https://github.com/facebook/tornado/blob/master/tornado/httpclient.py

    低级 AsyncHTTPClient:

    "一个由 py​​curl 支持的非阻塞 HTTP 客户端。示例用法:"

    import ioloop
    
    def handle_request(response):
        if response.error:
            print "Error:", response.error
        else:
            print response.body
        ioloop.IOLoop.instance().stop()
    
    http_client = httpclient.AsyncHTTPClient()
    http_client.fetch("http://www.google.com/", handle_request)
    ioloop.IOLoop.instance().start()
    

    " fetch() 可以接受字符串 URL 或 HTTPRequest 实例,提供更多选项,例如执行 POST/PUT/DELETE 请求。

    AsyncHTTPClient 构造函数的关键字参数 max_clients 确定可以在每个 IOLoop 上并行执行的同时 fetch() 操作的最大数量。 "

    还有新的实施正在进行中: https://github.com/facebook/tornado/blob/master/tornado/simple_httpclient.py “没有外部依赖的非阻塞 HTTP 客户端。......这个类仍在开发中,不推荐用于生产。”

    【讨论】:

      【解决方案2】:

      一种选择是将工作发布到某种队列中(您可以使用像 ActiveMQpyactivemqSTOMP 这样的企业作为连接器,或者您可以使用像 Kestrel 这样的轻量级的东西用 Scala 编写并使用与 memcache 相同的协议,因此您可以使用 python memcache 客户端与之对话。

      一旦您设置了队列机制,您就可以创建订阅队列的尽可能多或尽可能少的工作任务,并根据需要执行实际的下载工作。您甚至可以将它们安装在其他机器上,这样它们就不会影响您网站的服务速度。当工作人员完成后,他们会将结果发布回数据库或网络服务器可以提取的另一个队列。

      如果您不想管理外部工作进程,那么您可以在运行网络服务器的同一个 python 进程中创建工作线程,但显然它有更大的潜力影响您的网页服务性能。

      【讨论】:

        【解决方案3】:

        您或许可以使用urllib 下载文件并使用Queue 模块来管理多个工作线程。例如:

        import urllib
        from threading import Thread
        from Queue import Queue
        
        NUM_WORKERS = 20
        
        class Dnld:
            def __init__(self):
                self.Q = Queue()
                for i in xrange(NUM_WORKERS):
                    t = Thread(target=self.worker)
                    t.setDaemon(True)
                    t.start()
        
            def worker(self):
                while 1:
                    url, Q = self.Q.get()
                    try:
                        f = urllib.urlopen(url)
                        Q.put(('ok', url, f.read()))
                        f.close()
                    except Exception, e:
                        Q.put(('error', url, e))
                        try: f.close() # clean up
                        except: pass
        
            def download_urls(self, L):
                Q = Queue() # Create a second queue so the worker 
                            # threads can send the data back again
                for url in L:
                    # Add the URLs in `L` to be downloaded asynchronously
                    self.Q.put((url, Q))
        
                rtn = []
                for i in xrange(len(L)):
                    # Get the data as it arrives, raising 
                    # any exceptions if they occur
                    status, url, data = Q.get()
                    if status == 'ok':
                        rtn.append((url, data))
                    else:
                        raise data
                return rtn
        
        inst = Dnld()
        for url, data in inst.download_urls(['http://www.google.com']*2):
            print url, data
        

        【讨论】:

        • 有点跑题了(用线程而不是 aio 来回避问题),但这可能是要走的路;)您可以使用 await asyncio.get_event_loop().run_in_executor(executor, your_blocking_callable, *args) 和线程/队列接口 asyncio 和将同步可调用对象传递给它。
        【解决方案4】:

        我只是在 twisted 中构建一个服务,该服务执行并发获取和分析,并从 web.py 作为一个简单的 http 请求进行访问。

        【讨论】:

          【解决方案5】:

          如今,您可能想要使用一些出色的 Python 库 - urllib3(使用线程池)和 requests(通过 urllib3 使用线程池或通过 gevent 使用非阻塞 IO)

          【讨论】:

            【解决方案6】:

            【讨论】:

            • 我对 asynchttpclient 代码进行了一些错误修复。我试着给作者发邮件,但他似乎不在。如果你想要这些修复,你可以给我发电子邮件。我还启用了 HTTP 请求管道,这应该会进一步提高许多小型请求的速度。
            • 您可以在此处找到 asynchttp 客户端的错误修复和扩展:code.google.com/p/asynhttp
            【解决方案7】:

            我不确定我是否理解你的问题,所以我将给出多个部分答案。

            • 如果您担心 web.py 必须从某处下载数据并在响应之前分析结果,并且您担心在结果准备好之前请求可能会超时,您可以使用 ajax 来拆分工作。立即返回一个容器页面(保存结果)和一些 javascript 来轮询服务器以获取结果,直到客户端拥有所有结果。因此客户端永远不会等待服务器,尽管用户仍然需要等待结果。
            • 如果您担心的是占用服务器等待客户端获取结果,我怀疑这是否真的会成为问题。您的网络层不应要求您等待写入
            • 如果您担心服务器在客户端从其他地方下载静态内容时等待,那么 ajax 或巧妙地使用重定向应该可以解决您的问题

            【讨论】:

            • ajax 解决方案的问题是跨域限制 - 我无法从不是来自原始服务器的页面中获取内容。顺便说一句,在这种情况下,我并不担心等待写入,但这实际上是网络层没有解决的问题。
            • @Parand -- 不,但是您可以在您的域中设置一个廉价的直通代理,然后让他们通过它。
            【解决方案8】:

            我不知道这是否真的有效,但它看起来可能:EvServer: Python Asynchronous WSGI Server 有一个 web.py interface 并且可以将彗星样式推送到浏览器客户端。

            如果这不正确,也许您可​​以使用Concurrence HTTP client 异步下载页面,并弄清楚如何通过 ajax 或 Comet 将它们提供给浏览器。

            【讨论】:

              【解决方案9】:

              按照 MarkusQ 的回答,MochiKit 是一个不错的 JavaScript 库,具有受 Twisted 启发的强大异步方法。

              【讨论】:

                【解决方案10】:

                实际上你可以将twisted 与web.py 集成。我不太确定我是怎么做到的,因为我只用 django 完成了它(与它一起使用扭曲)。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-02-12
                  • 2019-02-25
                  • 2017-11-18
                  • 1970-01-01
                  • 2016-08-23
                  • 2016-05-12
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多