如何使用Python requests模块正确发送multipart格式POST请求
正确实现方法
原代码的问题排查
对照拦截到的原始请求,原有代码存在几个会直接导致请求失败的硬伤:
- 手动硬编码了
Content-Type里的固定boundary、写死Content-Length: 237:这个长度是浏览器发那次请求时的实际值,后续不管怎么调整请求体,这个固定长度都会和实际发送的内容不匹配;而requests构造multipart表单时,会自动生成合法的随机分隔边界、自动计算正确的请求体长度,手动写死这两个值会让服务器完全没法正确解析请求体。 - 传参逻辑完全错误:直接把纯字符串
"gmail.com"传给data参数,requests会把它当成普通urlencoded格式的请求发送,根本不是目标要求的multipart/form-data结构,服务器自然读不到对应的表单字段。 - 请求头键名写错:原来的代码里把Referer头的键写成了
"Referer: https",这是个无效的头标识,等于实际根本没把Referer值发送给服务器,站点的来源校验会直接拦截请求。 - 多余的重复头:Host头requests会根据请求的URL自动生成,不需要手动配置,硬塞进去反而可能出现请求头匹配错误。
可直接运行的正确代码
requests本身对multipart/form-data格式有内置支持,完全不需要手动拼接请求体、自己写boundary,只需要把表单字段按要求传入files参数,就会自动生成和浏览器格式完全一致的请求:
import requests # 对应拦截到的两个multipart表单字段:url、domain form_fields = { "url": (None, "gmail.com"), "domain": (None, "0") } # 只保留站点校验需要的自定义头,去掉所有requests会自动处理的头(Host、Content-Length、Content-Type、Connection均自动生成) request_headers = { "sec-ch-ua": '"Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"', "sec-ch-ua-mobile": '?0', "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36", "Accept": "*/*", "Origin": "https://cutt.ly", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Dest": "empty", "Referer": "https://cutt.ly/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Cookie": "urls=%5B%5B%222021-04-19%22%2C%22Gmail%22%2C%22http%3A%5C%2F%5C%2Fgmail.com%22%2C%22https%3A%5C%2F%5C%2Fcutt.ly%5C%2FFvTlMPI%22%5D%2C%5B%222021-04-19%22%2C%22Zaktualizuj%20swoj%5Cu0105%20przegl%5Cu0105dark%5Cu0119%20%7C%20Facebook%22%2C%22http%3A%5C%2F%5C%2Fwww.facebook.com%22%2C%22https%3A%5C%2F%5C%2Fcutt.ly%5C%2FqvTturR%22%5D%5D; __cfduid=dab381c8531b29b6a21779a3d8fc1c37b1618831070; PHPSESSID=isje9ckfgbi3mardervtpldlr7; _ga=GA1.2.1920648684.1618831073; _gid=GA1.2.1780730818.1618831073; cookies_accepted=T; _gat_gtag_UA_112763434_1=1" } response = requests.post( "https://cutt.ly/scripts/shortenUrl.php", files=form_fields, headers=request_headers ) print(response.text)
实操注意事项
- 传入
files的字典采用{字段名: (None, 字段值)}的格式,元组第一个参数填None是因为当前请求没有实际上传文件,只是普通文本表单字段,这样生成的multipart结构和浏览器实际发送的格式完全匹配。 - 绝对不要手动在headers里写带固定boundary的
Content-Type,requests会根据生成的随机边界自动补全正确的Content-Type头,手写固定值会直接导致格式不匹配。 - 原Cookie里的
PHPSESSID是动态会话值,过期后需要从浏览器重新抓取最新的有效Cookie再使用,否则站点会直接返回权限错误。
内容的提问来源于stack exchange,提问作者GOKULAKRISHNAN M
相关产品推荐
相关产品推荐

