加速 urlib.urlretrieve

speeding up urlib.urlretrieve

我正在从网上下载图片,结果我需要下载很多图片。我正在使用以下代码片段的一个版本(实际上循环我打算下载的链接和下载图片:

import urllib
urllib.urlretrieve(link, filename)

我每 15 分钟下载大约 1000 张图片,根据我需要下载的图片数量来看,速度非常慢。

为了效率,我每 5 秒设置一次超时(还有很多下载持续时间更长):

import socket
socket.setdefaulttimeout(5)

除了运行计算机集群上并行下载的工作,有没有办法让图片下载更快/更有效率?

我上面的代码非常幼稚,因为我没有利用多线程。显然需要 url 个请求才能得到响应,但没有理由在代理服务器响应时计算机无法发出进一步的请求。

做下面的调整,你可以提高10倍的效率——还有更多的方法可以提高效率,比如scrapy。

要添加多线程,使用 multiprocessing 包执行如下操作:

1) 将url检索封装在一个函数中:

import import urllib.request

def geturl(link,i):
try:
    urllib.request.urlretrieve(link, str(i)+".jpg")
except:
    pass

2) 然后创建一个包含所有 url 以及您想要的下载图片名称的集合:

urls = [url1,url2,url3,urln]
names = [i for i in range(0,len(urls))]

3) 从 multiprocessing 包中导入 Pool class 并使用这样的 class 创建一个对象(显然,您会在实际程序的代码的第一行中包含所有导入):

from multiprocessing.dummy import Pool as ThreadPool
pool = ThreadPool(100)

然后使用pool.starmap()方法并传递函数和函数的参数。

results = pool.starmap(geturl, zip(links, d))

注意:pool.starmap() 仅适用于 Python 3

当程序进入 I/O 等待时,执行将暂停,以便内核可以执行与 I/O 请求相关的低级操作(这称为 context switch)并且在 I/O 操作完成之前不会恢复。

上下文切换是一项相当繁重的操作。它要求我们保存程序的状态(丢失我们在 CPU 级别的任何类型的缓存)并放弃使用 CPU。稍后,当我们再次被允许运行时,我们必须花时间在主板上重新初始化我们的程序并准备恢复(当然,这一切都是在幕后发生的)。

另一方面,对于 concurrency,,我们通常有一个称为“事件循环”运行ning 的东西,它管理我们程序中到达 运行 的内容以及时间。本质上,事件循环只是一个需要 运行 的函数列表。列表顶部的函数获得 运行,然后是下一个,依此类推。

下面显示了一个简单的事件循环示例:

from Queue import Queue
from functools import partial

eventloop = None

class EventLoop(Queue):
    def start(self):
        while True:
            function = self.get()
            function()

def do_hello():
    global eventloop
    print "Hello"
    eventloop.put(do_world)

def do_world():
    global eventloop
    print "world"
    eventloop.put(do_hello)

if __name__ == "__main__":
    eventloop = EventLoop()
    eventloop.put(do_hello)
    eventloop.start()

如果以上内容看起来像您可能会用到的东西,并且您还想了解 gevent, tornado,AsyncIO, 如何帮助您解决问题,请前往你的(大学)图书馆,查看 High Performance Python by Micha Gorelick and Ian Ozsvald,并阅读第 181-202 页。

注意:以上代码和文字来自提到的book