Python Requests模块实现HTTP Digest/Basic身份验证

10
我的目标是能够从受密码保护的页面解析HTML/XML数据,然后基于该数据(时间戳),向另一个设备发送XML命令。我要访问的页面是由IP设备生成的Web服务器。
如果有其他编程语言更容易实现这个目标,请告诉我。我几乎没有编程经验(只学过一门C编程课)。
我尝试使用请求进行基本和摘要身份验证。但我仍无法得到认证,这阻止了我进一步操作。
以下是我的尝试:
import requests
from requests.auth import HTTPDigestAuth

url='http://myUsername:myPassword@example.com/cgi/metadata.cgi?template=html'
r = requests.get(url, auth=HTTPDigestAuth('myUsername', 'myPassword'))        
r.status_code

print(r.headers) 
print(r.status_code)

输出:

401 
CaseInsensitiveDict({'Content-Length': '0', 'WWW-Authenticate': 'Digest realm="the realm of device", nonce="23cde09025c589f05f153b81306928c8", qop="auth"', 'Server': 'Device server name'})

我还尝试了使用Requests库的BasicAuth方法,但输出结果相同。我已经尝试了将user:pass@包含在URL中和不包含在URL中两种方式。虽然当我在浏览器中输入这个URL时它可以正常工作。

我原以为Requests库可以处理Digest/BasicAuth的头部数据,但也许我还需要包含头部信息吗?

我使用了Live HTTP Headers(火狐浏览器插件)并获得了以下信息:

GET /cgi/metadata.cgi?template=html
HTTP/1.1 
Host: [Device IP] 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language: en-US,en;q=0.5 
Accept-Encoding: gzip, deflate DNT: 1 Connection: keep-alive
HTTP/1.1 401 Unauthorized WWW-Authenticate: Digest realm="Device Realm", nonce="a2333eec4cce86f78016343c48382d21", 
qop="auth" 
Server: Device Server Content-Length: 0

你确定你的代码包含两个 request.get() (第二个不发送任何身份验证信息)吗?请提供你实际尝试过的代码(你可以将设备IP替换为example.com,使用等效符号集更改用户名/密码(如果你的密码使用空格,则包括它,如果其中包含"则包括引号等)。 - jfs
这是我尝试过的确切代码(除了在URL中“digest-”和“auth”之间的空格,格式在复制时混乱了。我也尝试过不带URL末尾部分(digest-auth/auth/user/pass/)。我包含它只是因为在Requests文档中的示例中显示了这个。我已经用实际凭据替换了/user/pass/,我觉得我缺少了一步骤?这需要是.post而不是get吗?我也尝试过,但没有成功。 - user3566107
2个回答

9
这两个请求是独立的:
r = requests.get(url, auth=HTTPDigestAuth('user', 'pass')) 
response = requests.get(url) #XXX <-- DROP IT

第二个请求没有发送任何凭据。因此,它收到了“401未经授权”的http响应状态是不足为奇的。
要解决这个问题:
  1. 使用与浏览器中使用的相同的url。在末尾删除digest-auth/auth/user/pass,这只是requests文档中的一个示例。
  2. 打印r.status_code而不是response.status_code以查看是否成功。

为什么要在url和auth参数中都使用用户名/密码?从url中删除用户名/密码即可。要查看发送的请求和响应标头,可以启用日志记录/调试。请参阅:启用日志记录/调试
import logging
import requests
from requests.auth import HTTPDigestAuth

# these two lines enable debugging at httplib level (requests->urllib3->httplib)
# you will see the REQUEST, including HEADERS and DATA, 
# and RESPONSE with HEADERS but without DATA.
# the only thing missing will be the response.body which is not logged.
try:
    import httplib
except ImportError:
    import http.client as httplib

httplib.HTTPConnection.debuglevel = 1

logging.basicConfig(level=logging.DEBUG) # you need to initialize logging, 
                      # otherwise you will not see anything from requests

# make request
url = 'https://example.com/cgi/metadata.cgi?template=html'
r = requests.get(url, auth=HTTPDigestAuth('myUsername', 'myPassword'),
                 timeout=10)
print(r.status_code)
print(r.headers)

我已经摆脱了第二个请求和URL末尾的额外部分。现在当我运行代码时,它似乎只是无限期地在shell中运行,它从来没有到达print()行。我已经更新了代码以显示我现在正在尝试什么。我也再次尝试了带和不带user:pass@的方式。 - user3566107
如果程序卡住了,请启用日志记录以查看程序停在哪里。 - jfs

4
import requests
from requests.auth import HTTPDigestAuth

url='https://example.com/cgi/metadata.cgi?template=html'
r = requests.get(url, auth=HTTPDigestAuth('myUsername', 'myPassword'), verify=False,  stream=True)        


print(r.headers) 
print(r.status_code)

由于页面正在传输xml/html数据,因此需要添加stream=True来修复。我的下一个问题是,如何存储/解析不断流动的数据?

我尝试将其存储在r.content中,但似乎会无限运行(与之前遇到的相同问题)。


简单而不失优美的例子。 - barrypicker

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