转载自:https://webscrapingsite.com/zh-CN/resources/scrapy-splash-tutorial/
在过去的十年里,JavaScript 在网络上变得无处不在。 现在越来越多的网站依靠 JavaScript 在客户端而不是服务器端动态呈现内容。 这对网络爬虫提出了挑战。 Beautiful Soup 和 Scrapy 等传统工具只能抓取服务器提供的静态 HTML。 为了抓取动态 JavaScript 站点,我们需要一个无头浏览器。 这就是 Splash 的用武之地……
JavaScript Web 应用程序的兴起 多年来 JavaScript 的使用呈爆炸式增长:
现在 97% 的网站在客户端使用 JavaScript(W3Techs) 排名前 94 的网站中有 10,000% 使用 React、Angular 和 Vue 等 JavaScript 框架(应对) 这种转变是由前端 Web 框架(如 React、Angular 和 Vue)的流行推动的。 这些框架使用 JavaScript 在浏览器中动态呈现内容,而不是仅仅依赖服务器端模板。
抓取这些现代 JavaScript Web 应用程序需要浏览器来执行 JavaScript。 requests 和 Beautiful Soup 等工具在这方面存在不足。 相反,我们需要一个浏览器自动化工具,例如 Selenium、Playwright 或 Splash。
为什么 Splash 改变了游戏规则 Splash 是一种 JavaScript 渲染服务,具有用于控制浏览器的 HTTP API。 Splash 由 Scrapinghub 开发,与 Scrapy 完美集成,提供我们抓取动态 JavaScript 站点所需的浏览器自动化功能。
以下是 Splash 的一些主要优势:
无头浏览器 – Splash 利用 Chromium 浏览器中的 webkit,但无头运行,没有可见的 UI。 这使得它非常适合服务器端抓取。
快速轻巧 – Splash 消耗的 CPU 和内存资源比 Selenium 或 Puppeteer 少得多。 它专为大规模高性能而构建。
可编写脚本 – Lua 脚本可用于模拟复杂的用户交互,例如滚动、单击、表单提交等。
Scrapy集成 – Splash 中间件使其可以与 Scrapy 无缝使用。 感觉就像使用常规的 Scrapy 请求一样。
分布式爬取 – Splash 与 Scrapy 集群很好地配合进行分布式爬行。 水平缩放很容易。
总体而言,Splash 在快速、轻量级和可编写脚本的包中提供了 JavaScript 站点所需的动态渲染功能,可与 Scrapy 顺利集成以实现大规模分布式抓取。
安装启动画面 Splash 可作为 Docker 映像使用,使设置变得轻而易举。 它还可以在没有 Docker 的情况下安装在 Linux 和 macOS 上。
首先,确保您的系统上安装了 Docker。
然后从 Docker Hub 中拉取 Splash Docker 镜像:
docker pull scrapinghub/splash 下载镜像后,在端口 8050 上启动 Splash 实例:
docker run -p 8050:8050 scrapinghub/splash Splash 现在将在容器中运行并准备好进行抓取! Splash REPL 可在端口 8050 上进行测试。
将 Splash 与 Scrapy 集成 要从 Scrapy 蜘蛛调用 Splash,我们将使用 scrapy-splash 可以很好地处理集成的库。
首先安装Scrapy和scrapy-splash:
pip install scrapy scrapy-splash 接下来,启用 Splash 中间件和 dupefilter settings.py:
SPLASH_URL = ‘http://localhost:8050‘
DOWNLOADER_MIDDLEWARES = { ‘scrapy_splash.SplashCookiesMiddleware‘: 723, ‘scrapy_splash.SplashMiddleware‘: 725, }
DUPEFILTER_CLASS = ‘scrapy_splash.SplashAwareDupeFilter‘ 这将 Scrapy 配置为通过本地 Splash 实例发送请求。
使用 SplashRequest 进行抓取 为了将请求发送到 Splash 而不是直接抓取,Scrapy 提供了 SplashRequest 类。
from scrapy_splash import SplashRequest
def start_requests(self):
yield SplashRequest( url="http://quotes.toscrape.com", callback=self.parse, args={‘wait‘: 0.5}, )
def parse(self, response):
Extract quotes from response.body
args 参数允许将配置发送到 Splash,例如 wait 时间。
Splash 渲染 JavaScript 并将 HTML 结果返回给我们 parse 回调,我们可以像往常一样使用 Scrapy 选择器提取数据。
处理 JavaScript 驱动的分页 使用 Splash 的优点之一是它可以单击 JavaScript 处理的链接和按钮。 例如,让我们抓取像 Twitter 这样使用无限滚动分页的网站。
如果没有 Splash,我们将无法触发其他页面的加载。 但通过 Splash Lua 脚本,我们可以自动滚动以加载更多内容:
script = """ function main(splash) splash:go(splash.args.url)
while not splash:select(‘.loading‘) do
splash:runjs(‘window.scrollBy(0, 1000)‘)
splash:wait(1)
end
return splash:html()
end """
def start_requests(self):
yield SplashRequest( url="https://twitter.com/elonmusk", callback=self.parse, args={ ‘lua_source‘: script } )
def parse(self, response):
Extract Tweets
该脚本逐渐向下滚动,直到看不到加载指示器,从而展开暴露给 Scrapy 的页面片段。
使用 Splash 处理 reCAPTCHA 大量使用 JavaScript 的网站通常采用 reCAPTCHA 和其他反机器人措施。 Splash 允许通过自动化浏览器来解决这些挑战,从而绕过这些保护。
例如,我们可以构建一个脚本来自动单击 reCAPTCHA:
script = """ function main(splash)
-- Go to target url
splash:go(splash.args.url)
-- Wait for reCAPTCHA to load
splash:wait(5)
-- Click on reCAPTCHA checkbox
splash:runjs(‘document.getElementById("recaptcha-anchor").click()‘)
-- Wait for validation
splash:wait(10)
return splash:html()
end """ 这样,Splash 就可以通过阻碍其他抓取工具的初始 reCAPTCHA 门。 当然,机器人保护在不断发展,因此抓取工具需要不断维护和更新以适应。
调试技巧 以下是使用 Splash 调试 Scrapy 蜘蛛的一些技巧:
检查 Scrapy 日志中的 Splash 请求/响应信息 检查原始 HTML 响应 http://localhost:8050/render.html 启用详细的 Splash 登录 settings.py 使用浏览器开发工具解决 JS 问题 放慢速度 args={‘wait‘: 3} 隔离问题 设置启动参数 images=0 禁用资源加载 使用 splash:go() 在Lua脚本中重新启动渲染 捕获并处理常见的 SplashScriptError 异常 出现问题时密切监视日志有助于缩小问题范围。
生产最佳实践 大规模抓取 JavaScript 网站时,以下是一些提示:
使用 Oxylabs 等强大的代理轮换服务来避免 IP 封锁 在蜘蛛中实现 2-10 秒的随机延迟 将 scrapyd 工作人员分布在许多服务器上 使用 docker-compose 为 Splash 集群优化 Docker 设置 启用 Scrapy 缓存和持久性以减少 Splash 请求 监控性能瓶颈并相应地扩展资源 尽快避开爆破地点,以免被发现。 缓慢、稳定、分布式的抓取可以降低风险。
抓取复杂网站——GitHub 案例研究 让我们看一下抓取 GitHub 配置文件的示例,该示例严重依赖 JavaScript 进行导航和合并来自 API 调用的数据。
首先,一些示例资料页面:
https://github.com/scrapy https://github.com/tensorflow 蜘蛛代码:
import json from scrapy_splash import SplashRequest
class GithubSpider(scrapy.Spider):
Other spider code...
def start_requests(self):
profiles = [
‘https://github.com/scrapy/‘,
‘https://github.com/tensorflow‘
]
for url in profiles:
yield SplashRequest(url, self.parse, endpoint=‘render.html‘)
def parse(self, response):
# Extract profile info from HTML
yield {
‘name‘: response.css(‘name‘).get(),
‘bio‘: response.css(‘bio‘).get(),
# etc...
}
# Extract additional JSON data
json_data = json.loads(response.css(‘json-data‘).get())
yield {
‘public_repos‘: json_data[‘public_repos‘],
‘followers‘: json_data[‘followers‘]
}
关键点:
使用 render.html 而不是默认 execute 返回 HTML 的端点 GitHub 内联了我们可以直接解析的 JSON 数据 Lua 脚本还可以帮助抓取额外的页面 这演示了 Splash 如何提供渲染功能来处理复杂的、高度 JavaScript 驱动的网站。
总结 大量使用 JavaScript 的 Web 应用程序正在成为常态。 仅基于 BeautifulSoup 和 Requests 构建的爬虫不再能解决这个问题。 Splash 通过提供一个简单的 HTTP API 来控制无头浏览器渲染来弥补这一差距。
Splash 与 Scrapy 集成,可以大规模动态抓取复杂的 JavaScript Web 应用程序。 脚本等功能提供了模拟用户操作以进行分页和机器人缓解所需的控制。
为了处理下一代 Web 应用程序,每个抓取工具的工具包都需要像 Splash 这样的 JavaScript 渲染引擎。