我想用 Python 编写一个脚本,它可以从数据库中获取 url,并同时下载网页以加快速度,而不是等待每个页面一个接一个地下载。
根据这个线程,Python 不允许这样做,因为称为Global Interpreter Lock的东西可以防止多次启动同一个脚本。
在花时间学习 Twisted 框架之前,我想确保没有更简单的方法来完成我上面需要做的事情。
谢谢你的任何提示。
我想用 Python 编写一个脚本,它可以从数据库中获取 url,并同时下载网页以加快速度,而不是等待每个页面一个接一个地下载。
根据这个线程,Python 不允许这样做,因为称为Global Interpreter Lock的东西可以防止多次启动同一个脚本。
在花时间学习 Twisted 框架之前,我想确保没有更简单的方法来完成我上面需要做的事情。
谢谢你的任何提示。
GIL 会阻止您有效地使用线程进行处理器负载平衡。由于这不是处理器负载平衡,而是防止一个 IO 等待停止整个下载,因此 GIL 与此处无关。*)
因此,您需要做的就是创建多个同时下载的进程。您可以使用 threading 模块或 multiprocessing 模块来做到这一点。
*)好吧...除非您有千兆连接,并且您的问题实际上是您的处理器在您的网络之前过载。但这显然不是这里的情况。
我最近解决了同样的问题。需要考虑的一件事是,有些人不喜欢让他们的服务器陷入困境,并且会阻止这样做的 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 页的速度结束。
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)
下载是 IO,可以使用非阻塞套接字或扭曲的方式异步完成。这两种解决方案都将比线程或多处理更有效。