如何用requests实现urllib中HTTPPasswordMgrWithDefaultRealm的认证下载?
问题描述
已通过urllib实现NASA MODIS HDF文件的认证下载(示例文件来自https://e4ftl01.cr.usgs.gov/MOLT/MOD09GQ.061/2000.05.14),urllib实现代码如下:
url = "https://e4ftl01.cr.usgs.gov/MOLT/MOD09GQ.061/2000.05.14/MOD09GQ.A2000135.h20v02.061.2020042041314.hdf" password_manager = urllib.request.HTTPPasswordMgrWithDefaultRealm() password_manager.add_password(None, "https://urs.earthdata.nasa.gov", username, password) CJ = cookielib.CookieJar() opener = urllib.request.build_opener( urllib.request.HTTPBasicAuthHandler(password_manager), urllib.request.HTTPCookieProcessor(CJ)) urllib.request.install_opener(opener) req = urllib.request.Request(url) with urllib.request.urlopen(req) as resp: with open("im.hdf", "wb") as _fb: _fb.write(resp.read())
尝试改用requests实现时,三种基础认证方式均返回401错误:
- 会话中传递auth元组:
with requests.Session() as s: _auth = (username, password) with s.get(url, auth=_auth) as resp: print(resp.status_code) print(resp.headers)
- 使用HTTPBasicAuth:
with requests.Session() as s: _auth = HTTPBasicAuth(username, password) with s.get(url, auth=_auth) as resp: print(resp.status_code) print(resp.headers)
- 为会话设置auth属性:
with requests.Session() as s: s.auth = (username, password) with s.get(url) as resp: print(resp.status_code) print(resp.headers)
错误响应头包含WWW-Authenticate: Basic realm="Please enter your Earthdata Login credentials. If you do not have a Earthdata Login, create one at https://urs.earthdata.nasa.gov//users/new",响应内容为b'HTTP Basic: Access denied.'。
环境:Python 3.8.10(Linux WSL)、requests2.28.1、requests-oauthlib1.3.1
解决方案
Earthdata的认证机制是跨域名Cookie认证:文件服务器会重定向到URS(Earthdata Login服务器)完成认证,认证通过后URS会设置会话Cookie,后续访问文件服务器时需要携带该Cookie。直接给文件URL附加Basic Auth会失败,因为文件服务器不处理认证逻辑,只会引导到URS。
以下是等效的requests实现代码:
import requests # 目标文件URL file_url = "https://e4ftl01.cr.usgs.gov/MOLT/MOD09GQ.061/2000.05.14/MOD09GQ.A2000135.h20v02.061.2020042041314.hdf" # 替换为你的Earthdata账号信息 username = "your_earthdata_username" password = "your_earthdata_password" # 创建会话对象,自动管理Cookie session = requests.Session() # 先向URS服务器发起认证,获取认证Cookie session.auth = (username, password) # 发送HEAD请求触发认证流程,会话会自动保存返回的Cookie session.head("https://urs.earthdata.nasa.gov") # 使用带认证Cookie的会话下载文件,stream=True适合大文件 with session.get(file_url, stream=True) as response: # 检查请求是否成功,失败则抛出异常 response.raise_for_status() # 分块写入文件,避免内存溢出 with open("im.hdf", "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk)
核心要点:
- 必须先向
https://urs.earthdata.nasa.gov发起认证,让会话保存认证后的Cookie,才能访问文件服务器。 stream=True和iter_content用于分块下载大文件,避免一次性加载整个文件到内存。- 会话对象会自动处理重定向和Cookie的保存/携带,无需手动管理。
内容的提问来源于stack exchange,提问作者GionniD
相关产品推荐
相关产品推荐

