You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scrapy爬取含#!的AJAX URL遇301重定向问题求解

解决Scrapy爬取带#!的AJAX URL时的301重定向问题

你遇到的问题确实和URL里的#!(hashbang)有关——这种模式是早期AJAX网站用来支持搜索引擎爬取的方案,服务器会识别_escaped_fragment_参数来返回渲染后的页面,但直接请求带#!的URL时,Scrapy不会自动处理这个转换,导致服务器返回301重定向到首页。下面是具体的解决步骤和修改后的代码:

核心思路

要让服务器返回正确的内容,需要把带#!的URL转换为搜索引擎友好的格式:将#!替换为?_escaped_fragment_=,同时保留原#!后面的所有路径内容(注意不需要额外编码,原URL里的编码已经符合要求)。

修改后的蜘蛛代码

import json
import scrapy
import re
import pkgutil
import urllib.parse
from scrapy.loader import ItemLoader
from annonces_spiders.items import AnnonceItem

class GlenMarchSpider(scrapy.Spider):
    name = 'annonces_results'
    def __init__(self, *args, **kwargs):
        data_file = pkgutil.get_data("annonces_spiders", "json/input/db_scrap_url_lp_js_10000_reduced.json")
        self.data = json.loads(data_file)
    def start_requests(self):
        # 模拟浏览器的User-Agent,避免被服务器识别为爬虫
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
        for item in self.data:
            original_url = item['url_lp']
            # 处理hashbang格式的URL
            if '#!' in original_url:
                parsed_url = urllib.parse.urlsplit(original_url)
                # 提取#!后的片段(去掉开头的!)
                fragment_content = parsed_url.fragment[2:] if parsed_url.fragment.startswith('!') else parsed_url.fragment
                # 构造新的URL,将片段作为_escaped_fragment_参数
                new_url = urllib.parse.urlunsplit(
                    (parsed_url.scheme, parsed_url.netloc, parsed_url.path, f'_escaped_fragment_={fragment_content}', '')
                )
            else:
                new_url = original_url
            
            request = scrapy.Request(new_url, callback=self.parse, headers=headers)
            request.meta['item'] = item
            yield request
    def parse(self, response):
        item = response.meta['item']
        item['results'] = []
        for caritem in response.css("li.li-result"):
            data = AnnonceItem()
            data["marque"] = caritem.css("span.brand::text").extract_first().capitalize().replace(" ", "").strip()
            data["model"] = caritem.css("span.sub-title::text").extract_first().capitalize().replace(" ", "").strip()
            data["model_year"] = caritem.css("li:nth-child(3) > div.upper::text").extract_first().replace(" ", "").strip()
            data["price_str"] = caritem.css("p.prix::text").extract_first().strip()
            if "NC" not in data["price_str"]:
                data["price_int"] = int(caritem.css("p.prix::text").extract_first().replace("€", "").replace(" ", "").strip())
            else:
                data["price_int"] = None
            data["annonce_date"] = caritem.css("p.btn-publication::text").extract_first().strip().replace("/", "-")
            data["country"] = caritem.css("div.location > img::attr(alt)").extract_first().capitalize().strip()
            item['results'].append(data)
        yield item

关键修改点说明

  1. URL转换逻辑:用urllib.parse模块拆分原始URL,提取#!后的内容,将其作为_escaped_fragment_参数拼接到URL中,确保服务器能识别并返回渲染后的页面。
  2. 添加User-Agent:模拟浏览器请求头,避免服务器因识别为爬虫而返回异常内容或重定向。

额外检查建议

  • 运行爬虫后查看日志,如果仍有重定向,可以尝试添加dont_redirect=True到scrapy.Request参数中,但优先确保URL转换正确。
  • 如果服务器返回的内容还是不符合预期,可以检查响应的Status Code和内容,确认是否需要处理Cookies或其他请求头。

内容的提问来源于stack exchange,提问作者lf_celine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 06:38:01