Scrapy中multipart/form-data请求字符异常及正确使用方法问询
解决Scrapy中multipart/form-data字符转义问题
你遇到的问题核心是手动构造请求body时,Python会自动解析字符串中的转义字符,比如你写的\\wcp01zfs-03.clerk.local\files2\ORISPDF\,实际发送时会被转成\wcp01zfs-03.clerk.localfiles2ORISPDF\(因为\f是Python的换页符转义序列),这和浏览器发送的原始数据完全不一致,最终导致请求失败。
别再手动拼接multipart请求体了——Scrapy提供了更可靠的方式来处理表单提交,推荐使用FormRequest类自动处理编码、边界生成和字符转义,完全贴合浏览器的请求行为。
方案1:用FormRequest.from_response()自动提取表单
既然页面上已经存在目标表单(就是你提供的courtform),直接用from_response方法自动解析表单的action、enctype和所有字段,只需要替换动态的id和doc_id即可:
def get_image(self, response): # 自动解析页面中的courtform表单 yield scrapy.FormRequest.from_response( response, formname='courtform', # 匹配表单的name属性 formdata={ 'id': response.xpath("//input[@name='doc_id']/@value").extract_first(), 'doc_id': response.xpath("//input[@name='doc_id']/@value").extract_first() }, callback=self.get_pdf, dont_filter=True )
这个方法会帮你自动处理multipart/form-data的所有细节,包括边界字符串生成、字段值的正确编码,完全复刻浏览器的请求逻辑。
方案2:手动构造FormRequest
如果需要完全自定义表单字段,可以把所有数据整理成字典,交给FormRequest处理编码:
def get_image(self, response): url = 'http://oris.co.palm-beach.fl.us:8080/PdfServlet/PdfServlet27' doc_id = response.xpath("//input[@name='doc_id']/@value").extract_first() # 整理所有表单字段,注意带反斜杠的内容用原始字符串(r'')避免转义 formdata = { 'hostURL': 'http://oris.co.palm-beach.fl.us/or_web1/', 'pdfPath': r'\\wcp01zfs-03.clerk.local\files2\ORISPDF\', 'pdfURL': 'http://oris.co.palm-beach.fl.us/pdf/', 'pages': '1', 'id': doc_id, 'mpages': '1', 'doc_id': doc_id, 'page1': r'image_from_file.asp?imageurl=\\ors_fs\ORImage\O\30338\O.30338.0268.0001.tif', 'WaterMarkText': '1' } yield scrapy.FormRequest( url, formdata=formdata, # 指定enctype,Scrapy会自动生成正确的边界和请求体 headers={'Content-Type': 'multipart/form-data'}, callback=self.get_pdf, dont_filter=True )
如果你坚持手动构造body(不推荐)
如果一定要自己写body字符串,必须用原始字符串(在字符串前加r),这样Python不会解析里面的转义字符:
def get_image(self, response): url = 'http://oris.co.palm-beach.fl.us:8080/PdfServlet/PdfServlet27' doc_id = response.xpath("//input[@name='doc_id']/@value").extract_first() # 用r''定义原始字符串,避免转义字符被解析 body = r'''------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="hostURL" http://oris.co.palm-beach.fl.us/or_web1/ ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="pdfPath" \\wcp01zfs-03.clerk.local\files2\ORISPDF\ ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="pdfURL" http://oris.co.palm-beach.fl.us/pdf/ ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="pages" 1 ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="id" {} ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="mpages" 1 ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="doc_id" {} ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="page1" image_from_file.asp?imageurl=\\ors_fs\ORImage\O\30338\O.30338.0268.0001.tif ------WebKitFormBoundarysGRfL8zMeuSs4zsH Content-Disposition: form-data; name="WaterMarkText" 1 ------WebKitFormBoundarysGRfL8zMeuSs4zsH--'''.format(doc_id, doc_id) headers = { 'origin': "http://oris.co.palm-beach.fl.us", 'upgrade-insecure-requests': "1", 'dnt': "1", 'content-type': "multipart/form-data; boundary=----WebKitFormBoundarysGRfL8zMeuSs4zsH", 'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36", 'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 'cache-control': "no-cache", } r = scrapy.Request(url, method="POST", headers=headers, body=body, callback=self.get_pdf, dont_filter=True) yield r
不过这种方式很容易出错,比如边界字符串和headers里的不一致、换行符格式不匹配等,所以优先推荐前两种FormRequest的方案。
内容的提问来源于stack exchange,提问作者JBJ
相关产品推荐
相关产品推荐

