urllib2与多进程无用?

9 投票
5 回答
6643 浏览
提问于 2025-04-16 22:41

我最近尝试加速一个小工具,这个工具使用urllib2发送请求到一个(非官方的)Twitter按钮计数网址(超过2000个网址),然后解析它的结果。我在这里阅读了关于多线程和多进程的讨论,发现多线程反而让整个过程变慢,相比于标准的非线程版本。但我找不到一个(可能非常简单的)问题的答案:

使用多进程能加速网址请求吗,还是说瓶颈在于网络适配器?我不太明白,比如说urllib2的open方法中,哪一部分可以并行处理,以及该怎么做...

补充:这是我想要加速的请求和当前的多进程设置:

 urls=["www.foo.bar", "www.bar.foo",...]
 tw_url='http://urls.api.twitter.com/1/urls/count.json?url=%s'

 def getTweets(self,urls):
    for i in urls:
        try:
            self.tw_que=urllib2.urlopen(tw_url %(i))
            self.jsons=json.loads(self.tw_que.read())
            self.tweets.append({'url':i,'date':today,'tweets':self.jsons['count']})
        except ValueError:
            print ....
            continue
    return self.tweets 

 if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=4)            
    result = [pool.apply_async(getTweets(i,)) for i in urls]
    [i.get() for i in result]

5 个回答

3

这要看情况!你是在联系不同的服务器吗?传输的文件是大是小?你花很多时间在等服务器回复,还是在传输数据上?这些都会影响结果。

一般来说,多进程会有一些额外的开销,所以你需要确保通过并行处理工作所获得的速度提升,能够超过这些额外的开销。

还有一点:网络和输入输出(I/O)密集型的应用程序,使用异步I/O和事件驱动的架构效果更好,也更容易扩展,而不是使用线程或多进程。因为在这类应用中,很多时间都是在等待输入输出,而不是在进行计算。

针对你的具体问题,我建议你尝试使用TwistedgeventTornado,或者其他不使用线程来并行连接的网络框架。

7

又来了,关于GIL(全局解释器锁)的讨论。事情是这样的,使用urllib2获取内容主要是依赖输入输出(IO)的操作。对于这种IO密集型的任务,使用原生线程和多进程的性能是差不多的(线程的问题主要出现在CPU密集型的任务上)。没错,你可以加速这个过程,我自己就用Python的线程和大约10个下载线程做过。

基本上,你可以用一个生产者-消费者的模型,一个线程(或进程)负责生成要下载的URL,然后有个线程(或进程)从这个队列中取出URL并向服务器发送请求。

这里有一些伪代码:

# Make sure that the queue is thread-safe!!

def producer(self):
    # Only need one producer, although you could have multiple
    with fh = open('urllist.txt', 'r'):
        for line in fh:
            self.queue.enqueue(line.strip())

def consumer(self):
    # Fire up N of these babies for some speed
    while True:
        url = self.queue.dequeue()
        dh = urllib2.urlopen(url)
        with fh = open('/dev/null', 'w'): # gotta put it somewhere
            fh.write(dh.read())

如果你下载的数据量非常大(比如几百MB),而且单个请求已经把带宽用满了,那么同时进行多个下载就没什么意义了。通常情况下,进行多个下载的原因是请求的数据量小,而且延迟和开销相对较高。

7

看看这个叫做 gevent 的东西,特别是这个例子: concurrent_download.py。它的速度会比多进程和多线程快很多,而且还能轻松处理成千上万的连接。

撰写回答