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

如何使用 Scrapy 执行 JavaScript

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

大多数现代网站都使用客户端 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 提供

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

相关推荐

MySQL修改密码_mysql怎么改密码忘了怎么办

拥有原来的用户名账户的密码mysqladmin-uroot-ppassword"test123"Enterpassword:【输入原来的密码】忘记原来root密码第一...

数据库密码配置项都不加密?心也太大了吧!

先看一份典型的配置文件...省略...##配置MySQL数据库连接spring.datasource.driver-class-name=com.mysql.jdbc.Driverspr...

Linux基础知识_linux基础入门知识

系统目录结构/bin:命令和应用程序。/boot:这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。/dev:dev是Device(设备)的缩写,该目录...

MySQL密码重置_mysql密码重置教程

之前由于修改MySQL加密模式为mysql_native_password时操作失误,导致无法登陆MySQL数据库,后来摸索了一下,对MySQL数据库密码进行重置后顺利解决,步骤如下:1.先停止MyS...

Mysql8忘记密码/重置密码_mysql密码忘了怎么办?

Mysql8忘记密码/重置密码UBUNTU下Mysql8忘记密码/重置密码步骤如下:先说下大概步骤:修改配置文件,使得用空密码可以进入mysql。然后置当前root用户为空密码。再次修改配置文件,不能...

MySQL忘记密码怎么办?Windows环境下MySQL密码重置图文教程

有不少小白在使用Windows进行搭建主机的时候,安装了一些环境后,其中有MySQL设置后,然后不少马大哈忘记了MySQL的密码,导致在一些程序安装及配置的时候无法进行。这个时候怎么办呢?重置密码呗?...

10种常见的MySQL错误,你可中招?_mysql常见错误提示及解决方法

【51CTO.com快译】如果未能对MySQL8进行恰当的配置,您非但可能遇到无法顺利访问、或调用MySQL的窘境,而且还可能给真实的应用生产环境带来巨大的影响。本文列举了十种MySQL...

Mysql解压版安装过程_mysql解压版安装步骤

Mysql是目前软件开发中使用最多的关系型数据库,具体安装步骤如下:第一步:Mysql官网下载最新版(mysql解压版(mysql-5.7.17-winx64)),Mysql官方下载地址为:https...

MySQL Root密码重置指南:Windows新手友好教程

如果你忘记了MySQLroot密码,请按照以下简单步骤进行重置。你需要准备的工具:已安装的MySQL以管理员身份访问命令提示符一点复制粘贴的能力分步操作指南1.创建密码重置文件以管理员...

安卓手机基于python3搜索引擎_python调用安卓so库

环境:安卓手机手机品牌:vivox9s4G运行内存手机软件:utermux环境安装:1.java环境的安装2.redis环境的安装aptinstallredis3.elasticsearch环...

Python 包管理 3 - poetry_python community包

Poetry是一款现代化的Python依赖管理和打包工具。它通过一个pyproject.toml文件来统一管理你的项目依赖、配置和元数据,并用一个poetry.lock文件来锁定所有依赖的精...

Python web在线服务生产环境真实部署方案,可直接用

各位志同道合的朋友大家好,我是一个一直在一线互联网踩坑十余年的编码爱好者,现在将我们的各种经验以及架构实战分享出来,如果大家喜欢,就关注我,一起将技术学深学透,我会每一篇分享结束都会预告下一专题最近经...

官方玩梗:Python 3.14(πthon)稳定版发布,正式支持自由线程

IT之家10月7日消息,当地时间10月7日,Python软件基金会宣布Python3.14.0正式发布,也就是用户期待已久的圆周率(约3.14)版本,再加上谐音梗可戏称为π...

第一篇:如何使用 uv 创建 Python 虚拟环境

想象一下,你有一个使用Python3.10的后端应用程序,系统全局安装了a2.1、b2.2和c2.3这些包。一切运行正常,直到你开始一个新项目,它也使用Python3.10,但需要...

我用 Python 写了个自动整理下载目录的工具

经常用电脑的一定会遇到这种情况:每天我们都在从浏览器、微信、钉钉里下各种文件,什么截图、合同、安装包、临时文档,全都堆在下载文件夹里。起初还想着“过两天再整理”,结果一放就是好几年。结果某天想找一个发...