【问题标题】:"WindowsError: [Error 5] Access is denied" using urllib2“WindowsError:[错误 5] 访问被拒绝”使用 urllib2
【发布时间】:2015-10-21 16:24:19
【问题描述】:

我在使用 urllib2 读取网站时收到“WindowsError:[错误 5] 访问被拒绝”消息。

from urllib2 import urlopen, Request
from bs4 import BeautifulSoup

hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11'}
req = Request('https://' + url, headers=hdr)
soup = BeautifulSoup( urlopen( req ).read() )

完整的回溯是:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\urllib2.py", line 154, in urlopen
    return opener.open(url, data, timeout)
  File "C:\Python27\lib\urllib2.py", line 431, in open
    response = self._open(req, data)
  File "C:\Python27\lib\urllib2.py", line 449, in _open
    '_open', req)
  File "C:\Python27\lib\urllib2.py", line 409, in _call_chain
    result = func(*args)
  File "C:\Python27\lib\urllib2.py", line 1240, in https_open
    context=self._context)
  File "C:\Python27\lib\urllib2.py", line 1166, in do_open
    h = http_class(host, timeout=req.timeout, **http_conn_args)
  File "C:\Python27\lib\httplib.py", line 1258, in __init__
    context = ssl._create_default_https_context()
  File "C:\Python27\lib\ssl.py", line 440, in create_default_context
    context.load_default_certs(purpose)
  File "C:\Python27\lib\ssl.py", line 391, in load_default_certs
    self._load_windows_store_certs(storename, purpose)
  File "C:\Python27\lib\ssl.py", line 378, in _load_windows_store_certs
    for cert, encoding, trust in enum_certificates(storename):
WindowsError: [Error 5] Access is denied

按照here 的建议,我已尝试从具有管理员权限的命令提示符运行脚本,但它并不能解决问题。

关于如何解决此错误的任何建议?

【问题讨论】:

  • 您是否正在运行 web2py 的源版本,并且您是否能够在 web2py 上下文之外发出相同的请求(例如,来自标准 Python shell 或 Python 脚本)?
  • 从外观上看,Windows 拒绝访问证书存储区。
  • 你可以尝试安装certifi吗?
  • @Boa - 你能确认这个 sn-p 触发了你的问题吗? from _ssl import enum_certificates; print enum_certificates("ROOT"); print enum_certificates("CA")。如果有,是哪一个,还是两者都有?
  • 所以,Windows CA -certification authority 存储 (certmgr:Intermediate Certification Authorities) 出现问题,导致 WINAPI::CertOpenSystemStoreCA 失败。我建议启动certmgr,导航到Intermediate Certification Authorities 并浏览该证书列表或与微软支持部门联系,因为那里似乎有什么问题。无论如何,可以检查print enum_certificates(u"CA")是否也引发错误。

标签: python windows urllib2


【解决方案1】:

看起来这是 Windows 证书存储不一致。 httplib - 由 urllib2 内部调用 - 最近从无服务器证书验证更改为默认强制服务器证书验证。因此,您将在任何基于urllibhttplib 并在您的用户配置文件中运行的python 脚本中遇到此问题。

也就是说,您的 Windows 证书存储似乎有问题。 httplib 在尝试为命名证书存储 CA certification authority 枚举证书时失败(在 certmgr.msc 中显示为 Intermediate Certification Authorities),但 ROOT 成功,这是正常的受信任根证书存储(请参阅cmets 质疑)。因此,我建议检查 certmgr:intermediate certificate authorities 中的所有证书以获取最近添加的证书和/或 Windows 日志中的一般错误。 在您的情况下发生的事情是urllib2 在内部调用httplib,然后尝试设置默认的ssl 上下文并强制执行证书验证,作为其中的一部分,它通过调用ssl.enum_certificates 枚举系统的受信任证书锚点.此函数is implementedC 中作为_ssl_enum_certificates_impl 并在内部调用WINAPIs CertOpenSystemStoreCertEnumCertificatesInStore。对于证书存储位置 CA,它只是在两个 winapi 调用之一中失败,访问被拒绝。

如果你想进一步调试这个你也可以尝试manually invokeWINAPI:CertOpenSystemStoreLPTCSTR::'CA'作为参数并尝试从这里调试它,尝试其他windows certstore管理工具和/或致电微软支持寻求帮助。

