百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

如何使用 Scrapy 执行 JavaScript

itomcoil 2025-02-19 12:22 21 浏览

大多数现代网站都使用客户端 JavaScript 框架,例如 React、Vue 或 Angular。在没有服务器端渲染的情况下从动态网站抓取数据通常需要执行 JavaScript 代码。

我已经抓取了数百个网站,而且我总是使用 Scrapy。Scrapy 是一个流行的 Python 网页抓取框架。与其他 Python 抓取库(例如 Beautiful Soup)相比,Scrapy 帮助您根据一些最佳实践来构建代码。Scrapy 负责并发、收集统计数据、缓存、处理重试逻辑和许多其他问题。

在本文中,我比较了使用 Scrapy 执行 JavaScript 的最流行的解决方案,包括如何扩展无头浏览器,并介绍了与 ScrapingBee API 的开源集成以支持 JavaScript 和代理轮换。

使用 Scrapy 抓取动态网站

使用 Scrapy 抓取客户端呈现的网站曾经很痛苦。我经常自己检查浏览器网络工具上的 API 请求并从 JavaScript 变量中提取数据。虽然这些 hack 可能适用于某些网站,但我发现这些代码比传统的 XPATH 更难理解和维护。但要直接从 HTML 中抓取客户端数据,您首先需要执行 JavaScript 代码。

用于无头浏览器的 Scrapy 中间件

无头浏览器是没有图形用户界面的网络浏览器。我使用了三个库来使用 Scrapy 执行 JavaScript:scrapy-selenium、scrapy-splash 和 scrapy-scrapingbee。

所有三个库都集成为 Scrapy下载器中间件。一旦在您的项目设置中进行配置,您就不会从您的spiders产生一个正常的 Scrapy 请求,而是产生一个 SeleniumRequest、SplashRequest 或 ScrapingBeeRequest。

使用 Selenium 在 Scrapy 中执行

在本地,您可以使用带有scrapy-selenium中间件的 Scrapy 与无头浏览器交互。Selenium 是一个与浏览器交互的框架,通常用于测试应用程序、网页抓取和截屏。

Selenium 需要一个Web 驱动程序来与浏览器交互。例如,Firefox 要求您安装 geckodriver。然后,您可以在 Scrapy 项目设置中配置 Selenium。



from shutil import which

SELENIUM_DRIVER_NAME = 'firefox'

SELENIUM_DRIVER_EXECUTABLE_PATH = which('geckodriver')

SELENIUM_DRIVER_ARGUMENTS=['-headless']

DOWNLOADER_MIDDLEWARES = {

    'scrapy_selenium.SeleniumMiddleware': 800

}

在你的spiders中,你可以产生一个 SeleniumRequest。

from scrapy_selenium import SeleniumRequest

yield SeleniumRequest(url, callback=self.parse)

Selenium 允许您使用 Python 和 JavaScript 与浏览器进行交互。驱动程序对象可以从 Scrapy 响应中访问。有时在单击按钮后检查 HTML 代码会很有用。在本地,您可以使用 ipdb 调试器设置断点来检查 HTML 响应。

def parse(self, response):


    driver = response.request.meta['driver']
    driver.find_element_by_id('show-price').click()


    import ipdb; ipdb.set_trace()
    print(driver.page_source)

否则,可以从响应对象访问 Scrapy XPATH 和 CSS 选择器以从 HTML 中选择数据。

def parse(self, response):
    title = response.selector.xpath(
        '//title/@text'
    ).extract_first()

SeleniumRequest 接受一些额外的参数,例如在返回响应之前等待的 wait_time,等待 HTML 元素的 wait_until,截取屏幕截图的屏幕截图和用于执行自定义 JavaScript 脚本的脚本。

在生产中,scrapy-selenium 的主要问题是没有简单的方法来设置Selenium 网格以在远程机器上运行多个浏览器实例。接下来,我将比较两种使用 Scrapy 大规模执行 JavaScript 的解决方案。

使用 Splash 在 Scrapy 中执行 JavaScript

Splash是一种带有 API 的 Web 浏览器即服务。它由 Scrapy 的主要贡献者 Scrapinghub 维护,并通过scrapy-splash中间件与 Scrapy 集成。它也可以由 Scrapinghub 托管。

Splash 创建于 2013 年,在无头 Chrome 和其他主要无头浏览器于 2017 年发布之前。从那时起,其他流行的项目(如 PhantomJS)已停止使用,转而支持 Firefox、Chrome 和 Safari 无头浏览器。

您可以使用 Docker 在本地运行 Splash 实例。

