使用urllib2.urlopen()正确关闭打开的文件

8

我有一个Python脚本中的以下代码

  try:
    # send the query request
    sf = urllib2.urlopen(search_query)
    search_soup = BeautifulSoup.BeautifulStoneSoup(sf.read())
    sf.close()
  except Exception, err:
    print("Couldn't get programme information.")
    print(str(err))
    return

我很担心如果sf.read()出现错误,那么sf.close()就不会被调用。 我试着将sf.close()放入finally块中,但如果在urlopen()上发生异常,则没有文件需要关闭,并且我会在finally块中遇到异常! 所以我尝试了......
  try:
    with urllib2.urlopen(search_query) as sf:
      search_soup = BeautifulSoup.BeautifulStoneSoup(sf.read())
  except Exception, err:
    print("Couldn't get programme information.")
    print(str(err))
    return

但是在with...这一行上出现了语法错误。我该如何最好地处理这个问题,我感到很愚蠢!

正如评论者所指出的那样,我正在使用Pys60,它是Python 2.5.4。


2
“with”语句仅适用于Python 2.6,或者在文件顶部加入“from future import with_statement”后也可在2.5中使用。我不太记得PyS60实现的是哪个Python版本,但可能是2.5? - Liquid_Fire
这是2.5.4版本,导入是个不错的点 :) - Habbie
8个回答

19

我会使用contextlib.closing(与来自__future__的with_statement结合使用,以用于旧版本的Python):

from contextlib import closing

with closing(urllib2.urlopen('http://blah')) as sf:
    search_soup = BeautifulSoup.BeautifulStoneSoup(sf.read())

或者,如果您想避免使用with语句:

try:
    sf = None
    sf = urllib2.urlopen('http://blah')
    search_soup = BeautifulSoup.BeautifulStoneSoup(sf.read())
finally:
    if sf:
        sf.close()

然而并不太优雅。


8
finally:
    if sf: sf.close()

7

为什么不尝试关闭sf,并在不存在时跳过呢?

import urllib2
try:
    search_query = 'http://blah'
    sf = urllib2.urlopen(search_query)
    search_soup = BeautifulSoup.BeautifulStoneSoup(sf.read())
except urllib2.URLError, err:
    print(err.reason)
finally:
    try:
        sf.close()
    except NameError: 
        pass

我知道我错过了一些显而易见的东西,嵌套的try语句!你的解决方案非常优雅,谢谢! - fearoffours

1

1
如果urlopen()出现异常,请捕获并调用异常的close()函数,如下所示:
try:
    req = urllib2.urlopen(url)
    req.close()
    print 'request {0} ok'.format(url)
except urllib2.HTTPError, e:
    e.close()
    print 'request {0} failed, http code: {1}'.format(url, e.code)
except urllib2.URLError, e:
    print 'request {0} error, error reason: {1}'.format(url, e.reason)

异常也是一个完整的响应对象,你可以在这个问题信息中看到:http://bugs.jython.org/issue1544


0

为什么不直接使用多个try/except块呢?

try:
    # send the query request
    sf = urllib2.urlopen(search_query)
except urllib2.URLError as url_error:
    sys.stderr.write("Error requesting url: %s\n" % (search_query,))
    raise

try:
    search_soup = BeautifulSoup.BeautifulStoneSoup(sf.read())
except Exception, err: # Maybe catch more specific Exceptions here
    sys.stderr.write("Couldn't get programme information from url: %s\n" % (search_query,))
    raise # or return as in your original code
finally:
    sf.close()

0

你可以创建自己的通用URL打开器:

from contextlib import contextmanager

@contextmanager
def urlopener(inURL):
    """Open a URL and yield the fileHandle then close the connection when leaving the 'with' clause."""
    fileHandle = urllib2.urlopen(inURL)
    try:     yield fileHandle
    finally: fileHandle.close()

然后您可以使用您原始问题中的语法:

with urlopener(theURL) as sf:
    search_soup = BeautifulSoup.BeautifulSoup(sf.read())

这个解决方案为您提供了一个干净的关注点分离。您将获得一个干净的通用urlopener语法,它处理正确关闭资源的复杂性,而不管在with子句下发生的错误。


0

看起来问题比我想象的更深入 - 这个论坛帖子 表明 urllib2 在 Python 2.6 之后才实现了 with,可能直到 3.1 才有。


这是真的。我正在使用Python 2.7(在2013年进行评论),它似乎不起作用,所以我正在用Python 2重写最初为Python 3编写的东西,并被迫重写所有我的“with urllib.urlopen(source) as f:”语句。 - erewok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接