Scrapy POST请求Payload格式问题及404错误排查咨询
解决Scrapy请求Storia Imóveis AJAX接口404错误的疑问解答
先结合你的代码和问题逐一拆解分析:
1. Scrapy请求携带的Payload是否需要特定类型或格式?
是的,完全取决于目标接口的要求。从你设置的Content-Type: application/json可以看出,这个接口要求JSON格式的请求体,所以你的Payload必须是符合JSON规范的字符串,同时结构要和浏览器发送的请求体完全匹配(包括字段名称、嵌套层级、数据类型)。比如你Payload里的经纬度是数字类型,这就和原请求一致,不能随便改成字符串。
另外要注意,接口依赖的sessionId是硬编码的,这很可能已经过期失效——这也是导致404的常见原因之一,建议从起始页的HTML或Cookie中动态提取有效的sessionId。
2. 是否需要调用json.dumps(payload)后再发送,还是直接发送字典?
在Scrapy的Request中,如果你的Content-Type是application/json,必须用json.dumps()把Python字典转换成JSON字符串,然后赋值给body参数。如果直接传字典,Scrapy会把它当成表单数据处理,接口无法识别,必然返回错误。你代码里的body=json.dumps(self.payload)这部分是正确的。
3. 是否需要将Payload的所有键值对转为字符串?
不需要!json.dumps()会自动处理Python的基础数据类型(数字、布尔值、列表、字典),把它们转换成符合JSON规范的格式。比如你Payload里的经纬度是数字,就应该保持数字类型,如果强行转成字符串,反而会和接口预期的格式不符,导致请求失败。
4. 还有哪些可能导致请求失败?
除了上面提到的点,还有几个常见的坑:
- URL参数错误:你
start_urls里的API URL用了&(HTML转义符),这是错误的,应该直接用&,否则URL会被解析错误,返回404。 - 初始请求逻辑错误:你的
start_urls直接写了API地址,Scrapy默认会发送GET请求,但这个接口需要POST,所以初始的GET请求就会返回404。正确的做法是把起始页设为https://www.storiaimoveis.com.br/alugar/brasil,在parse方法中处理起始页的响应,提取必要的参数(比如sessionId)后,再构造POST请求到API接口。 - SessionId过期/无效:你硬编码的
sessionId大概率是临时的,网站会定期失效,必须从浏览器请求中动态获取(比如查看起始页的Cookie、HTML中的脚本变量)。 - 缺少必要的请求头:除了你现有的头,可能还需要添加
X-Requested-With: XMLHttpRequest(很多AJAX接口会校验这个),或者Accept-Language等头,建议用浏览器开发者工具对比原请求的所有头信息。 - 反爬机制拦截:你的
User-Agent是Chrome 75,版本太旧了,网站可能会识别为爬虫。建议换成最新的Chrome/Firefox的User-Agent,或者添加Cookie字段(从浏览器复制有效Cookie测试)。
修正后的代码片段参考
import scrapy import json from scrapy.spiders import CrawlSpider class MySpider(CrawlSpider): name = 'myspider' # 起始页改为网站首页,而非API接口 start_urls = ['https://www.storiaimoveis.com.br/alugar/brasil'] headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Referer': 'https://www.storiaimoveis.com.br/alugar/brasil', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest' # 添加AJAX标识头 } def parse(self, response): # 这里可以从响应中提取动态的sessionId,比如从Cookie中获取(示例逻辑,需根据实际调整) session_id = "" for cookie in response.headers.getlist('Set-Cookie'): cookie_str = cookie.decode('utf-8') if 'sessionId' in cookie_str: session_id = cookie_str.split('sessionId=')[1].split(';')[0] break # 构造正确的API URL,替换&为& api_url = f'https://www.storiaimoveis.com.br/api/search?fields=$$meta.geo.postalCodeAddress.city,$$meta.geo.postalCodeAddress.neighborhood,$$meta.geo.postalCodeAddress.street,$$meta.location,$$meta.created,address.number,address.postalCode,address.neighborhood,address.state,media,livingArea,totalArea,types,operation,salePrice,rentPrice,newDevelopment,administrationFee,yearlyTax,account.logoUrl,account.name,account.id,account.creci,garage,bedrooms,suites,bathrooms,ref&optimizeMedia=true&size=20&from=0&sessionId={session_id}' payload = { "locations":[{"geo":{"top_left":{"lat":5.2717863, "lon":-73.982817}, "bottom_right":{"lat":-34.0891, "lon":-28.650543}}, "placeId":"ChIJzyjM68dZnAARYz4p8gYVWik", "keywords":"Brasil", "address":{"label":"Brasil","country":"BR"}}], "operation":["RENT"], "bathrooms":[], "bedrooms":[], "garage":[], "features":[] } yield scrapy.Request( url=api_url, method='POST', headers=self.headers, body=json.dumps(payload), callback=self.parse_items ) def parse_items(self, response): from scrapy.shell import inspect_response inspect_response(response, self) print(response.text)
内容的提问来源于stack exchange,提问作者gunesevitan