docker run -p 8050:8050 scrapinghub/splash`

配置 Splash 中间件需要添加多个中间件并在项目设置中更改 HttpCompressionMiddleware 的默认优先级。

SPLASH_URL = 'http://192.168.59.103:8050'


DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}


SPIDER_MIDDLEWARES = {
    'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}


DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

然后你可以产生一个带有可选参数 wait 和 lua_source 的 SplashRequest。

from scrapy_splash import SplashRequest

yield SplashRequest(url, callback=self.parse, args={
	'wait': 0.5,
    'lua_source': script
})

Splash 是一种流行的解决方案,因为它已经推出了很长时间,但它有两个主要问题:它使用自定义的无头浏览器,并且需要在 Lua 中编码才能与网站交互。由于这两个问题,在我的最后一个抓取项目中,我决定为 ScrapingBee API 创建一个中间件。

使用 ScrapingBee 在 Scrapy 中执行 JavaScript

ScrapingBee是一个 Web 抓取 API,可以为您处理无头浏览器和代理。ScrapingBee 使用最新的无头 Chrome 版本并支持 JavaScript 脚本。

与其他两个中间件一样,您可以简单地使用 pip 安装scrapy-scrapingbee中间件。

pip install scrapy-scrapingbee

首先,您需要创建一个 ScrapingBee 帐户以获取 API 密钥。然后你可以根据你的项目设置中的 ScrapingBee 计划添加下载器中间件并设置并发。

SCRAPINGBEE_API_KEY = 'REPLACE-WITH-YOUR-API-KEY'

DOWNLOADER_MIDDLEWARES = {
    'scrapy_scrapingbee.ScrapingBeeMiddleware': 725,
}

CONCURRENT_REQUESTS = 1

然后你可以从 ScrapingBeeSpider 继承你的蜘蛛并产生一个 ScrapingBeeRequest。

from scrapy_scrapingbee import ScrapingBeeSpider, ScrapingBeeRequest

class HttpbinSpider(ScrapingBeeSpider):
    name = 'httpbin'
    start_urls = [
        'https://httpbin.org',
    ]


    def start_requests(self):
        for url in self.start_urls:
            yield ScrapingBeeRequest(url)


    def parse(self, response):
        …

ScrapingBeeRequest 采用可选的 params 参数来执行 js_snippet,在返回响应之前设置自定义等待,或者使用 wait_for 在 HTML 代码中等待 CSS 或 XPATH 选择器。

在某些网站中,当您滚动浏览页面时,HTML 会异步加载。您可以使用下面的 JavaScript 片段滚动到页面末尾。

JS_SNIPPET = 'window.scrollTo(0, document.body.scrollHeight);'

yield ScrapingBeeRequest(url, params={
           'js_snippet': JS_SNIPPET,
           # 'wait': 3000,
           # 'wait_for': '#swagger-ui',
       })

ScrapingBee 收集了其他常见的 JavaScript 片段,以便与ScrapingBee 文档中的网站进行交互。

在幕后,scraping-scrapingbee 中间件将原始请求转换为转发到 ScrapingBee API 的请求,并对 URL 查询字符串中的每个参数进行编码。API 端点记录在您的 Scrapy 日志中,并且 api_key 被 ScrapingBeeSpider 隐藏。

2020-06-22 12:32:07 [scrapy.core.engine] DEBUG: Crawled (200)  (referer: None)

在您的蜘蛛的解析方法中,中间件将 response.url 解析为传递给 ScrapingBeeRequest 的原始 URL。

def parse(self, response):
    assert response.url == 'https://httpbin.org'

使用 ScrapingBee 的另一个优点是您可以使用以下参数访问不同国家的住宅代理和开箱即用的代理轮换。

yield ScrapingBeeRequest(url, params={
   'premium_proxy': True,
   'country_code': 'fr',
})

使用 Scrapy 缓存和并发来更快地抓取

Scrapy 在底层使用了 Twisted,这是一个异步网络框架。Twisted 使 Scrapy 快速并且能够同时抓取多个页面。但是,要执行 JavaScript 代码,您需要使用真正的浏览器或无头浏览器来解析请求。无头浏览器有两个挑战:它们速度较慢且难以扩展。

在无头浏览器中执行 JavaScript 并等待所有网络调用每页可能需要几秒钟。抓取多个页面时,它会使抓取器显着变慢。希望 Scrapy 提供缓存来加速生产运行的开发和并发请求。

在本地,在开发爬虫时,您可以使用 Scrapy 的内置缓存系统。由于响应存储在计算机上的隐藏文件夹 .scrapy/httpcache 中,它将使后续运行更快。您可以在项目设置中激活 HttpCacheMiddleware:

HTTPCACHE_ENABLED = True

无头浏览器的另一个问题是它们会为每个请求消耗内存。在生产环境中,您需要一个可以处理多个浏览器的环境。要同时发出多个请求,您可以修改项目设置:

CONCURRENT_REQUESTS = 1

使用 ScrapingBee 时,请记住根据您的 ScrapingBee 计划设置并发。

结论

我比较了三个使用 Scrapy 渲染和执行 JavaScript 的 Scrapy 中间件。Selenium 允许您在所有主要的无头浏览器中使用 Python 与 Web 浏览器进行交互,但可能难以扩展。Splash 可以使用 Docker 在本地运行或部署到 Scrapinghub,但依赖于自定义浏览器实现,您必须在 Lua 中编写脚本。ScrapingBee 使用最新的 Chrome 无头浏览器,允许您在 JavaScript 中执行自定义脚本,并为最难抓取的网站提供代理轮换。


scrapy-selenium

scrapy-splash

scrapy-scrapingbee

本地运行

是的

是的,使用 Docker

远程扩展

使用Selenium Grid

使用 Scrapinghub

使用ScrapingBee

脚本语言

JavaScript、Python

lua

JavaScript

浏览器支持

Chrome, Firefox, Edge, Safari

Splash

Latest Headless Chrome

代理轮换

由另一项服务 Crawlera 提供

是的,由相同的中间件提供

相关推荐

最强聚类模型,层次聚类 !!_层次聚类的优缺点

哈喽,我是小白~咱们今天聊聊层次聚类,这种聚类方法在后面的使用,也是非常频繁的~首先,聚类很好理解,聚类(Clustering)就是把一堆“东西”自动分组。这些“东西”可以是人、...

python决策树用于分类和回归问题实际应用案例

决策树(DecisionTrees)通过树状结构进行决策,在每个节点上根据特征进行分支。用于分类和回归问题。实际应用案例:预测一个顾客是否会流失。决策树是一种基于树状结构的机器学习算法,用于解决分类...

Python教程(四十五):推荐系统-个性化推荐算法

今日目标o理解推荐系统的基本概念和类型o掌握协同过滤算法(用户和物品)o学会基于内容的推荐方法o了解矩阵分解和深度学习推荐o掌握推荐系统评估和优化技术推荐系统概述推荐系统是信息过滤系统,用于...

简单学Python——NumPy库7——排序和去重

NumPy数组排序主要用sort方法,sort方法只能将数值按升充排列(可以用[::-1]的切片方式实现降序排序),并且不改变原数组。例如:importnumpyasnpa=np.array(...

PyTorch实战:TorchVision目标检测模型微调完

PyTorch实战:TorchVision目标检测模型微调完整教程一、什么是微调(Finetuning)?微调(Finetuning)是指在已经预训练好的模型基础上,使用自己的数据对模型进行进一步训练...

C4.5算法解释_简述c4.5算法的基本思想

C4.5算法是ID3算法的改进版,它在特征选择上采用了信息增益比来解决ID3算法对取值较多的特征有偏好的问题。C4.5算法也是一种用于决策树构建的算法,它同样基于信息熵的概念。C4.5算法的步骤如下:...

Python中的数据聚类及可视化分析实践

探索如何通过聚类分析揭露糖尿病预测数据集的特征!我们将运用Python的强力工具,深入挖掘数据,以直观的可视化揭示不同特征间的关系。一同探索聚类分析在糖尿病预测中的实践!所有这些可视化都可以通过数据操...

用Python来统计大乐透号码的概率分布

用Python来统计大乐透号码的概率分布,可以按照以下步骤进行:导入所需的库:使用Python中的numpy库生成数字序列,使用matplotlib库生成概率分布图。读取大乐透历史数据:从网络上找到大...

python:支持向量机监督学习算法用于二分类和多分类问题示例

监督学习-支持向量机(SVM)支持向量机(SupportVectorMachine,简称SVM)是一种常用的监督学习算法,用于解决分类和回归问题。SVM的目标是找到一个最优的超平面,将不同类别的...

25个例子学会Pandas Groupby 操作

groupby是Pandas在数据分析中最常用的函数之一。它用于根据给定列中的不同值对数据点(即行)进行分组,分组后的数据可以计算生成组的聚合值。如果我们有一个包含汽车品牌和价格信息的数据集,那么可以...

数据挖掘流程_数据挖掘流程主要有哪些步骤

数据挖掘流程1.了解需求,确认目标说一下几点思考方法:做什么?目的是什么?目标是什么?为什么要做?有什么价值和意义?如何去做?完整解决方案是什么?2.获取数据pandas读取数据pd.read.c...

使用Python寻找图像最常见的颜色_python 以图找图

如果我们知道图像或对象最常见的是哪种颜色,那么可以解决图像处理中的几个用例,例如在农业领域,我们可能需要确定水果的成熟度。我们可以简单地检查一下水果的颜色是否在预定的范围内,看看它是成熟的,腐烂的,还...

财务预算分析全网最佳实践:从每月分析到每天分析

原文链接如下:「链接」掌握本文的方法,你就掌握了企业预算精细化分析的能力,全网首发。数据模拟稍微有点问题,不要在意数据细节,先看下最终效果。在编制财务预算或业务预算的过程中,通常预算的所有数据都是按月...

常用数据工具去重方法_数据去重公式

在数据处理中,去除重复数据是确保数据质量和分析准确性的关键步骤。特别是在处理多列数据时,保留唯一值组合能够有效清理数据集,避免冗余信息对分析结果的干扰。不同的工具和编程语言提供了多种方法来实现多列去重...

Python教程(四十):PyTorch深度学习-动态计算图

今日目标o理解PyTorch的基本概念和动态计算图o掌握PyTorch张量操作和自动求导o学会构建神经网络模型o了解PyTorch的高级特性o掌握模型训练和部署PyTorch概述PyTorc...