还有迹象表明其他人在接口该 api 调用时遇到了类似的问题,请参阅 google:access denied CertOpenSystemStore

如果您只是想让它工作而不修复根本原因,您可以尝试使用以下解决方法临时修补 _windows_cert_stores 以不包括损坏的 CA 证书存储或完全禁用信任锚加载逻辑。 (所有其他ssl.SSLContext 调用将在当前进程中进行修补)

请注意,这实际上会禁用服务器证书验证。

ssl.SSLContext._windows_cert_stores = ("ROOT",)         # patch windows_cert_stores default to only include "ROOT" as "CA" is broken for you.
#ssl.SSLContext.load_default_certs = lambda s,x:None    # alternative, fully NOP load_default_certs to do nothing instead.
ctx = ssl.create_default_context()                      # create new sslcontext, not veryfing any certificates, hostnames.
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE                         

hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11'}
req = Request('https://' + url, headers=hdr)
x = urlopen( req , context=ctx).read() 
ssl.SSLContext._windows_cert_stores = ("ROOT","CA")   # UNDO PATCH

我希望这些信息能帮助您解决问题。祝你好运。

【讨论】:

  • 感谢您的详尽解释!解决方法也有效。
【解决方案2】:

使用 Windows 证书存储存在几个潜在问题。 (我发现在没有完整用户配置文件的情况下从服务帐户运行代码的情况,这几乎是不可能的)。原因有些复杂,但不值得进一步讨论,因为有一个更简单的解决方案。如前所述,关闭 SSL 验证是一种解决方法,但如果您关心所提供证书的有效性,可能不是最好的。

只需使用独立的证书存储即可完全避免这种情况。对于 Python,这是 certifi 包,它会保持最新状态。这很容易从 python requests 包中访问。对于大多数常见的 python 发行版,两者都应该很容易访问

import requests
from bs4 import BeautifulSoup

url = "www.google.com"
hdr = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11'}

r = requests.get('https://' + url, headers=hdr, verify=True)
soup = BeautifulSoup(r.text)

请注意,requests.get() 将在无效地址、无法访问的站点和证书验证失败时引发异常。所以你要准备好抓住这些。当一个站点被成功联系并验证了证书,但没有找到页面(例如 404 错误)时,您将不会收到异常。因此,您还应该在发出请求后检查 r.status_code==200。 (自动处理 30x 重定向,因此除非您告诉它不要遵循它们,否则您不会将它们视为状态代码。)为清楚起见,示例代码中省略了此检查。

另请注意,您没有在此处明确引用 certifi 模块。 requests 将在安装后使用它。如果未安装,requests 将使用更有限的内置根 CA 集。

【讨论】:

  • 谢谢,wojtow。事实上,我最终转向使用certifi 的解决方案(主要是出于好奇的原因询问绕过验证的方法)。在某些时候,I was interested in whether its possible to use certifi with urllib2,但经过一些研究,requests 似乎是urllib 的更实用的替代品。
  • 哈,我只是在那里发布了基本相同的答案,没有意识到这是同一个提问者。虽然答案是相同的,但这两个问题的起点足够独特,不会重复。只是想确保其他人不必像我最初那样经历几天的头部撞击(显然你也这样做了)。
  • @wojwow - 我很幸运能够使用 selenium(在切换到 certifi 变体之前),所以我跳过了headbanging,但这并不是在所有情况下都必须使用的选项,并带有许多缺点(即硒比替代品慢;需要启动烦人的浏览器)也可能导致很多挫败感,所以如果可以选择,我会推荐 [requests/curl/whatever]/证书变体。方便的解决方案,所以我会给它投票,但不能将其标记为正确,因为它没有使用 urllib2,正如原始问题所问的那样。
  • @Boa,赢不了。最近因为坚持原始发帖人选择的函数调用和有关它的具体问题而被否决,而不是建议一个公认的更好的选择。
  • 好吧,我确实给了你赞成票,如果它不是完全胜利,至少否定反对票!
猜你喜欢
  • 2011-03-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-03
  • 2016-10-16
  • 1970-01-01
  • 2017-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多