我一直在使用亚马逊的产品广告 API 来生成包含给定书籍价格的网址。我生成的一个网址如下:
当我单击链接或将链接粘贴到地址栏上时,网页加载正常。但是,当我执行以下代码时,出现错误:
url = "http://www.amazon.com/gp/offer-listing/0415376327%3FSubscriptionId%3DAKIAJZY2VTI5JQ66K7QQ%26tag%3Damaztest04-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0415376327"
html_contents = urllib2.urlopen(url)
错误是 urllib2.HTTPError:HTTP 错误 503:服务不可用。首先,我不明白为什么网页加载成功后会出现此错误。
另外,我注意到的另一个奇怪的行为是以下代码有时会给出所述错误,有时不会给出所述错误:
html_contents = urllib2.urlopen("http://www.amazon.com/gp/offer-listing/0415376327%3FSubscriptionId%3DAKIAJZY2VTI5JQ66K7QQ%26tag%3Damaztest04-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0415376327")
我完全不知道这种行为是如何发生的。对此有任何修复或解决方法吗?我的目标是读取 url 的 html 内容。
编辑
我不知道为什么堆栈溢出会更改我的代码,将我上面在代码中列出的亚马逊链接更改为 rads.stackoverflow。无论如何,忽略 rads.stackoverflow 链接并在引号之间使用我上面的链接。
亚马逊拒绝 urllib2 的默认用户代理。一种解决方法是使用请求模块
import requests
page = requests.get("http://www.amazon.com/gp/offer-listing/0415376327%3FSubscriptionId%3DAKIAJZY2VTI5JQ66K7QQ%26tag%3Damaztest04-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0415376327")
html_contents = page.text
如果您坚持使用 urllib2,可以通过伪造标头来实现此目的:
import urllib2
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
response = opener.open('http://www.amazon.com/gp/offer-listing/0415376327%3FSubscriptionId%3DAKIAJZY2VTI5JQ66K7QQ%26tag%3Damaztest04-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0415376327')
html_contents = response.read()
不用担心 stackoverflow 编辑 URL。他们解释说他们正在这里这样做。
这是因为亚马逊不允许自动访问他们的数据,所以他们拒绝您的请求,因为它不是来自正确的浏览器。如果你看一下503回复的内容,它说:
要讨论自动访问亚马逊数据,请联系 [电子邮件受保护]。 有关迁移到我们的 API 的信息,请参阅我们的 Marketplace API:https://developer.amazonservices.com/ref=rm_5_sv, 或我们的产品广告 API: https://affiliate-program.amazon.com/gp/advertising/api/detail/main.html/ref=rm_5_ac 用于广告用例。
这是因为 Python 的
User-Agent
的 urllib
显然不是浏览器。你总是可以伪造 User-Agent
,但这并不是真正好的(或道德)做法。
顺便说一句,正如另一个答案中提到的,
requests
库非常适合Python中的HTTP访问。
本的答案是对OP问题的正确且公认的答案(我猜;我还没有验证它)。
但是,由于这个问题是在谷歌搜索“python 503 urllib2”时第一个出现的问题,并且它没有解决我过去三个小时调查的问题......我将提供替代答案。
如果您不幸遇到 Python 2.6(2023 年!),那么您可能真的运气不好。
TLS 的 SNI 首次在 Python 3 中引入,并向后移植到 Python 2.7(请参阅 https://stackoverflow.com/a/27717544/8280541),但从未添加到 2.6 或更早版本。如今,许多(如果不是大多数)HTTPS 服务器将依赖于 SNI。
长话短说,SNI 允许单个 HTTPS 服务器为多个不同的站点提供服务,并使用可能不同的证书。当客户端通过 HTTPS 连接到服务器时,它所做的第一件事就是向服务器提供其 SNI(服务器名称指示),甚至在它们谈论证书之前。有了这些信息,服务器将能够向客户端提供他们想要的网站内容以及关联的证书。
确认您处于这种情况:
curl
或其他工具从同一主机访问同一地址openssl s_client -connect my.server.url:443 < /dev/null | grep subject
;记下它显示的服务器名称(它应该是您期望的名称)openssl s_client -noservername -connect my.server.url:443 < /dev/null | grep subject
;服务器名称已更改如果是这样的话,Python 2.6 不适合你。也许是时候升级了?如果您仍然坚持使用古老且不受支持的软件,一种替代方法是通过
curl
运行 subprocess.Popen
以获得您想要的东西。