Scrapy 爬取新房列表页面返回 None 的根本原因与解决方案

梦强同学_8778

梦强同学_8778

2026-10-30

801人浏览

原创

Scrapy 爬取新房列表页面返回 None 的根本原因与解决方案

本文详解 scrapy 解析动态生成或类名随机化的网页时 selector 失效的常见原因,提供基于属性前缀/内容的稳健 css 选择器写法、调试技巧及完整可运行代码。

本文详解 scrapy 解析动态生成或类名随机化的网页时 selector 失效的常见原因,提供基于属性前缀/内容的稳健 css 选择器写法、调试技巧及完整可运行代码。

在使用 Scrapy 爬取俄罗斯房地产平台(如 xn--80az8a.xn--d1aqf.xn--p1ai)的新建楼盘列表页时,开发者常遇到 response.css(...).get() 持续返回 None 的问题——即使 HTML 结构清晰、元素可见,CSS 选择器却“失灵”。这并非 Scrapy 本身缺陷,而是现代前端框架(如 React + Styled Components)生成的类名具有高度随机性(如 NewBuildingItem__MainTitle-sc-o36w9y-6 KYYzh 中的 o36w9y-6 和 KYYzh 每次构建可能变化),导致硬编码完整类名的选择器极易失效。

✅ 根本原因:类名不可靠,非语义化

原始代码中使用的 CSS 选择器:

response.css('div.Newbuildings__NewBuildingList-sc-1bou0u4-14.huMfny')

依赖了完整且固定的类名组合。但实际页面中,Newbuildings__NewBuildingList-sc-1bou0u4-14 中的哈希后缀(如 1bou0u4-14)会随前端构建版本变更;而 huMfny 是随机生成的 BEM-style 修饰符,不具备稳定性。

✅ 推荐方案:使用语义化、前缀驱动的选择器

应放弃匹配完整类名,转而利用类名前缀(^=)、类名包含(*=)或结构关系+文本特征进行定位。以下是针对您提供的 HTML 片段优化后的稳健选择器(已验证有效):

目标字段 推荐 Scrapy CSS 选择器 说明
楼盘容器 div[class^='NewBuildingItem__Wrapper'] 匹配以 NewBuildingItem__Wrapper 开头的 class
详情链接 a[class^='NewBuildingItem__ImageWrapper']::attr(href) 利用功能语义前缀,避免依赖随机后缀
楼盘标题 a[class^='NewBuildingItem__MainTitle']::text 同上,精准定位主标题链接文本
地址信息 p[class^='NewBuildingItem__Text']:not([class*='InfoLabel']):not([class*='InfoValue'])::text 排除标签类,保留纯文本描述段落
ID 编号 p[class*='ObjectID']::text 使用 *= 匹配含关键词的类名,更鲁棒
入住时间 p[class^='NewBuildingItem__InfoValue']:nth-child(1)::text 结合顺序定位(首个 InfoValue)
开发商 p[class^='NewBuildingItem__InfoValue']:nth-child(2)::text 第二个 InfoValue,对应开发商字段

? 小技巧:在 Scrapy Shell 中快速验证选择器是否生效:

scrapy shell "https://xn--80az8a.xn--d1aqf.xn--p1ai/..."
>>> response.css("div[class^='NewBuildingItem__Wrapper']").getall()[:2]

✅ 完整修复版 Spider 代码

import scrapy

class BuildingSpider(scrapy.Spider):
    name = "building"
    allowed_domains = ["xn--80az8a.xn--d1aqf.xn--p1ai"]
    # 注意:URL 中 & 需转义为 &(HTML 实体),但 Scrapy 发起请求时应使用原始 &,否则可能被服务端拒绝
    start_urls = [
        "https://xn--80az8a.xn--d1aqf.xn--p1ai/%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D1%8B/%D0%BA%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3-%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BA/%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BE%D0%B2/%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA?place=0-44&objStatus=0"
    ]

    def parse(self, response):
        # 使用前缀匹配,绕过随机哈希类名
        building_wrappers = response.css("div[class^='NewBuildingItem__Wrapper']")

        if not building_wrappers:
            self.logger.warning("⚠️ 未找到任何楼盘容器,请检查页面结构或是否被反爬拦截")
            return

        self.logger.info(f"✅ 成功定位 {len(building_wrappers)} 个楼盘条目")

        for idx, building in enumerate(building_wrappers):
            try:
                link = building.css("a[class^='NewBuildingItem__ImageWrapper']::attr(href)").get()
                title = building.css("a[class^='NewBuildingItem__MainTitle']::text").get(default="").strip()
                address = building.css(
                    "p[class^='NewBuildingItem__Text']:not([class*='InfoLabel']):not([class*='InfoValue'])::text"
                ).get(default="").strip()
                building_id = building.css("p[class*='ObjectID']::text").re_first(r'ID:\s*(\d+)') or ""
                # 提取两个 InfoValue:按 DOM 顺序,第一个是“入伙时间”,第二个是“开发商”
                info_values = building.css("p[class^='NewBuildingItem__InfoValue']::text").getall()
                commissioning = info_values[0].strip() if len(info_values) > 0 else ""
                developer = info_values[1].strip() if len(info_values) > 1 else ""

                yield {
                    'link': response.urljoin(link) if link else None,
                    'title': title,
                    'address': address,
                    'id': building_id,
                    'commissioning': commissioning,
                    'developer': developer,
                    'scraped_index': idx
                }

            except Exception as e:
                self.logger.error(f"❌ 解析第 {idx} 个楼盘时出错: {e}")
                continue

        # 可选:自动翻页(若存在分页按钮)
        next_page = response.css("a[aria-label='Следующая страница']::attr(href)").get()
        if next_page:
            yield response.follow(next_page, self.parse)

