如何并发下载多个页面?

2 投票
5 回答
3708 浏览
提问于 2025-04-15 14:40

我想写一个Python脚本,从数据库中获取网址,并同时下载网页,这样可以加快速度,而不是一个一个等着下载。

根据这个讨论,Python不允许这样做,因为有一个叫做全局解释器锁的东西,它会阻止同一个脚本同时运行多次。

在我花时间学习Twisted框架之前,我想确认一下有没有更简单的方法来完成我上面提到的事情。

谢谢任何建议。

5 个回答

2

我最近也遇到过这个问题。有一点需要注意的是,有些人不喜欢他们的服务器被拖慢,如果你这样做,他们可能会封锁你的IP地址。我听说的一个常规礼仪是每次请求页面之间间隔大约3秒,但这个时间是可以灵活调整的。

如果你是从多个网站下载内容,可以按域名把网址分组,每个域名创建一个线程。然后在你的线程里可以这样做:

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个页面。

7

GIL(全局解释器锁)会让你在使用线程时,无法有效地进行处理器负载均衡。这里并不是在进行处理器负载均衡,而是为了防止一个输入输出的等待导致整个下载停滞,所以GIL在这里并不重要。

所以你需要做的就是创建几个同时下载的进程。你可以使用线程模块或者多进程模块来实现这个。

*) 嗯……除非你有千兆的网络连接,而你的问题确实是处理器在网络之前就已经超负荷了。但显然在这里并不是这种情况。

9

别担心GIL(全局解释器锁),在你的情况下它并不重要。

想要实现你想要的功能,最简单的方法就是创建一个线程池,使用threading模块和来自ASPN的线程池实现。线程池里的每个线程都可以用httplib来下载你的网页。

另一种选择是使用PyCURL模块——它本身就支持并行下载,所以你不需要自己去实现这个功能。

撰写回答