Scrapy爬取Immobilienscout24页面JSON报JSONDecodeError如何解决
Scrapy抓取ImmoScout24接口报JSONDecodeError修复方案
问题根因
抛出JSONDecodeError的核心原因是你拿到的响应根本不是预期的JSON格式,而是站点返回的HTML拦截页/验证码页,和切换VPN、补全常规请求头没有直接关系,具体问题点有两个:
- 跳过了会话初始化流程:该站点反爬规则要求客户端必须先访问无参数的基础搜索页,拿到服务端下发的会话校验cookie之后,发送带筛选参数的XHR请求才会返回JSON数据。你直接请求带参数的接口,服务端识别到请求携带的cookie不合法,直接返回拦截页,自然无法解析JSON。
- 请求头配置错误:GET请求不需要添加
content-type: application/json头,这个头仅用于请求体为JSON格式的POST请求,添加该头反而会直接触发反爬规则。
修复步骤
- 先模拟浏览器首次访问逻辑,第一步先请求不带任何参数的基础搜索页,让爬虫自动保存服务端下发的合法cookie,再发起带筛选参数的接口请求。
- 移除GET请求中冗余错误的
content-type请求头,请求头字段尽量和真实浏览器请求顺序保持一致,不要随意增删非必要字段。 - 增加响应校验逻辑,不要直接调用
response.json(),先判断响应的Content-Type是否为application/json,如果返回HTML内容就触发重试、重新刷新cookie,避免硬解析报错。 - 控制请求频率,单IP请求间隔保持在3秒以上,降低触发验证码拦截的概率。
修复后代码示例
Scrapy版本
import scrapy import urllib class ImmobilienScoutSpider(scrapy.Spider): name = "immobilienscout" base_url = "https://www.immobilienscout24.de/Suche/de/nordrhein-westfalen/wohnung-kaufen" api_headers = { 'accept': 'application/json; charset=utf-8', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'en-US,en;q=0.9', 'x-requested-with': 'XMLHttpRequest', 'referer': base_url, 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36', } params = { 'price': '1000.0-', 'constructionyear': '-2000', 'pagenumber': '1' } def start_requests(self): # 首次访问基础页获取合法cookie yield scrapy.Request( url=self.base_url, headers={ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', 'user-agent': self.api_headers['user-agent'], 'accept-language': self.api_headers['accept-language'] }, callback=self.request_search_api ) def request_search_api(self, response): # cookie已自动保存,再请求带参数的JSON接口 req_url = f'{self.base_url}?{urllib.parse.urlencode(self.params)}' yield scrapy.Request( url=req_url, headers=self.api_headers, callback=self.parse, ) def parse(self,response): # 校验响应格式,避免解析报错 if 'application/json' not in response.headers.get('Content-Type', b'').decode(): self.logger.warning("Intercepted by anti-crawl, retrying...") # 触发重试,重新获取cookie yield scrapy.Request( url=self.base_url, headers={ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'user-agent': self.api_headers['user-agent'], 'accept-language': self.api_headers['accept-language'] }, callback=self.request_search_api, dont_filter=True ) return yield {"response":response.json()}
requests版本
import requests base_link = 'https://www.immobilienscout24.de/Suche/de/nordrhein-westfalen/wohnung-kaufen' headers = { 'accept': 'application/json; charset=utf-8', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'en-US,en;q=0.9', 'x-requested-with': 'XMLHttpRequest', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36', 'referer': base_link, } params = { 'price': '1000.0-', 'constructionyear': '-2000', 'pagenumber': '2' } sess = requests.Session() # 第一步访问基础页获取初始cookie sess.get( base_link, headers={ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'user-agent': headers['user-agent'], 'accept-language': headers['accept-language'] } ) # 再请求带参数的搜索接口 resp = sess.get(base_link, params=params, headers=headers) # 校验响应格式后再解析 if 'application/json' in resp.headers.get('Content-Type', ''): print(resp.json()) else: print(f"Request blocked, status code: {resp.status_code}")
补充说明
不要硬编码固定cookie,站点的会话cookie有有效期,过期后即使携带也会被拦截;如果遇到验证码拦截,不要重复发送接口请求,先重新访问基础页刷新cookie后再重试。
内容的提问来源于stack exchange,提问作者robots.txt
相关产品推荐
相关产品推荐

