导言
欢迎来到动态网站抓取的精彩世界!正如您可能从我们之前的文章中了解到的那样,使用传统的网页抓取工具浏览此类网站可能会有些棘手。但请放心!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 实例

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 也是一个绝佳的解决方案。它能为您处理所有复杂任务,让您即使从最难抓取的网站也能轻松提取数据。无论您选择哪种方案,关键都要根据具体需求,选择最适合您项目的解决方案。感谢阅读,祝您抓取顺利!




