返回博客
指南
米海·马克西姆2023年1月30日阅读时间:6分钟

如何使用 Scrapy 执行 JavaScript

如何使用 Scrapy 执行 JavaScript

导言

欢迎来到动态网站抓取的精彩世界!正如您可能从我们之前的文章中了解到的那样,使用传统的网页抓取工具浏览此类网站可能会有些棘手。但请放心!Scrapy 这款值得信赖的网页抓取助手将为您提供强力支持,它拥有各种插件和库,让动态网站抓取变得轻而易举。

在本文中,我们将深入探讨使用 Scrapy 抓取基于 JavaScript 的网站时,一些最常用的方法。为了让操作更加简单,我们将提供每种方法的使用示例,助您从容应对任何网站。

如果您是 Scrapy 的新手,请不必担心。您可以参考我们的指南,了解如何使用 Scrapy 进行网页抓取。

无头浏览器?

如果你对无头浏览器还不熟悉,让我来给你解释一下。简而言之,这是一种没有可见界面的网页浏览器。是的,我知道在使用时看不到浏览器窗口听起来很奇怪。但相信我,在网页抓取方面,无头浏览器确实能带来革命性的改变。

原因如下:与仅能显示网页的普通浏览器不同,无头浏览器能够执行 JavaScript。这意味着,如果你需要抓取一个依赖 JavaScript 生成内容的网站,无头浏览器可以通过执行 JavaScript 并让你抓取生成的 HTML 内容来提供帮助。

探索不同的解决方案

在 Scrapy 中渲染 JavaScript 的最佳策略取决于您的具体需求和资源。如果预算紧张,您可能需要选择一种性价比高的解决方案。使用无头浏览器或 JavaScript 渲染库可能是成本最低的选择,但您仍需应对 IP 被封禁的风险,以及维护和运行该解决方案所产生的成本。

最好还是尝试几种不同的方案,看看哪一种最适合您的具体情况。

如何使用 Splash 在 Scrapy 中执行 JavaScript

Splash 是一款专为网络爬虫设计的轻量级无头浏览器。它基于 WebKit 引擎,该引擎也是 Safari 浏览器的核心引擎。Splash 的最大优势在于配置简便,尤其是配合 Docker 使用时。此外,它还通过 scrapy-splash 中间件与 Scrapy 实现了集成。

要使用该中间件,您首先需要使用 pip 安装此包:

$ pip install scrapy-splash

