2

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

根据这个线程,Python 不允许这样做,因为称为Global Interpreter Lock的东西可以防止多次启动同一个脚本。

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

谢谢你的任何提示。

4

5 回答 5

9

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

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

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

于 2009-09-29T11:52:32.423 回答
7

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

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

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

于 2009-09-29T12:17:17.870 回答
2

我最近解决了同样的问题。需要考虑的一件事是,有些人不喜欢让他们的服务器陷入困境,并且会阻止这样做的 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 页的速度结束。

于 2009-09-29T12:38:07.853 回答
2

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

您需要做的是从 DB 获取 url,并为每个 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)
于 2009-09-29T14:43:24.780 回答
0

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

于 2009-09-29T11:57:11.307 回答