Python Urllib2&;美丽的一对:很好的一对,但是太慢了-urllib3&;线程?
当我听到一些关于线程和urllib3的好消息时,我正在寻找一种优化代码的方法。显然,人们不同意哪种解决方案是最好的 我下面的脚本的问题是执行时间:太慢了 步骤1:我获取此页面 步骤2:我使用BeautifulSoup解析页面 第3步:我将数据放入excel文档中 第4步:我一遍又一遍地为我的名单(大名单)中的所有国家/地区做这件事 (我只是将url中的“阿富汗”改为另一个国家) 这是我的密码:Python Urllib2&;美丽的一对:很好的一对,但是太慢了-urllib3&;线程?,python,multithreading,performance,beautifulsoup,urllib2,Python,Multithreading,Performance,Beautifulsoup,Urllib2,当我听到一些关于线程和urllib3的好消息时,我正在寻找一种优化代码的方法。显然,人们不同意哪种解决方案是最好的 我下面的脚本的问题是执行时间:太慢了 步骤1:我获取此页面 步骤2:我使用BeautifulSoup解析页面 第3步:我将数据放入excel文档中 第4步:我一遍又一遍地为我的名单(大名单)中的所有国家/地区做这件事 (我只是将url中的“阿富汗”改为另一个国家) 这是我的密码: ws = wb.add_sheet("BULATS_IA") #We add a new tab i
ws = wb.add_sheet("BULATS_IA") #We add a new tab in the excel doc
x = 0 # We need x and y for pulling the data into the excel doc
y = 0
Countries_List = ['Afghanistan','Albania','Andorra','Argentina','Armenia','Australia','Austria','Azerbaijan','Bahrain','Bangladesh','Belgium','Belize','Bolivia','Bosnia and Herzegovina','Brazil','Brunei Darussalam','Bulgaria','Cameroon','Canada','Central African Republic','Chile','China','Colombia','Costa Rica','Croatia','Cuba','Cyprus','Czech Republic','Denmark','Dominican Republic','Ecuador','Egypt','Eritrea','Estonia','Ethiopia','Faroe Islands','Fiji','Finland','France','French Polynesia','Georgia','Germany','Gibraltar','Greece','Grenada','Hong Kong','Hungary','Iceland','India','Indonesia','Iran','Iraq','Ireland','Israel','Italy','Jamaica','Japan','Jordan','Kazakhstan','Kenya','Kuwait','Latvia','Lebanon','Libya','Liechtenstein','Lithuania','Luxembourg','Macau','Macedonia','Malaysia','Maldives','Malta','Mexico','Monaco','Montenegro','Morocco','Mozambique','Myanmar (Burma)','Nepal','Netherlands','New Caledonia','New Zealand','Nigeria','Norway','Oman','Pakistan','Palestine','Papua New Guinea','Paraguay','Peru','Philippines','Poland','Portugal','Qatar','Romania','Russia','Saudi Arabia','Serbia','Singapore','Slovakia','Slovenia','South Africa','South Korea','Spain','Sri Lanka','Sweden','Switzerland','Syria','Taiwan','Thailand','Trinadad and Tobago','Tunisia','Turkey','Ukraine','United Arab Emirates','United Kingdom','United States','Uruguay','Uzbekistan','Venezuela','Vietnam']
Longueur = len(Countries_List)
for Countries in Countries_List:
y = 0
htmlSource = urllib.urlopen("http://www.cambridgeesol.org/institutions/results.php?region=%s&type=&BULATS=on" % (Countries)).read() # I am opening the page with the name of the correspondant country in the url
s = soup(htmlSource)
tableGood = s.findAll('table')
try:
rows = tableGood[3].findAll('tr')
for tr in rows:
cols = tr.findAll('td')
y = 0
x = x + 1
for td in cols:
hum = td.text
ws.write(x,y,hum)
y = y + 1
wb.save("%s.xls" % name_excel)
except (IndexError):
pass
所以我知道一切都不是完美的,但我期待着在Python中学习新的东西!这个脚本非常慢,因为urllib2没有那么快,而且非常漂亮。对于汤的事情,我想我真的不能做得更好,但是对于urllib2,我没有
编辑1:
对我来说似乎很有趣。
你们觉得这个潜在的解决方案怎么样
# 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())
编辑2:URLLIB3
有人能告诉我更多关于这个的事情吗
对多个请求重新使用同一套接字连接
(HTTPConnectionPool和HTTPSConnectionPool)(带可选
客户端证书验证)。
就我为不同的页面请求了122次相同的网站而言,我想重复使用相同的套接字连接会很有趣,我错了吗?
不能快点吗
http = urllib3.PoolManager()
r = http.request('GET', 'http://www.bulats.org')
for Pages in Pages_List:
r = http.request('GET', 'http://www.bulats.org/agents/find-an-agent?field_continent_tid=All&field_country_tid=All&page=%s' % (Pages))
s = soup(r.data)
我不认为urllib或beautifulsou很慢。我用修改过的版本在本地机器上运行您的代码(删除了excel的内容)。打开连接、下载内容、解析内容并将其打印到某个国家的控制台大约需要100毫秒 10ms是BeautifulSoup解析内容并按国家/地区打印到控制台所花费的总时间。这足够快了 我不相信使用刮擦或线程将解决问题。因为问题在于人们对速度的期望 欢迎来到HTTP世界。有时候会很慢,有时候会很快。两个连接速度慢的原因
- 因为服务器正在处理您的请求(有时返回404)
- DNS解析
- HTTP握手
- 您的ISP的连接稳定性
- 您的带宽速率
- 丢包率
看看请求库。阅读他们的文档。如果您这样做是为了进一步学习Python,那么不要直接跳入Scrapy 嘿,伙计们, 一些来自问题的消息!我找到了这个脚本,它可能很有用!我实际上正在测试它,它很有希望(6.03运行下面的脚本) 我的想法是找到一种将其与urllib3混合的方法。实际上,我多次向同一个主机提出请求 PoolManager将随时为您重新使用连接 您请求相同的主机。这应该涵盖大多数没有 效率显著降低,但您始终可以降低到 较低级别的组件可实现更精细的控制。(urrlib3文档站点) 无论如何,这看起来非常有趣,如果我还看不到如何混合这两种功能(urllib3和下面的线程脚本),我想这是可行的!:-) 非常感谢你花时间帮我,闻起来很香
import Queue
import threading
import urllib2
import time
from bs4 import BeautifulSoup as BeautifulSoup
hosts = ["http://www.bulats.org//agents/find-an-agent?field_continent_tid=All&field_country_tid=All", "http://www.bulats.org//agents/find-an-agent?field_continent_tid=All&field_country_tid=All&page=1", "http://www.bulats.org//agents/find-an-agent?field_continent_tid=All&field_country_tid=All&page=2", "http://www.bulats.org//agents/find-an-agent?field_continent_tid=All&field_country_tid=All&page=3", "http://www.bulats.org//agents/find-an-agent?field_continent_tid=All&field_country_tid=All&page=4", "http://www.bulats.org//agents/find-an-agent?field_continent_tid=All&field_country_tid=All&page=5", "http://www.bulats.org//agents/find-an-agent?field_continent_tid=All&field_country_tid=All&page=6"]
queue = Queue.Queue()
out_queue = Queue.Queue()
class ThreadUrl(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, queue, out_queue):
threading.Thread.__init__(self)
self.queue = queue
self.out_queue = out_queue
def run(self):
while True:
#grabs host from queue
host = self.queue.get()
#grabs urls of hosts and then grabs chunk of webpage
url = urllib2.urlopen(host)
chunk = url.read()
#place chunk into out queue
self.out_queue.put(chunk)
#signals to queue job is done
self.queue.task_done()
class DatamineThread(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, out_queue):
threading.Thread.__init__(self)
self.out_queue = out_queue
def run(self):
while True:
#grabs host from queue
chunk = self.out_queue.get()
#parse the chunk
soup = BeautifulSoup(chunk)
#print soup.findAll(['table'])
tableau = soup.find('table')
rows = tableau.findAll('tr')
for tr in rows:
cols = tr.findAll('td')
for td in cols:
texte_bu = td.text
texte_bu = texte_bu.encode('utf-8')
print texte_bu
#signals to queue job is done
self.out_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, out_queue)
t.setDaemon(True)
t.start()
#populate queue with data
for host in hosts:
queue.put(host)
for i in range(5):
dt = DatamineThread(out_queue)
dt.setDaemon(True)
dt.start()
#wait on the queue until everything has been processed
queue.join()
out_queue.join()
main()
print "Elapsed Time: %s" % (time.time() - start)
考虑使用类似的方法。参考该示例,与的组合将类似于:
import workerpool
import urllib3
URL_LIST = [] # Fill this from somewhere
NUM_SOCKETS = 3
NUM_WORKERS = 5
# We want a few more workers than sockets so that they have extra
# time to parse things and such.
http = urllib3.PoolManager(maxsize=NUM_SOCKETS)
workers = workerpool.WorkerPool(size=NUM_WORKERS)
class MyJob(workerpool.Job):
def __init__(self, url):
self.url = url
def run(self):
r = http.request('GET', self.url)
# ... do parsing stuff here
for url in URL_LIST:
workers.put(MyJob(url))
# Send shutdown jobs to all threads, and wait until all the jobs have been completed
# (If you don't do this, the script might hang due to a rogue undead thread.)
workers.shutdown()
workers.wait()
您可能会从Mass Downloader示例中注意到,有多种方法可以做到这一点。我选择这个特殊的例子只是因为它不那么神奇,但其他任何策略都是有效的
免责声明:我是urllib3和workerpool的作者。是的,这里的问题是获取是单线程的。但如果使用多线程,则必须使写入excel的过程线程安全。我推荐scrapy,一个Python的刮片框架,它可以为您做任何事情。非常感谢,我将看看scrapy能为我做些什么。而且urllib3也不是有效的解决方案?:)但如果有任何可能,使它更快,而不使用刮痧,这将是更好的形式,我。我正在学习python,所以我想了解所有内容!你不应该打开一个以上的连接到同一个网站,我相信这更像是一个“君子协定”2或3同时连接到同一个服务器是好的,100将不会。请记住,您建立的每个连接都有性能成本。有TCP三方握手,然后是慢速启动。如果可以,请使用管道,否则请使用连接保持活动。非常感谢您提供的所有信息!当然,Scrapy看起来很有趣,但我的目标是学习Python,所以我需要继续使用Python:-)我需要删除wb。将(“%s.xls”%name\u excel)保存为“for”也很愚蠢。我将看看Requests lib,正如您所建议的:)“考虑使用urllib3。它支持连接池和通过进程(而不是线程)的多个并发请求。它应该可以解决这个问题。如果您联系许多不同的站点,请小心垃圾收集连接池,因为每个站点都有自己的池。”在我的例子中,它可能很有趣不?我还没有使用url3,但您写下的内容非常有希望。学习的最大步骤是尝试不同的选择,了解问题和可能的解决方案。你也可以说“了解来龙去脉”,找到解决问题的合适方法。但首先要很好地理解“问题”。请求在URLLib上有一个很好的API,所以也可以看看源代码。继续玩。。