⚠️ 关键注意事项

  • URL 编码与实体转义:start_urls 中的 & 应保持原始字符(非 &),否则 Scrapy 请求将发送错误参数。
  • 动态渲染风险:该网站可能依赖 JavaScript 渲染列表。若 response.body 中无目标 HTML,需改用 scrapy-splash 或 playwright 集成方案。
  • 反爬机制:添加 USER_AGENT 和合理 DOWNLOAD_DELAY,避免被封 IP:
    custom_settings = {
        'USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'DOWNLOAD_DELAY': 1.5,
        'RANDOMIZE_DOWNLOAD_DELAY': True,
    }
  • 数据清洗:ID: 60491 中含 HTML 空格实体  ,推荐用 .re_first(r'ID:\s*(\d+)') 提取纯数字。

✅ 总结

当 Scrapy 返回 None 时,首要排查方向不是“XPath 写错了”,而是页面是否真实返回了预期 HTML(用 response.text[:500] 查看原始响应)以及选择器是否过度依赖不稳定类名。坚持使用 [class^='Prefix']、[class*='Keyword'] 和结构逻辑(如 :nth-child()、:not())替代完整类名,可大幅提升爬虫健壮性。对于重度 JS 渲染站点,应果断引入浏览器自动化方案,而非强行用静态解析“硬刚”。

通过以上策略,您将不再被随机类名困扰,真正实现稳定、可维护的新房数据采集流程。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
html版权符号
html版权符号

html版权符号是“©”,可以在html源文件中直接输入或者从word中复制粘贴过来,php中文网还为大家带来html的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.14

6835

7

html在线编辑器
html在线编辑器

html在线编辑器是用于在线编辑的工具,编辑的内容是基于HTML的文档。它经常被应用于留言板留言、论坛发贴、Blog编写日志或等需要用户输入普通HTML的地方,是Web应用的常用模块之一。php中文网为大家带来了html在线编辑器的相关教程、以及相关文章等内容,供大家免费下载使用。

2023.06.21

3792

4

html网页制作
html网页制作

html网页制作是指使用超文本标记语言来设计和创建网页的过程,html是一种标记语言,它使用标记来描述文档结构和语义,并定义了网页中的各种元素和内容的呈现方式。本专题为大家提供html网页制作的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.31

3470

5

html空格
html空格

html空格是一种用于在网页中添加间隔和对齐文本的特殊字符,被用于在网页中插入额外的空间,以改变元素之间的排列和对齐方式。本专题为大家提供html空格的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.01

3479

5

html是什么
html是什么

HTML是一种标准标记语言,用于创建和呈现网页的结构和内容,是互联网发展的基石,为网页开发提供了丰富的功能和灵活性。本专题为大家提供html相关的各种文章、以及下载和课程。

2023.08.11

5079

6

html字体大小怎么设置
html字体大小怎么设置

在网页设计中,字体大小的选择是至关重要的。合理的字体大小不仅可以提升网页的可读性,还能够影响用户对网页整体布局的感知。php中文网将介绍一些常用的方法和技巧,帮助您在HTML中设置合适的字体大小。

2023.08.11

3421

3

html转txt
html转txt

html转txt的方法有使用文本编辑器、使用在线转换工具和使用Python编程。本专题为大家提供html转txt相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.31

3229

3

html文本框代码怎么写
html文本框代码怎么写

html文本框代码:1、单行文本框【<input type="text" style="height:..;width:..;" />】;2、多行文本框【textarea style=";height:;"></textare】。

2023.09.01

2808

6

HTML嵌入CSS样式的方法
HTML嵌入CSS样式的方法

HTML嵌入CSS样式的方法有内联样式、内部样式表和外部样式表。本专题为大家提供CSS样式相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.20

2928

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.1万人学习