POST 两个名称相同但类型不同的表单输入
POST two form inputs with same name but different type
我有一个网页在一个表单中提交两个输入。下面可以看到我浏览器的POST
第一个输入是当前上传到站点的文件的路径(type="hidden"
);第二个输入是要上传的新文件 (type="file"
).
-----------------------------334620328614915644833547167693
Content-Disposition: form-data; name="image1"
data/file.jpg
-----------------------------334620328614915644833547167693
Content-Disposition: form-data; name="image1"; filename=""
Content-Type: application/octet-stream
这些输入名称相同但类型不同,这导致我对 requests
库的理解出现问题。据我所知,要提交数据,您 POST 一个包含输入名称及其新值的字典。
data = {}
form = get_all_forms(url)[1] #function returns list of forms gained using requests.get() - we select the correct form
formDetails = get_form_details(form) #uses BeautifulSoup to find all input values
for inputTag in formDetails["inputs"]:
if inputTag["type"] == "file":
pass #doesn't send anything
else:
data[inputTag["name"]] = inputTag["value"] #sets to current value
res = requests.post(url, data=data)
上面的代码将 POST 一个带有 'image1':'data/file.jpg'
的字典。不幸的是,该页面现在认为正在上传一个名为 data/file.jpg
的新文件,当找不到该文件时,它会删除当前图像。
如何 POST 分离 type="file"
和 type="hidden"
的值?
首先,只是一个建议:考虑为您的表单字段使用不同的、更重要的 name
属性(强调不同!)。在你的情况下,这样的事情会更有意义:
<input type="hidden" name="old-file-path" value="data/file.jpg"/>
<input type="file" name="new-file"/>
现在,进入实际问题,您可以使用 requests.post()
的 data=
和 files=
参数来实现您想要的,如下所示:
new_file = open('path/to/new/file.jpg', 'wb')
data = {'image1': 'data/file.jpg'}
files = {'image1': new_file}
requests.post(url, data=data, files=files)
应该生成这样的请求:
...
Content-Type: multipart/form-data; boundary=d64ebc3e14a4909699c6f01dd1473855
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"
data/file.jpg
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"; filename="file.jpg"
...
这里的重点是files=
用于上传新文件,data=
用于旧文件名。
如果您还想显式设置新文件的名称和 Content-Type
,您可以在 files=
字典中传递一个 3 项元组,如下所示:
new_file = open('path/to/new/file.jpg', 'wb')
data = {'image1': 'data/file.jpg'}
files = {'image1': ('NEW_FILE_NAME', new_file, 'application/octet-stream')}
requests.post(url, data=data, files=files)
结果:
...
Content-Type: multipart/form-data; boundary=d64ebc3e14a4909699c6f01dd1473855
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"
data/file.jpg
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"; filename="NEW_FILE_NAME"
Content-Type: application/octet-stream
...
或者如果你想从内存而不是磁盘发送一个文件,甚至是一个空文件,你可以使用:
from io import BytesIO
new_file = BytesIO(b'...file content here...')
# ... same code as above
将其适应您现有的代码,整个事情应该变成:
data = {}
files = {}
new_file = ... # obtain the new file somehow
for inputTag in formDetails["inputs"]:
if inputTag["type"] == "file":
files[inputTag["name"]] = ("NEW_FILE_NAME", new_file, "application/octect-stream")
elif inputTag["type"] == "hidden":
data[inputTag["name"]] = inputTag["value"]
requests.post(url, data=data, files=files)
最后,在你的赏金评论中,我看到你说:
[...] and when I just need multiple values for the same input name.
在这种情况下,您必须使用 list
而不是 data=
的字典,如下所示:
data = [('image1', 'A'), ('image1', 'B')]
# ...
requests.post(url, data=data)
结果将如下所示:
--9cc0d413be5d2ed2ba8c80a2e7b54442
Content-Disposition: form-data; name="image1"
A
--9cc0d413be5d2ed2ba8c80a2e7b54442
Content-Disposition: form-data; name="image1"
B
这是否有意义取决于您的决定,因为它取决于服务器端的实现以及多次使用相同名称时字段的含义。
我有一个网页在一个表单中提交两个输入。下面可以看到我浏览器的POST
第一个输入是当前上传到站点的文件的路径(type="hidden"
);第二个输入是要上传的新文件 (type="file"
).
-----------------------------334620328614915644833547167693
Content-Disposition: form-data; name="image1"
data/file.jpg
-----------------------------334620328614915644833547167693
Content-Disposition: form-data; name="image1"; filename=""
Content-Type: application/octet-stream
这些输入名称相同但类型不同,这导致我对 requests
库的理解出现问题。据我所知,要提交数据,您 POST 一个包含输入名称及其新值的字典。
data = {}
form = get_all_forms(url)[1] #function returns list of forms gained using requests.get() - we select the correct form
formDetails = get_form_details(form) #uses BeautifulSoup to find all input values
for inputTag in formDetails["inputs"]:
if inputTag["type"] == "file":
pass #doesn't send anything
else:
data[inputTag["name"]] = inputTag["value"] #sets to current value
res = requests.post(url, data=data)
上面的代码将 POST 一个带有 'image1':'data/file.jpg'
的字典。不幸的是,该页面现在认为正在上传一个名为 data/file.jpg
的新文件,当找不到该文件时,它会删除当前图像。
如何 POST 分离 type="file"
和 type="hidden"
的值?
首先,只是一个建议:考虑为您的表单字段使用不同的、更重要的 name
属性(强调不同!)。在你的情况下,这样的事情会更有意义:
<input type="hidden" name="old-file-path" value="data/file.jpg"/>
<input type="file" name="new-file"/>
现在,进入实际问题,您可以使用 requests.post()
的 data=
和 files=
参数来实现您想要的,如下所示:
new_file = open('path/to/new/file.jpg', 'wb')
data = {'image1': 'data/file.jpg'}
files = {'image1': new_file}
requests.post(url, data=data, files=files)
应该生成这样的请求:
...
Content-Type: multipart/form-data; boundary=d64ebc3e14a4909699c6f01dd1473855
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"
data/file.jpg
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"; filename="file.jpg"
...
这里的重点是files=
用于上传新文件,data=
用于旧文件名。
如果您还想显式设置新文件的名称和 Content-Type
,您可以在 files=
字典中传递一个 3 项元组,如下所示:
new_file = open('path/to/new/file.jpg', 'wb')
data = {'image1': 'data/file.jpg'}
files = {'image1': ('NEW_FILE_NAME', new_file, 'application/octet-stream')}
requests.post(url, data=data, files=files)
结果:
...
Content-Type: multipart/form-data; boundary=d64ebc3e14a4909699c6f01dd1473855
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"
data/file.jpg
--d64ebc3e14a4909699c6f01dd1473855
Content-Disposition: form-data; name="image1"; filename="NEW_FILE_NAME"
Content-Type: application/octet-stream
...
或者如果你想从内存而不是磁盘发送一个文件,甚至是一个空文件,你可以使用:
from io import BytesIO
new_file = BytesIO(b'...file content here...')
# ... same code as above
将其适应您现有的代码,整个事情应该变成:
data = {}
files = {}
new_file = ... # obtain the new file somehow
for inputTag in formDetails["inputs"]:
if inputTag["type"] == "file":
files[inputTag["name"]] = ("NEW_FILE_NAME", new_file, "application/octect-stream")
elif inputTag["type"] == "hidden":
data[inputTag["name"]] = inputTag["value"]
requests.post(url, data=data, files=files)
最后,在你的赏金评论中,我看到你说:
[...] and when I just need multiple values for the same input name.
在这种情况下,您必须使用 list
而不是 data=
的字典,如下所示:
data = [('image1', 'A'), ('image1', 'B')]
# ...
requests.post(url, data=data)
结果将如下所示:
--9cc0d413be5d2ed2ba8c80a2e7b54442
Content-Disposition: form-data; name="image1"
A
--9cc0d413be5d2ed2ba8c80a2e7b54442
Content-Disposition: form-data; name="image1"
B
这是否有意义取决于您的决定,因为它取决于服务器端的实现以及多次使用相同名称时字段的含义。