使用 Docker 配置 Splash 非常简单。您只需在本地机器上使用 Docker 运行一个 Splash 实例即可(https://docs.docker.com/get-docker/)。

$ docker run -p 8050:8050 scrapinghub/splash

之后,您应该能够通过http://localhost:8050/访问本地 Splash 实例

一个展示 JavaScript 渲染服务的 Splash 网页界面,包含代码编辑器面板和“渲染”按钮

Splash 提供了一个 REST API,使其能够轻松与 Scrapy 或任何其他网络爬虫工具配合使用。您可以在 Scrapy 控制台中发送一个 GET 请求来测试服务器:

fetch('http://localhost:8050/render.html?url=<target_url>')

要配置中间件,请在 settings.py 文件中添加以下几行代码。

SPLASH_URL = 'http://localhost: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'

请访问https://github.com/scrapy-plugins/scrapy-splash,了解有关各项设置的更多信息。

使用 Splash 处理请求的最简单方法是在蜘蛛内部使用 scrapy_splash.SplashRequest:

import scrapy

from scrapy_splash import SplashRequest

class RandomSpider(scrapy.Spider):

    name = 'random_spider'

    def start_requests(self):

        start_urls = [

        '<first_url',

        '<second_url>'

       ]

        for url in start_urls:

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

    def parse(self, response):

       

            result = response.css("h3::text").extract()

         

            yield result

您可以添加一个“wait”参数,用于指定 Splash 在返回请求前需要等待的时间。

使用 Splash 的一个潜在缺点是,它需要借助 Lua 脚本语言来执行诸如点击按钮、填写表单和跳转页面等操作。

如何使用 Selenium 通过 Scrapy 执行 JavaScript

您可以将 Scrapy 与 Selenium WebDriver 结合使用。scrapy-selenium 中间件的工作原理是将 Selenium WebDriver 注入请求流程,从而将生成的 HTML 返回给爬虫进行解析。

在实施此解决方案之前,请务必注意:您需要安装一个 Web 驱动程序才能与浏览器进行交互。例如,若要使用 Selenium 控制 Firefox,您需要安装 geckodriver。安装好 Web 驱动程序后,您就可以在 Scrapy 项目设置中配置 Selenium:

SELENIUM_DRIVER_NAME = 'firefox'

SELENIUM_DRIVER_EXECUTABLE_PATH = which('geckodriver')

SELENIUM_DRIVER_ARGUMENTS=['-headless']  # '--headless' if using chrome instead of firefox

DOWNLOADER_MIDDLEWARES = {

    'scrapy_selenium.SeleniumMiddleware': 800

}

ITEM_PIPELINES = {

    'myproject.pipelines.SanitizePipeline': 1,

}

然后,您可以配置您的爬虫:

import scrapy

from scrapy_selenium import SeleniumRequest

class RandomSpider(scrapy.Spider):

    name = 'random_spider'

    def start_requests(self):

        start_urls = [

        '<first_url',

        '<second_url>'

       ]

        for url in start_urls:

            yield SeleniumRequest(url=url, callback=self.parse)

    def parse(self, response):

            print(response.request.meta['driver'].title)

            #The request will be handled by selenium, and the request will have an additional meta key, named driver containing the selenium driver with the request processed.

       

            result = response.selector.css("#result::text").extract()

            #The selector response attribute work as usual (but contains the  html processed by the selenium driver).

         

            yield result

有关可用驱动程序方法和属性的更多信息,请参阅 Selenium Python 文档:

http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webdriver

由于 Selenium 并非独立的无头浏览器,因此运行它的机器上必须安装网页浏览器。这使得在多台机器或云环境中部署和运行 Selenium 变得更加困难。

如何使用 WebScrapingApi 在 Scrapy 中执行 JavaScript

WebScrapingAPI 提供了一个 API,它将为您处理所有繁重的工作。它能够执行 JavaScript、轮换代理,甚至处理验证码,确保您能够轻松地抓取网站。 此外,您再也不必担心因发送过多请求而导致 IP 被封禁。为了让 Scrappy 与 WebScrapingAPI 协同工作,我们将配置一个代理中间件,将所有 fetch 请求通过 WSA 进行隧道传输。

为此,我们将配置 Scrapy 以连接到 WSA 代理服务器:

import base64

# add this to your middlewares.py file

class WSAProxyMiddleware:

    def process_request(self, request, spider):

        # Set the proxy for the request

        request.meta['proxy'] = "http://proxy.webscrapingapi.com:80"

        request.meta['verify'] = False

        # Set the proxy authentication for the request

        proxy_user_pass = "webscrapingapi.render_js=1:<API_KEY>"

        encoded_user_pass = base64.b64encode(proxy_user_pass.encode()).decode()

        request.headers['Proxy-Authorization'] = f'Basic {encoded_user_pass}'

并启用中间件:

DOWNLOADER_MIDDLEWARES = {

    'myproject.middlewares.WSAProxyMiddleware': 1,

}

webscrapingapi.render_js=1 is the proxy authentication username, <API_KEY> the password.

您可以在https://www.webscrapingapi.com/上创建新账户,免费获取 API_KEY

通过指定 render_js=1 参数,您将启用 WebScrapingAPI 使用无头浏览器访问目标网页的功能,该功能允许在将最终刮擦结果返回给您之前渲染 JavaScript 页面元素。

您还可以指示 WSA 在处理您的 URL 时执行特定操作。您可以通过指定 js_instructions 参数来实现:

js_instructions=[

{"action":"click","selector":"button#log-in-button"}

]

// this sequence could be used to click on a button

就这样,WSA 现在会自动为您处理所有请求。

总结

抓取动态网站可能是一项艰巨的任务,但只要使用合适的工具,就会变得容易得多。在本文中,我们探讨了使用 Scrapy 抓取动态网站的三种不同方案。像 Splash 和 Selenium 这样的无头浏览器,可以让你像普通浏览器一样执行 JavaScript 并渲染网页。 不过,如果您希望走捷径,使用 WebScrapingApi 这样的 API 也是一个绝佳的解决方案。它能为您处理所有复杂任务,让您即使从最难抓取的网站也能轻松提取数据。无论您选择哪种方案,关键都要根据具体需求,选择最适合您项目的解决方案。感谢阅读,祝您抓取顺利!

关于作者
米海·马克西姆,WebScrapingAPI 全栈开发工程师
米哈伊-马克西姆全栈开发工程师

米海·马克西姆(Mihai Maxim)是 WebScrapingAPI 的全栈开发工程师,他在产品各领域均有贡献,并协助为该平台构建可靠的工具和功能。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网页数据。