【问题标题】:Download multiple pages concurrently?同时下载多个页面?
【发布时间】:2009-09-29 11:35:11
【问题描述】:

我想用 Python 编写一个脚本,它可以从数据库中获取 url,并同时下载网页以加快速度,而不是等待每个页面一个接一个地下载。

根据this thread,Python 不允许这样做,因为名为@9​​87654322@ 的东西会阻止多次启动同一个脚本。

在花时间学习 Twisted 框架之前,我想确保没有更简单的方法来完成我需要做的上述工作。

感谢您的任何提示。

【问题讨论】:

  • 非并发,但非常可靠,小心(而且是旧的!)实现伴随您的 Python 安装(或在线浏览 svn.python.org/view/python/trunk/Tools/webchecker)——确保在您的并发实现中应用所有它的礼貌和预防措施(robots.txt 解析等;-)。
  • @Alex,是否有一个集中的位置来提供与网络抓取相关的所有礼遇和预防措施?
  • 对于使用 Python3 和 urllib3 的简洁解决方案,请参阅另一个答案:stackoverflow.com/a/52172680/127465

标签: python concurrent-processing


【解决方案1】:

不用担心 GIL。在你的情况下没关系。

做你想做的最简单的方法是创建线程池,使用 threading 模块和来自ASPN 的线程池实现之一。该池中的每个线程都可以使用 httplib 下载您的网页。

另一种选择是使用PyCURL 模块——它原生支持并行下载,因此您不必自己实现。

【讨论】:

    【解决方案2】:

    GIL 会阻止您有效地使用线程进行处理器负载平衡。由于这不是处理器负载平衡,而是防止一个 IO 等待停止整个下载,因此 GIL 与此处无关。 *)

    因此,您需要做的就是创建多个同时下载的进程。您可以使用线程模块或多处理模块来做到这一点。

    *) 好吧...除非您有千兆连接,并且您的问题实际上是您的处理器在您的网络之前过载。但这显然不是这里的情况。

    【讨论】:

      【解决方案3】:

      我最近解决了同样的问题。需要考虑的一件事是,有些人不喜欢让他们的服务器陷入困境,并且会阻止这样做的 IP 地址。我听说的标准礼貌是页面请求之间大约 3 秒,但这是灵活的。

      如果您从多个网站下载,您可以按域对 URL 进行分组,并为每个网站创建一个线程。然后在您的线程中,您可以执行以下操作:

      for url in urls:
          timer = time.time()
          # ... get your content ...
          # perhaps put content in a queue to be written back to 
          # your database if it doesn't allow concurrent writes.
          while time.time() - timer < 3.0:
              time.sleep(0.5)
      

      有时只需 3 秒就能得到您的回复,您不必担心。

      当然,如果您只从一个站点下载,这对您毫无帮助,但它可以防止您被阻止。

      在管理它们的开销减慢进程之前,我的机器处理了大约 200 个线程。我最终以每秒 40-50 页的速度结束。

      【讨论】:

        【解决方案4】:

        urllib & threading(或multiprocessing)包拥有你需要做的所有“蜘蛛”。

        您需要做的是从数据库中获取 url,并为每个 url 启动一个线程或进程 抓取网址。

        仅作为示例(缺少数据库 url 检索):

        #!/usr/bin/env python
        import Queue
        import threading
        import urllib2
        import time
        
        hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com",
            "http://ibm.com", "http://apple.com"]
        
        queue = Queue.Queue()
        
        class ThreadUrl(threading.Thread):
            """Threaded Url Grab"""
            def __init__(self, queue):
                threading.Thread.__init__(self)
                self.queue = queue
        
            def run(self):
                while True:
                    #grabs host from queue
                    host = self.queue.get()
        
                    #grabs urls of hosts and prints first 1024 bytes of page
                    url = urllib2.urlopen(host)
                    print url.read(1024)
        
                    #signals to queue job is done
                    self.queue.task_done()
        
        start = time.time()
        def main():
        
            #spawn a pool of threads, and pass them queue instance
            for i in range(5):
                t = ThreadUrl(queue)
                t.setDaemon(True)
                t.start()
        
            #populate queue with data
            for host in hosts:
                queue.put(host)
        
            #wait on the queue until everything has been processed
            queue.join()
        
        main()
        print "Elapsed Time: %s" % (time.time() - start)
        

        【讨论】:

          【解决方案5】:

          下载是 IO,可以使用非阻塞套接字异步完成,也可以扭曲。这两种解决方案都将比线程或多处理更高效。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-09-06
            • 1970-01-01
            • 2021-09-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-30
            相关资源
            最近更新 更多