Scrapy CSS选择器失效:深入理解浏览器与爬虫获取HTML内容的差异

Scrapy CSS选择器失效:深入理解浏览器与爬虫获取HTML内容的差异

在使用scrapy进行网页抓取时,开发者常常会遇到一个令人困惑的问题:精心调试的css选择器浏览器开发者工具中能够准确匹配元素,但在scrapy爬取时却一无所获。这通常并非选择器本身有误,而是scrapy所见的网页内容与用户在浏览器中看到的内容存在本质差异。本文将深入探讨这一现象的原因,并提供实用的方法来验证scrapy实际获取的html,从而有效解决此类问题。

1. 问题现象:css选择器在Scrapy中失效

考虑一个典型的场景:你正在爬取两个结构相似的页面,例如https://dicionario.priberam.org/putear和https://dicionario.priberam.org/puteares。你使用了一个css选择器,例如div.dp-conteudo__esquerda span.varpb,期望从中提取特定的文本。在第一个页面上,该选择器成功返回了结果,但在第二个页面上,它却返回空值。

初步检查页面源代码可能会发现,第二个页面上确实存在一个带有varpb类的span标签,甚至可能出现在不同的父级元素下,但无论如何,Scrapy似乎都无法找到它。这种不一致性让开发者感到困惑,因为理论上,只要元素存在于html中,选择器就应该能够匹配。

2. 根本原因:浏览器与Scrapy获取内容的差异

问题的核心在于浏览器和Scrapy处理网页内容的方式不同。

  • 浏览器行为: 当你在浏览器中访问一个网页时,浏览器不仅会下载初始HTML文档,还会解析并执行其中包含的javaScript代码。这些javascript代码可能会动态地修改dom结构、从服务器请求额外的数据(例如通过ajax),然后将这些数据插入到页面中。因此,你在浏览器开发者工具中看到的“源代码”实际上是经过JavaScript处理和渲染后的最终DOM结构。
  • Scrapy行为: Scrapy(以及大多数传统的网络爬虫)默认情况下只负责下载服务器返回的原始HTML响应。它不会执行JavaScript代码,也不会渲染页面。这意味着,如果网页的某些内容是通过JavaScript动态加载或生成的,那么这些内容将不会出现在Scrapy获取的原始HTML中。

回到我们的例子,第二个页面https://dicionario.priberam.org/puteares上的目标span.varpb元素很可能就是通过JavaScript在页面加载完成后动态添加到DOM中的。由于Scrapy不执行JavaScript,它在抓取时就无法“看到”这个动态添加的元素,因此选择器自然会失效。而第一个页面https://dicionario.priberam.org/putear上的相应元素可能直接包含在初始HTML中,所以Scrapy能够成功获取。

立即学习前端免费学习笔记(深入)”;

3. 验证Scrapy所见内容的最佳实践

理解了原因之后,解决问题的关键就是确保你的CSS选择器是基于Scrapy实际获取到的HTML内容来编写的。以下是两种验证Scrapy所见内容的方法:

3.1 使用Scrapy Shell的view(response)命令

Scrapy Shell是一个强大的交互式环境,允许你在不运行整个爬虫的情况下测试代码和检查响应。view(response)命令可以让你在浏览器中打开Scrapy当前处理的response对象所包含的HTML内容。这能直观地展示Scrapy到底“看到了”什么。

操作步骤:

  1. 在终端中启动Scrapy Shell并fetch目标URL:
    scrapy shell 'https://dicionario.priberam.org/puteares'

  2. 在Scrapy Shell中,执行view(response):
    In [1]: view(response)

    这会打开一个新的浏览器窗口,显示Scrapy抓取到的原始HTML。你可以使用浏览器开发者工具检查这个页面,看看目标元素是否真的存在。

    Scrapy CSS选择器失效:深入理解浏览器与爬虫获取HTML内容的差异

    Calliper 文档对比神器

    文档内容对比神器

    Scrapy CSS选择器失效:深入理解浏览器与爬虫获取HTML内容的差异 28

    查看详情 Scrapy CSS选择器失效:深入理解浏览器与爬虫获取HTML内容的差异

3.2 将response.text保存到本地文件

另一种更直接、更方便离线分析的方法是将Scrapy获取到的原始HTML内容保存到一个本地文件。

操作步骤:

  1. 在Scrapy Shell中fetch目标URL:

    In [2]: fetch('https://dicionario.priberam.org/putear') 2023-12-28 00:22:01 [scrapy.core.engine] INFO: Spider opened 2023-12-28 00:22:01 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://dicionario.priberam.org/putear> (referer: None)

  2. 将response.text写入一个本地html文件

    In [3]: with open('putear.html', 'wt', encoding='utf8') as fd:    ...:     fd.write(response.text)    ...:

  3. 对第二个页面重复上述步骤:

    In [4]: fetch('https://dicionario.priberam.org/puteares') 2023-12-28 00:23:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://dicionario.priberam.org/puteares> (referer: None)  In [5]: with open('puteares.html', 'wt', encoding='utf8') as fd:    ...:     fd.write(response.text)    ...:

  4. 现在,你可以在本地文件系统找到putear.html和puteares.html这两个文件。用任何文本编辑器或浏览器打开它们,检查它们的源代码。你会发现,在puteares.html中,你期望的div.dp-conteudo__esquerda span.varpb结构很可能是不存在的,或者span.varpb元素位于完全不同的位置,导致你的原始选择器失效。

4. 总结与注意事项

  • 始终验证Scrapy所见: 当CSS选择器在浏览器中有效但在Scrapy中失效时,第一步也是最重要的一步就是验证Scrapy实际获取的HTML内容。这能帮助你区分是选择器错误还是内容动态加载的问题。
  • 动态内容处理: 如果确认目标内容是通过JavaScript动态加载的,传统的Scrapy爬虫将无法直接获取。在这种情况下,你需要考虑使用能够执行JavaScript的工具,例如:
    • Scrapy Splash: 一个轻量级的JavaScript渲染服务,可以与Scrapy集成。
    • Selenium/Playwright: 浏览器自动化工具,可以模拟用户行为,等待页面加载完成并获取渲染后的HTML。
  • 选择器优化: 如果目标元素存在于Scrapy获取的HTML中,但只是位置与浏览器中看到的略有不同,那么你需要根据Scrapy实际获取的HTML结构来调整你的CSS选择器。
  • 优先级: 在调试Scrapy选择器时,优先使用response.css()或response.xpath()进行测试,而不是完全依赖浏览器开发者工具中的选择器结果,因为它们所操作的DOM可能不同。

通过理解Scrapy与浏览器在处理动态内容上的差异,并利用上述验证方法,你将能更有效地调试和开发你的Scrapy爬虫,从而避免因HTML内容不一致而导致的抓取失败。

上一篇
下一篇
text=ZqhQzanResources