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