【问题标题】:Switching from urllib2 to requests, strangely different results with the same parameters从urllib2切换到requests,参数相同的结果奇怪的不同
【发布时间】:2013-09-06 18:44:59
【问题描述】:

我正在尝试从 POST 请求中获取 cookie。以前,我使用 urllib2,它仍然可以正常工作,但我想切换到更清晰的库 python-requests。不幸的是,我在页面上收到错误。

由于请求是 HTTPS,我无法通过嗅探它们来定位差异。

urllib2 代码:

NINTENDO_LOGIN_PAGE = "https://id.nintendo.net/oauth/authorize/"
MIIVERSE_CALLBACK_URL = "https://miiverse.nintendo.net/auth/callback"
parameters = {'client_id': 'ead88d8d450f40ada5682060a8885ec0',
              'response_type': 'code',
              'redirect_uri': MIIVERSE_CALLBACK_URL,
              'username': MIIVERSE_USERNAME,
              'password': miiverse_password}

data = urlencode(parameters)
self.logger.debug(data)
req = urllib2.Request(NINTENDO_LOGIN_PAGE, data)
page = urllib2.urlopen(req).read()
self.logger.debug(page)

结果(良好):

[...]
<div id="main-body">
    <div id="try-miiverse">
        <p class="try-miiverse-catch">A glimpse at some of the posts that are currently popular on Miiverse.</p>
        <h2 class="headline">Miiverse Sampler</h2>
        <div id="slide-post-container" class="list post-list">
        [...]

请求代码:

req = requests.post(NINTENDO_LOGIN_PAGE, data=parameters)
self.logger.debug(req.text)

结果(差):

[...]
<div id="main-body">
    <h2 class="headline">Activity Feed</h2>

    <div class="activity-feed content-loading-window">
        <div>
            <img src="https://d13ph7xrk1ee39.cloudfront.net/img/loading-image-green.gif" alt=""></img>
            <p class="tleft"><span>Loading activity feed...</span></p>
        </div>
    </div>
    <div class="activity-feed content-load-error-window none"><div>
    <p>The activity feed could not be loaded. Check your Internet connection, wait a moment and then try reloading.</p>
    <div class="buttons-content"><a href="/" class="button">Reload</a></div>
    </div>
</div>
[...]

提前感谢您提供解决此问题的任何提示。

更新 1:感谢大家的回复!

按照@abarnert 的建议,我检查了重定向。

resp = urllib2.urlopen(req)
print(resp.geturl()) # https://miiverse.nintendo.net/

req = requests.post(NINTENDO_LOGIN_PAGE, data=parameters)
print(req.url) # https://miiverse.nintendo.net/
print(req.history) # (<Response [303]>, <Response [302]>)

似乎他们都遵循了重定向,但最终都在同一个地方。

@sigmavirus24,非常有用的网站,感谢您让我发现它。以下是结果(我编辑了参数的顺序以便它们易于比较):

urllib2:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "client_id": "ead88d8d450f40ada5682060a8885ec0",
    "response_type": "code",
    "redirect_uri": "https://miiverse.nintendo.net/auth/callback",
    "username": "Wiwiweb",
    "password": "password"
  },
  "headers": {
    "Accept-Encoding": "identity",
    "Connection": "close",
    "Content-Length": "170",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "Python-urllib/2.7"
  },
  "json": null,      
  "origin": "24.85.129.188",
  "url": "http://httpbin.org/post"
}

请求:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "client_id": "ead88d8d450f40ada5682060a8885ec0",
    "response_type": "code",
    "redirect_uri": "https://miiverse.nintendo.net/auth/callback",
    "username": "Wiwiweb",
    "password": "password"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate, compress",
    "Connection": "close",
    "Content-Length": "170",
    "Content-Type": "application/x-www-form-urlencoded"
    "Host": "httpbin.org",
    "User-Agent": "python-requests/1.2.3 CPython/2.7.5 Windows/7"
  },
  "json": null,
  "origin": "24.85.129.188"
  "url": "http://httpbin.org/post",
}

看起来有些标题略有不同。我没有任何其他想法,所以我不妨尝试完全复制 urllib2 标头。可能是欺骗用户代理。

更新 2: 我已将这些标头添加到“请求”请求中:

headers = {'User-Agent': 'Python-urllib/2.7',
           'Accept-Encoding': 'identity'}

我仍然得到相同的结果......现在请求之间的唯一区别是“请求”有一个额外的标题:"Accept": "*/*"。我不确定这是不是问题。

它可能来自重定向吗?

【问题讨论】:

  • 看来您的urllib2 代码正在发出GET 请求,对吧?
  • @alecxe:不,正如the docs 所说,“......当提供 data 参数时,HTTP 请求将是 POST 而不是 GET。”
  • 第一步可能是收集每个库发送的确切请求(例如,nc -l 8000 &gt; output.txt 在单独的终端窗口中,然后将NINTENDO_LOGIN_PAGE 更改为http://localhost:8000,运行第一个脚本,点击^C 到手时,查看 output.txt,重复第二个脚本)。正文和大多数标题应该是相同的(当然,验证“应该”),但其他一些标题可能不同。然后尝试添加/删除标题以使它们相同,看看它们中的任何一个是否有所不同。
  • 每个库发送的用户代理是什么?
  • 如果您对 @abarnert 与 nc 核对的建议感到不舒服,请将这些请求指向 https://httpbin.org/post

标签: python httprequest urllib2 python-requests


【解决方案1】:

好吧,我还没有完全解决“为什么”重定向不同,但我发现使用请求从哪里获取我的 cookie。

我认为这两个库之间的差异与它们处理重定向的方式有关。所以我检查了这两个请求的历史记录。对于像req.history 一样简单的“请求”,但对于 urllib2,我使用了这段代码:

class MyHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
    def http_error_302(self, req, fp, code, msg, headers):
        print("New request:")
        print(headers)
        return urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)

opener = urllib2.build_opener(MyHTTPRedirectHandler, urllib2.HTTPCookieProcessor())
urllib2.install_opener(opener)

检查历史记录让我看到“requests”请求在第一次重定向期间具有“set-cookie”标头(因此是三个请求中的第二个),但最后没有。这对我来说已经足够好了,因为我现在知道从哪里得到它:req.history[1].cookies['ms']

奇怪的是,由于我添加到 urllib2 请求中的那一点,它开始返回与“请求”请求相同的内容!甚至将其更改为:

class MyHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
    pass

opener = urllib2.build_opener(MyHTTPRedirectHandler, urllib2.HTTPCookieProcessor())
urllib2.install_opener(opener)

足以让它完全改变它对一直返回的相同“请求”的响应(我在问题中标记为“坏”的那一点)。

我很难过,但知道在哪里可以找到 cookie 对我来说已经足够了。也许好奇和无聊的人会有兴趣尝试找出原因。

谢谢大家的帮助:)

【讨论】:

    猜你喜欢
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    • 2014-03-24
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多