Python使用FancyURLopener时,遇到401和“Connection: close”的问题

2 投票
1 回答
3398 浏览
提问于 2025-04-15 20:01

我刚开始学Python,所以如果我漏掉了什么明显的东西,请多包涵。

我正在使用urllib.FancyURLopener来获取一个网页文档。当网页服务器没有开启身份验证时,这个方法很好用,但一旦开启身份验证就不行了。

我猜我需要创建一个新的类来继承urllib.FancyURLopener,并重写get_user_passwd()和/或prompt_user_passwd()这两个方法。所以我这样做了:

class my_opener (urllib.FancyURLopener):

    # Redefine
    def get_user_passwd(self, host, realm, clear_cache=0):
        print "get_user_passwd() called; host %s, realm %s" % (host, realm)
        return ('name', 'password')

然后我尝试打开这个页面:

try:
    opener = my_opener()
    f = opener.open ('http://1.2.3.4/whatever.html')
    content = f.read()
    print "Got it:  ", content

except IOError:
    print "Failed!"

我希望FancyURLopener能处理401错误,调用我的get_user_passwd()方法,然后重试请求。

但它没有这样做;当我调用“f = opener.open()”时,我得到了IOError异常。

Wireshark告诉我请求已经发送,服务器返回了“401 Unauthorized”的响应,并且有两个我需要关注的头信息:

WWW-Authenticate: BASIC
Connection: close

连接随后被关闭,我捕获了这个异常,一切都结束了。

即使在IOError之后我重试“f = opener.open()”,也还是失败。

我已经通过重写http_error_401()方法并简单地打印“Got 401 error”来验证我的my_opener()类是有效的。我也尝试重写prompt_user_passwd()方法,但也没有发生。

我看不到任何主动指定用户名和密码的方法。

那么,我该如何让urllib重试这个请求呢?

谢谢。

1 个回答

0

我刚在我的网络服务器(nginx)上试了你的代码,结果一切正常:

  • 从urllib客户端获取数据
  • 服务器返回了HTTP/1.1 401 未授权的状态,并附带了一些头信息

    Connection: close
    WWW-Authenticate: Basic realm="Restricted"
    
  • 客户端再次尝试,并添加了授权头信息

    Authorization: Basic <Base64encoded credentials>
    
  • 服务器回应了200 OK,并返回了内容

所以我猜你的代码是对的(我用的是python 2.7.1),可能你尝试访问的网络服务器没有正常工作。这里是我在免费的http基本认证测试网站browserspy.dk上测试的代码(看起来他们使用的是apache - 代码运行正常):

import urllib

class my_opener (urllib.FancyURLopener):

    # Redefine
    def get_user_passwd(self, host, realm, clear_cache=0):
        print "get_user_passwd() called; host %s, realm %s" % (host, realm)
        return ('test', 'test')

try:
    opener = my_opener()
    f = opener.open ('http://browserspy.dk/password-ok.php')
    content = f.read()
    print "Got it:  ", content

except IOError:
    print "Failed!"

撰写回答