我有一个脚本,该脚本可获取多个网页并解析信息。
(可以在http://bluedevilbooks.com/search/?DEPT=MATH&CLASS=103&SEC=01上看到一个示例)
我在上面运行了cProfile,正如我所假设的,urlopen占用大量时间。有没有一种方法可以更快地获取页面?还是一次获取多个页面的方法?我将做最简单的事情,因为我是python和Web开发的新手。
提前致谢!:)
更新:我有一个名为的函数fetchURLs(),该函数用于使我需要的URL组成一个数组urls = fetchURLS().URL都是来自Amazon和eBay API的XML文件(这使我感到困惑,为什么加载时间这么长,也许我的虚拟主机很慢?)
fetchURLs()
urls = fetchURLS()
我需要做的是加载每个URL,阅读每个页面,然后将数据发送到脚本的另一部分,该脚本将解析并显示数据。
请注意,在获取所有页面之前,我无法完成后一部分,这就是我的问题。
而且,我相信我的主机一次只能将我限制在25个进程之内,因此,服务器上最简单的操作都是不错的:)
这是时间:
Sun Aug 15 20:51:22 2010 prof 211352 function calls (209292 primitive calls) in 22.254 CPU seconds Ordered by: internal time List reduced from 404 to 10 due to restriction <10> ncalls tottime percall cumtime percall filename:lineno(function) 10 18.056 1.806 18.056 1.806 {_socket.getaddrinfo} 4991 2.730 0.001 2.730 0.001 {method 'recv' of '_socket.socket' objects} 10 0.490 0.049 0.490 0.049 {method 'connect' of '_socket.socket' objects} 2415 0.079 0.000 0.079 0.000 {method 'translate' of 'unicode' objects} 12 0.061 0.005 0.745 0.062 /usr/local/lib/python2.6/HTMLParser.py:132(goahead) 3428 0.060 0.000 0.202 0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:1306(endData) 1698 0.055 0.000 0.068 0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:1351(_smartPop) 4125 0.053 0.000 0.056 0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:118(setup) 1698 0.042 0.000 0.358 0.000 /usr/local/lib/python2.6/HTMLParser.py:224(parse_starttag) 1698 0.042 0.000 0.275 0.000 /usr/local/lib/python2.6/site-packages/BeautifulSoup.py:1397(unknown_starttag)
编辑 :我正在扩大答案,以包括一个更完善的示例。我在这篇文章中发现了很多关于线程与异步I / O的敌意和错误信息。因此,我还添加了更多论点来驳斥某些无效的主张。我希望这将帮助人们为正确的工作选择正确的工具。
这是3天前提出的问题的答案。
Python urllib2.open运行缓慢,需要一种更好的方法来读取多个URL-堆栈溢出 Python urllib2.urlopen()运行缓慢,需要一种更好的方法来读取多个URL
我正在完善代码以显示如何使用线程并行获取多个网页。
import time import threading import Queue # utility - spawn a thread to execute target for each args def run_parallel_in_threads(target, args_list): result = Queue.Queue() # wrapper to collect return value in a Queue def task_wrapper(*args): result.put(target(*args)) threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list] for t in threads: t.start() for t in threads: t.join() return result def dummy_task(n): for i in xrange(n): time.sleep(0.1) return n # below is the application code urls = [ ('http://www.google.com/',), ('http://www.lycos.com/',), ('http://www.bing.com/',), ('http://www.altavista.com/',), ('http://achewood.com/',), ] def fetch(url): return urllib2.urlopen(url).read() run_parallel_in_threads(fetch, urls)
如您所见,特定于应用程序的代码只有3行,如果您比较积极,可以将其折叠为1行。我认为没有人能证明他们的主张是复杂且不可维持的。
不幸的是,这里发布的大多数其他线程代码都有一些缺陷。他们中的许多人都进行主动轮询以等待代码完成。join()是同步代码的更好方法。我认为到目前为止,此代码已对所有线程示例进行了改进。
join()
保持连接
如果您所有的URL都指向同一服务器,那么WoLpH关于使用保持活动连接的建议可能会非常有用。
扭曲的
亚伦·加拉格尔(Aaron Gallagher)是twisted框架的狂热者,他对任何建议使用线程的人都怀有敌意。不幸的是,他的许多说法都是错误的信息。例如,他说“ -1表示线程建议。这是IO绑定的;线程在这里无用。” 这与证据相反,因为我和Nick T都已经证明了使用线程的速度提高。实际上,使用Python的线程可以最大程度地提高I / O绑定的应用程序的收益(而CPU绑定的应用程序则没有收益)。Aaron对线程的误导性批评表明,他对并行编程感到困惑。
twisted
适用于正确工作的正确工具
我很清楚与使用线程,python,异步I / O等进行并行编程有关的问题。每个工具都有其优缺点。对于每种情况,都有一个适当的工具。我不反对扭曲(尽管我自己还没有部署)。但是我不相信我们可以断定在所有情况下线程都是错误的,而扭曲是良好的。
例如,如果OP的要求是并行获取10,000个网站,则异步I / O是可取的。线程是不适当的(除非使用无堆栈Python)。
亚伦对线程的反对大多是概括。他未能意识到这是一项琐碎的并行化任务。每个任务都是独立的,并且不共享资源。因此,他的大部分攻击都不适用。
鉴于我的代码没有外部依赖关系,因此我将其称为用于正确工作的正确工具。
性能
我认为大多数人都会同意,此任务的性能在很大程度上取决于网络代码和外部服务器,而平台代码的性能应在这些方面可以忽略不计。但是Aaron的基准测试显示,与线程代码相比,速度提高了50%。我认为有必要对这种明显的速度提高做出反应。
在尼克的代码中,有一个明显的缺陷导致效率低下。但是您如何解释我的代码比233ms的速度提高呢?我认为即使是扭曲的粉丝也不会做出结论,将其归因于扭曲的效率。毕竟,系统代码之外还有大量变量,例如远程服务器的性能,网络,缓存以及urllib2和扭曲的Web客户端之间的差异实现等。
为了确保Python的线程不会导致大量的低效率,我做了一个快速基准测试,先生成5个线程,然后生成500个线程。我很舒服地说生成5个线程的开销可以忽略不计,并且不能解释233ms的速度差异。
In [274]: %time run_parallel_in_threads(dummy_task, [(0,)]*5) CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s Wall time: 0.00 s Out[275]: <Queue.Queue instance at 0x038B2878> In [276]: %time run_parallel_in_threads(dummy_task, [(0,)]*500) CPU times: user 0.16 s, sys: 0.00 s, total: 0.16 s Wall time: 0.16 s In [278]: %time run_parallel_in_threads(dummy_task, [(10,)]*500) CPU times: user 1.13 s, sys: 0.00 s, total: 1.13 s Wall time: 1.13 s <<<<<<<< This means 0.13s of overhead
在我的并行读取中进行的进一步测试显示,在17次运行中响应时间存在巨大差异。(不幸的是,我没有费力去验证Aaron的代码)。
0.75 s 0.38 s 0.59 s 0.38 s 0.62 s 1.50 s 0.49 s 0.36 s 0.95 s 0.43 s 0.61 s 0.81 s 0.46 s 1.21 s 2.87 s 1.04 s 1.72 s
我的测试不支持亚伦的结论,即线程始终比异步I / O慢很多。给定涉及的变量数量,我不得不说这不是衡量异步I / O与线程之间系统性能差异的有效测试。