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

如何用Scrapy实现动态加载(Load More按钮)网站分页?含gelbeseiten实例

Gelbeseiten网站分页爬取解决方案

针对你遇到的Gelbeseiten酒店列表分页问题,结合你提到的AJAX请求和现有Scrapy代码,提供两种可行的分页实现方案:

方案一:Selenium模拟点击分页按钮(简单直接)

适合不想深入分析AJAX参数的场景,直接模拟浏览器点击下一页按钮,加载新页面后继续提取数据。

修改后的完整代码

import scrapy
from scrapy_selenium import SeleniumRequest
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException


class Data2Spider(scrapy.Spider):
    name = 'data2'

    def start_requests(self):
        yield SeleniumRequest(
            url="https://www.gelbeseiten.de/suche/hotels/n%c3%bcrnberg",
            callback=self.parse,
        )

    def parse(self, response):
        # 提取当前页所有酒店的data-realid
        real_ids = response.xpath("//article/@data-realid").getall()
        for real_id in real_ids:
            yield SeleniumRequest(
                url=f"https://www.gelbeseiten.de/gsbiz/{real_id}",
                callback=self.parse_data,
            )
        
        # 处理分页逻辑
        driver = response.meta['driver']
        try:
            # 定位下一页按钮(若页面更新需调整选择器)
            next_btn = driver.find_element(By.XPATH, "//a[@class='mod-Pagination__link mod-Pagination__link--next']")
            # 检查按钮是否未被禁用
            if 'mod-Pagination__link--disabled' not in next_btn.get_attribute('class'):
                next_btn.click()
                # 等待新页面的酒店元素加载完成
                WebDriverWait(driver, 10).until(
                    EC.presence_of_element_located((By.XPATH, "//article[@data-realid]"))
                )
                # 生成下一页的请求
                yield SeleniumRequest(
                    url=driver.current_url,
                    callback=self.parse,
                    meta={'driver': driver}
                )
        except NoSuchElementException:
            # 无下一页,结束爬取
            self.logger.info("已爬取所有页面")

    def parse_data(self, response):
        yield {
            'URL': response.url,
            'Title': response.xpath("//div[@class='a']/h1/text()").get(),
            'Phone': response.xpath("//a[@class='nolink-black']/span/text()").get(),
            'Fax': response.xpath("//div[@class='mod-Kontaktdaten__list-item contains-icon-fax']/span/text()").get(),
            'email': response.xpath("normalize-space(//div[@class='mod-Kontaktdaten__list-item contains-icon-email']/a/text())").get(),
            'Website': response.xpath("normalize-space(//div[@class='mod-Kontaktdaten__list-item contains-icon-homepage']/a/text())").get()
        }

方案二:直接调用AJAX接口(高效推荐)

通过分析浏览器网络面板中的ajaxsuche请求,直接发送POST请求获取分页数据,无需加载完整页面,爬取效率大幅提升。

修改后的完整代码

import scrapy
import re
from scrapy_selenium import SeleniumRequest


class Data2Spider(scrapy.Spider):
    name = 'data2'

    def start_requests(self):
        # 先请求初始页面,获取分页所需参数
        yield SeleniumRequest(
            url="https://www.gelbeseiten.de/suche/hotels/n%c3%bcrnberg",
            callback=self.get_pagination_params,
        )

    def get_pagination_params(self, response):
        # 提取csrfToken
        csrf_token = response.xpath("//meta[@name='csrf-token']/@content").get()
        # 提取searchParams(从页面全局变量中匹配)
        script_content = response.xpath("//script[contains(text(), 'window.searchParams')]/text()").get()
        search_params_match = re.search(r'window.searchParams = (\{.*?\});', script_content)
        search_params = search_params_match.group(1) if search_params_match else ''

        # 处理第一页数据
        real_ids = response.xpath("//article/@data-realid").getall()
        for real_id in real_ids:
            yield SeleniumRequest(
                url=f"https://www.gelbeseiten.de/gsbiz/{real_id}",
                callback=self.parse_data,
            )

        # 发起第二页的AJAX请求
        yield scrapy.Request(
            url="https://www.gelbeseiten.de/ajaxsuche",
            method="POST",
            headers={
                'X-CSRF-Token': csrf_token,
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'X-Requested-With': 'XMLHttpRequest'
            },
            formdata={
                'page': '2',
                'size': '20',
                'searchParams': search_params
            },
            callback=self.parse_ajax_page,
            meta={
                'csrf_token': csrf_token,
                'search_params': search_params,
                'current_page': 2
            }
        )

    def parse_ajax_page(self, response):
        # 解析AJAX返回的JSON数据
        data = response.json()
        # 提取酒店realId
        hits = data.get('hits', [])
        for hit in hits:
            real_id = hit.get('realId')
            if real_id:
                yield SeleniumRequest(
                    url=f"https://www.gelbeseiten.de/gsbiz/{real_id}",
                    callback=self.parse_data,
                )

        # 检查是否有下一页
        has_next = data.get('pagination', {}).get('hasNextPage', False)
        if has_next:
            current_page = response.meta['current_page'] + 1
            # 发起下一页请求
            yield scrapy.Request(
                url="https://www.gelbeseiten.de/ajaxsuche",
                method="POST",
                headers={
                    'X-CSRF-Token': response.meta['csrf_token'],
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    'X-Requested-With': 'XMLHttpRequest'
                },
                formdata={
                    'page': str(current_page),
                    'size': '20',
                    'searchParams': response.meta['search_params']
                },
                callback=self.parse_ajax_page,
                meta={
                    'csrf_token': response.meta['csrf_token'],
                    'search_params': response.meta['search_params'],
                    'current_page': current_page
                }
            )

    def parse_data(self, response):
        yield {
            'URL': response.url,
            'Title': response.xpath("//div[@class='a']/h1/text()").get(),
            'Phone': response.xpath("//a[@class='nolink-black']/span/text()").get(),
            'Fax': response.xpath("//div[@class='mod-Kontaktdaten__list-item contains-icon-fax']/span/text()").get(),
            'email': response.xpath("normalize-space(//div[@class='mod-Kontaktdaten__list-item contains-icon-email']/a/text())").get(),
            'Website': response.xpath("normalize-space(//div[@class='mod-Kontaktdaten__list-item contains-icon-homepage']/a/text())").get()
        }

注意事项

  • 网站可能存在反爬机制,建议在Scrapy的settings.py中配置合理的USER_AGENT,必要时添加请求延迟或代理。
  • AJAX请求的参数(如searchParams结构)可能随网站更新变化,需定期检查调整。
  • 使用Selenium时,确保浏览器驱动版本与本地浏览器版本匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 00:01:03