Python - 使用 Python 3 urllib 发送 POST 请求

74

我试图向以下页面发起POST请求:http://search.cpsa.ca/PhysicianSearch

为了模拟点击“搜索”按钮而不填写表单,从而向页面添加数据。我通过在Chrome开发者工具的网络选项卡下查看按钮并点击获取了POST头信息。我发布这篇文章的原因是我认为我可能没有获取正确的头信息,在其他类似问题的解决方案中只是复制粘贴是行不通的。

这个格式是否正确并且我抓取了正确的信息?我以前从未进行过POST请求。

这是我设法整理出来的内容:

import urllib.parse
import urllib.request


data = urllib.parse.urlencode({'Host': 'search.cpsa.ca', 'Connection': 'keep-alive', 'Content-Length': 23796,
                                     'Origin': 'http://search.cpsa.ca', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                                     'Cahce-Control': 'no-cache', 'X-Requested-With': 'XMLHttpRequest',
                                     'X-MicrosoftAjax': 'Delta=true', 'Accept': '*/*',
                                     'Referer': 'http://search.cpsa.ca/PhysicianSearch',
                                     'Accept-Encoding': 'gzip, deflate',
                                     'Accept-Language': 'en-GB,en-US;q=0.8,en;q=0.6',
                                     'Cookie': 'ASP.NET_SessionId=kcwsgio3dchqjmyjtwue402c; _ga=GA1.2.412607756.1459536682; _gat=1'})


url = "http://www.musi-cal.com/cgi-bin/query?%s"

data = data.encode('ascii')
with urllib.request.urlopen("http://search.cpsa.ca/PhysicianSearch", data) as f:
    print(f.read().decode('utf-8'))

这个解决方案输出了页面的HTML,但没有任何我想从POST请求中检索的数据。


POST数据应该对应于您提交的表单的输入元素的名称属性和值。您可以通过检查表单的HTML来获取名称属性。您正在使用请求头作为POST数据 - 这样做不起作用。此外,考虑使用requests包(http://docs.python-requests.org/en/master/),它比urllib2更友好。 - snakecharmerb
我应该如何指定表单?还是只需要指定数据的键值对? - Daniel Paczuski Bak
在 Chrome 中,像之前一样查看网络选项卡中的 POST 请求,并转到标题选项卡的底部 - 在那里,您将看到名称和值,无论是 POST 请求还是带有查询参数的 GET 请求。 - snakecharmerb
我已经这样做了,发生了POST请求,但实际上它并没有抓取页面。这就是requests.text给我的内容:1 |#|| 4 | 50 | pageRedirect ||%2fError.aspx%3faspxerrorpath%3d%2fPhysicianSearch| - Daniel Paczuski Bak
5个回答

134

这就是你如何做到它.

from urllib import request, parse
data = parse.urlencode(<your data dict>).encode()
req =  request.Request(<your url>, data=data) # this will make the method "POST"
resp = request.urlopen(req)

4
如果我想用空的请求体发起POST请求怎么办? - Oleg Yablokov
3
request.Request(..., method='POST')。https://docs.python.org/3/library/urllib.request.html#urllib.request.Request.method - Anupam Ghosh
1
推荐的做法是使用 with request.urlopen(req) as resp: 并在该块中处理 resp - mVChr
谢谢您,先生。您节省了我的时间。您的代码在Python3上运行完美。 - Ganesan J
那么你的意思是问题在于试图直接将URL和数据传递给urllib.request.urlopen()吗?文档在哪里说这样做是错误的? - root
@c熊猫,当我使用这段代码向我的API发送POST请求时,502错误网关错误的原因可能是什么? - punky

36

谢谢 C Panda。你真的让我很容易学会了这个模块。

我发现我们传递的字典没有为我编码。我必须做一个小改变 -

from urllib import request, parse
import json

# Data dict
data = { 'test1': 10, 'test2': 20 }

# Dict to Json
# Difference is { "test":10, "test2":20 }
data = json.dumps(data)

# Convert to String
data = str(data)

# Convert string to byte
data = data.encode('utf-8')

# Post Method is invoked if data != None
req =  request.Request(<your url>, data=data)

# Response
resp = request.urlopen(req)

10
应该真正是一条评论。 - hd1
7
如果服务对接受的内容类型有严格要求且为JSON格式,则还需要添加以下内容:req.add_header('Content-Type', 'application/json')。参考链接:https://dev59.com/wGkw5IYBdhLWcg3woMJD#9746432。 - D_K
12
您可以将您对 data 进行的三个操作合并为一个命令:request.urlopen(url='您的URL', data=bytes(json.dumps(dict_obj), encoding='utf-8')) - Chen A.

16

request.Request() 中设置 method="POST"


发送一个没有请求体的 POST 请求:

from urllib import request

req = request.Request('https://postman-echo.com/post', method="POST")
r = request.urlopen(req)
content = r.read()
print(content)
发送带有JSON主体的POST请求:
from urllib import request
import json

req = request.Request('https://postman-echo.com/post', method="POST")
req.add_header('Content-Type', 'application/json')
data = {
    "hello": "world"
}
data = json.dumps(data)
data = data.encode()
r = request.urlopen(req, data=data)
content = r.read()
print(content)

我的问题在于我在构建request.Request时传递了data关键字参数,例如request.Request(..., data=...),而实际上应该将数据传递给urllib.request.urlopen - bool3max
这个问题应该在顶部。 - urek mazino

13

以上代码在JSON字符串中添加了一些额外的\",这给我带来了很多问题。以下是更好的实现方式:

from urllib import request, parse

url = "http://www.example.com/page"

data = {'test1': 10, 'test2': 20}
data = parse.urlencode(data).encode()

req = request.Request(url, data=data)
response = request.urlopen(req)

print (response.read())

4

当我使用urlencode时,它失败了。因此,我使用以下代码在Python3中进行POST调用:

from urllib import request, parse

data = b'{"parameter1": "test1", "parameter2": "test2"}'
req = request.Request("http://www.musi-cal.com/cgi-bin/query?%s", data)
resp = request.urlopen(req).read().decode('utf-8')
print(resp)

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