我正在尝试使用 Playwright 通过 Python API 来 PUT 多部分表单数据。 这是相关的文档。
但是,尚不清楚如何构造 multipart 参数。 文档说:
multipart: Dict[str, str|float|bool|[ReadStream]|Dict] (optional) name: str File name mimeType: str File type buffer: bytes File content
提供一个对象,该对象将使用 multipart/form-data 编码序列化为 html 表单,并作为此请求正文发送。如果指定此参数,内容类型标头将设置为 multipart/form-data,除非明确提供。文件值可以作为 fs.ReadStream 或作为包含文件名、mime 类型及其内容的类文件对象传递。
因此,我们向它传递一个带有
str
键和 str
、float
、bool
、ReadStream
或 Dict
值的字典。但是我们应该使用什么键呢?另外,如何从 Python 脚本传递 fs.ReadStream
(一个 JavaScript 对象)?看起来按键应该是 'name'
、'mimeType'
和 'buffer'
。但是,使用 nc -l -p 8080
检查生成的 HTTP 请求表明,为多部分字典中的每个键创建了一个新的表单部分。
我本质上是在尝试复制以下
curl
命令:
curl 'localhost:8080' -X PUT --form file='@test.zip;filename=test.zip;type=application/x-zip-compressed'
假设您之前运行过
nc -l -p 8080 | less
,您将看到以下 HTTP 请求:
PUT / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.81.0
Accept: */*
Content-Length: 380
Content-Type: multipart/form-data; boundary=------------------------f3dde3ddff901cd3
--------------------------f3dde3ddff901cd3
Content-Disposition: form-data; name="file"; filename="test.zip"
Content-Type: application/x-zip-compressed
PK^C^D
^@^@^@^@^@<D1>`xW^N<93>ESC<91>
^@^@^@
^@^@^@^H^@^\^@test.txtUT ^@^C^Z<F7>_e^Z<F7>_eux^K^@^A^D<E8>^C^@^@^D<E8>^C^@^@Test file
PK^A^B^^^C
^@^@^@^@^@<D1>`xW^N<93>ESC<91>
^@^@^@
^@^@^@^H^@^X^@^@^@^@^@^A^@^@^@<FF><81>^@^@^@^@test.txtUT^E^@^C^Z<F7>_eux^K^@^A^D<E8>^C^@^@^D<E8>^C^@^@PK^E^F^@^@^@^@^A^@^A^@N^@^@^@L^@^@^@^@^@
--------------------------f3dde3ddff901cd3--
这是使用 Playwright 实现等效 HTTP 请求的失败尝试:
from playwright.sync_api import sync_playwright
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
context = browser.new_context()
page = context.new_page()
upload_file = "test.zip"
with open(upload_file, "rb") as file:
response = page.request.put(
"http://localhost:8080",
multipart={
"name": "file",
"filename": upload_file,
"mimeType": "application/x-zip-compressed",
"buffer": file.read(),
}
)
print(response)
这会产生以下(明显错误的)HTTP 请求:
PUT / HTTP/1.1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/115.0.5790.24 Safari/537.36
accept: */*
accept-encoding: gzip,deflate,br
content-type: multipart/form-data; boundary=----WebKitFormBoundarynU6IBe6rgn1KUgpw
content-length: 365
Host: localhost:8080
Connection: close
------WebKitFormBoundarynU6IBe6rgn1KUgpw
content-disposition: form-data; name="name"
file
------WebKitFormBoundarynU6IBe6rgn1KUgpw
content-disposition: form-data; name="filename"
test.zip
------WebKitFormBoundarynU6IBe6rgn1KUgpw
content-disposition: form-data; name="mimeType"
application/x-zip-compressed
------WebKitFormBoundarynU6IBe6rgn1KUgpw--
如何使用 Playwright 复制curl HTTP 请求?
剧作家文档不是很清楚。
multipart
参数的结构应该是一个字典,每个表单部分都有一个键。在这种情况下,我们只提交一个“部分”,因此应该只有一个密钥。
将所需多部分表单提交的组件映射到 Playwright 多部分字典是有启发性的。例如下面的表单部分:
Content-Disposition: form-data; name="file"; filename="test.zip"
Content-Type: application/x-zip-compressed
映射到以下多部分字典:
multipart={
"file": { # name="file"
"name": "test.zip", # filename="test.zip"
"mimeType": "application/x-zip-compressed", # Content-Type value
"buffer": file.read(), # binary data from file
}
}
所以完整的例子是:
from playwright.sync_api import sync_playwright
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
context = browser.new_context()
page = context.new_page()
upload_file = "test.zip"
with open(upload_file, "rb") as file:
response = page.request.put(
"http://localhost:8080",
multipart={
"file": {
"name": upload_file,
"mimeType": "application/x-zip-compressed",
"buffer": file.read(),
}
},
)
print(response)
产生以下 HTTP 请求:
PUT / HTTP/1.1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/115.0.5790.24 Safari/537.36
accept: */*
accept-encoding: gzip,deflate,br
content-type: multipart/form-data; boundary=----WebKitFormBoundaryK3WyngXmdiXmbBHu
content-length: 376
Host: localhost:8080
Connection: close
------WebKitFormBoundaryK3WyngXmdiXmbBHu
content-disposition: form-data; name="file"; filename="test.zip"
content-type: application/x-zip-compressed
PK^C^D
^@^@^@^@^@<D1>`xW^N<93>ESC<91>
^@^@^@
^@^@^@^H^@^\^@test.txtUT ^@^C^Z<F7>_e^Z<F7>_eux^K^@^A^D<E8>^C^@^@^D<E8>^C^@^@Test file
PK^A^B^^^C
^@^@^@^@^@<D1>`xW^N<93>ESC<91>
^@^@^@
^@^@^@^H^@^X^@^@^@^@^@^A^@^@^@<FF><81>^@^@^@^@test.txtUT^E^@^C^Z<F7>_eux^K^@^A^D<E8>^C^@^@^D<E8>^C^@^@PK^E^F^@^@^@^@^A^@^A^@N^@^@^@L^@^@^@^@^@
------WebKitFormBoundaryK3WyngXmdiXmbBHu--
请注意,这与curl 请求并不完全相同,但看起来足够接近。