小编典典

如何在python中使用urllib2加快获取页面的速度?

python

我有一个脚本,该脚本可获取多个网页并解析信息。

(可以在http://bluedevilbooks.com/search/?DEPT=MATH&CLASS=103&SEC=01上看到一个示例)

我在上面运行了cProfile,正如我所假设的,urlopen占用大量时间。有没有一种方法可以更快地获取页面?还是一次获取多个页面的方法?我将做最简单的事情,因为我是python和Web开发的新手。

提前致谢!:)

更新:我有一个名为的函数fetchURLs(),该函数用于使我需要的URL组成一个数组urls = fetchURLS().URL都是来自Amazon和eBay API的XML文件(这使我感到困惑,为什么加载时间这么长,也许我的虚拟主机很慢?)

我需要做的是加载每个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)

阅读 198

收藏
2020-12-20

共1个答案

小编典典

编辑 :我正在扩大答案,以包括一个更完善的示例。我在这篇文章中发现了很多关于线程与异步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()是同步代码的更好方法。我认为到目前为止,此代码已对所有线程示例进行了改进。

保持连接

如果您所有的URL都指向同一服务器,那么WoLpH关于使用保持活动连接的建议可能会非常有用。

扭曲的

亚伦·加拉格尔(Aaron
Gallagher)是twisted框架的狂热者,他对任何建议使用线程的人都怀有敌意。不幸的是,他的许多说法都是错误的信息。例如,他说“
-1表示线程建议。这是IO绑定的;线程在这里无用。” 这与证据相反,因为我和Nick
T都已经证明了使用线程的速度提高。实际上,使用Python的线程可以最大程度地提高I /
O绑定的应用程序的收益(而CPU绑定的应用程序则没有收益)。Aaron对线程的误导性批评表明,他对并行编程感到困惑。

适用于正确工作的正确工具

我很清楚与使用线程,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与线程之间系统性能差异的有效测试。

2020-12-20