Python urllib2.urlopen()很慢,需要更好的方法来阅读几个url
正如标题所示,我正在使用python编写的网站上工作,并且会多次调用urllib2模块来读取网站。 然后我用BeautifulSoupparsing它们。
由于我必须阅读5-10个网站,页面需要一段时间才能加载。
我只是想知道是否有办法一次读取所有的网站? 或任何使它更快,就像我应该在每次阅读后closuresurllib2.urlopen,或保持打开?
另外,如果我只是切换到PHP,那么从其他网站获取和Parsi HTML和XML文件会更快? 我只是希望它加载更快,而不是目前所需的〜20秒
我正在使用threading
和Queue
等现代Python模块重写下面的Dumb Guy的代码。
import threading, urllib2 import Queue urls_to_load = [ 'http://stackoverflow.com/', 'http://slashdot.org/', 'http://www.archive.org/', 'http://www.yahoo.co.jp/', ] def read_url(url, queue): data = urllib2.urlopen(url).read() print('Fetched %s from %s' % (len(data), url)) queue.put(data) def fetch_parallel(): result = Queue.Queue() threads = [threading.Thread(target=read_url, args = (url,result)) for url in urls_to_load] for t in threads: t.start() for t in threads: t.join() return result def fetch_sequencial(): result = Queue.Queue() for url in urls_to_load: read_url(url,result) return result
find_sequencial()
最佳时间是2s。 fetch_parallel()
最佳时间是0.9s。
由于GIL,在Python中说thread
是无用的也是不正确的。 这是线程在Python中很有用的情况之一,因为线程在I / O上被阻塞。 正如你在我的结果中所看到的那样,并行情况比以前快两倍。
编辑:请看看Wai的post,以获得更好的代码版本。 请注意,这段代码没有任何问题,尽pipe有下面的注释,它仍然可以正常工作 。
阅读网页的速度可能受限于你的互联网连接,而不是Python。
你可以使用线程一次加载它们。
import thread, time, urllib websites = {} def read_url(url): websites[url] = urllib.open(url).read() for url in urls_to_load: thread.start_new_thread(read_url, (url,)) while websites.keys() != urls_to_load: time.sleep(0.1) # Now websites will contain the contents of all the web pages in urls_to_load
这是maby不完美。 但是当我需要从一个网站的数据。 我只是这样做:
import socket def geturldata(url): #NO HTTP URLS PLEASE!!!!! server = url.split("/")[0] args = url.replace(server,"") returndata = str() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((server, 80)) #lets connect :p s.send("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (args, server)) #simple http request while 1: data = s.recv(1024) #buffer if not data: break returndata = returndata + data s.close() return returndata.split("\n\r")[1]
作为一般规则,任何语言的给定结构在测量之前都是不慢的。
在Python中,不仅时间经常违背直觉,而且测量执行时间的工具非常好。
Scrapy可能对你有用。 如果你不需要所有的function,你可以使用twisted的twisted.web.client.getPage
来代替。 一个线程中的asynchronousIO将比使用多个线程和阻塞IO的任何东西都更具性能和易于debugging。
不知道为什么没有人提到multiprocessing
(如果有人知道为什么这可能是一个坏主意,让我知道):
import multiprocessing from urllib2 import urlopen URLS = [....] def get_content(url): return urlopen(url).read() pool = multiprocessing.Pool(processes=8) # play with ``processes`` for best results results = pool.map(get_content, URLS) # This line blocks, look at map_async # for non-blocking map() call pool.close() # the process pool no longer accepts new tasks pool.join() # join the processes: this blocks until all URLs are processed for result in results: # do something
有多multiprocessing
池的注意事项。 首先,与线程不同,这些是全新的Python进程(解释器)。 虽然它不受全球解释器locking的限制,但意味着您在传递新stream程方面的能力有限。
您不能传递dynamic定义的lambdas和函数。 map()
调用中使用的函数必须在模块中定义,以允许其他进程导入它。
Pool.map()
是并发处理多个任务最直接的方法,它没有提供传递多个参数的方法,因此您可能需要编写包装函数或更改函数签名,或者将多个parameter passing为被映射的迭代的一部分。
你不能有subprocess产生新的。 只有父母可以产生subprocess。 这意味着您必须仔细计划和testing(有时会编写代码的多个版本),以确定最有效的stream程使用情况。
尽pipe有缺点,但我发现多处理是并发阻塞调用最直接的方法之一。 你也可以结合多处理和线程(afaik,但如果我错了,请纠正我),或者结合多处理与绿色线程。
1)你是否多次打开同一个网站,或许多不同的网站? 如果有很多不同的网站,我认为urllib2是好的。 如果一次又一次地做同一个网站,我有一些个人运气urllib3 http://code.google.com/p/urllib3/
2)BeautifulSoup易于使用,但速度很慢。 如果您必须使用它,请确保分解您的标签以摆脱内存泄漏..或者它可能会导致内存问题(对我来说)。
你的内存和CPU是什么样的? 如果你最大化你的CPU,确保你使用真正的重量级的线程,所以你可以运行在多个核心上。
如何使用pycurl?
你可以通过
$ sudo apt-get python-pycurl
首先,你应该尝试multithreading/多处理包。 目前,三大stream行的是多处理 , concurrent.futures和[threading] [3]。 这些软件包可以帮助您同时打开多个url,这可能会提高速度。
更重要的是,在使用multithreading处理之后,如果尝试同时打开数百个URL,则会发现urllib.request.urlopen非常慢,而打开和读取上下文成为最耗时的部分。 所以,如果你想让它更快,你应该尝试请求包,requests.get(url).content()比urllib.request.urlopen(url).read()更快。
所以,在这里我列出了两个例子来做快速多urlparsing,速度比其他答案快。 第一个例子使用古典线程包,同时生成数百个线程。 (一个微不足道的缺点是它不能保持股票的原始顺序。)
import time import threading import pandas as pd import requests from bs4 import BeautifulSoup ticker = pd.ExcelFile('short_tickerlist.xlsx') ticker_df = ticker.parse(str(ticker.sheet_names[0])) ticker_list = list(ticker_df['Ticker']) start = time.time() result = [] def fetch(ticker): url = ('http://finance.yahoo.com/quote/' + ticker) print('Visit ' + url) text = requests.get(url).content soup = BeautifulSoup(text,'lxml') result.append([ticker,soup]) print(url +' fetching...... ' + str(time.time()-start)) if __name__ == '__main__': process = [None] * len(ticker_list) for i in range(len(ticker_list)): process[i] = threading.Thread(target=fetch, args=[ticker_list[i]]) for i in range(len(ticker_list)): print('Start_' + str(i)) process[i].start() # for i in range(len(ticker_list)): # print('Join_' + str(i)) # process[i].join() print("Elapsed Time: %ss" % (time.time() - start))
第二个例子使用多处理程序包,它更简单一些。 由于您只需要声明池的数量并映射函数。 获取上下文后,顺序不会改变,速度与第一个例子类似,但比其他方法快得多。
from multiprocessing import Pool import requests from bs4 import BeautifulSoup import pandas as pd import os import time os.chdir('file_path') start = time.time() def fetch_url(x): print('Getting Data') myurl = ("http://finance.yahoo.com/q/cp?s=%s" % x) html = requests.get(myurl).content soup = BeautifulSoup(html,'lxml') out = str(soup) listOut = [x, out] return listOut tickDF = pd.read_excel('short_tickerlist.xlsx') li = tickDF['Ticker'].tolist() if __name__ == '__main__': p = Pool(5) output = p.map(fetch_url, ji, chunksize=30) print("Time is %ss" %(time.time()-start))