news 2026/6/24 16:44:58

实战:用Splash搞定JavaScript密集型网页渲染

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
实战:用Splash搞定JavaScript密集型网页渲染

免费编程软件「python+pycharm」
链接:https://pan.quark.cn/s/48a86be2fdc0

一、为什么需要Splash?

传统爬虫遇到动态网页时总会抓狂。明明URL能打开,但爬下来的页面全是空白或乱码——这是因为现代网站大量使用JavaScript动态加载内容,像React、Vue这类前端框架更是让DOM结构在客户端"凭空生成"。

举个真实案例:某电商网站的商品列表页,用requests库获取的HTML只有200多行,但浏览器实际渲染后超过5000行。关键数据都藏在<script>标签的JSON里,或者通过AJAX异步加载。这时候普通爬虫就像拿到一张藏宝图却看不懂符号。

Splash就是解决这个痛点的瑞士军刀。这个由Scrapinghub开发的轻量级浏览器,能像真实用户一样执行JavaScript,返回渲染后的完整HTML。更棒的是它提供了HTTP API接口,可以无缝集成到Python爬虫中。

二、快速安装部署

方案1:Docker一键部署(推荐)

docker pull scrapinghub/splash docker run -d -p 8050:8050 scrapinghub/splash

三行命令就能在本地启动服务,访问http://localhost:8050 看到控制台界面。这种方式隔离性好,版本兼容问题少,特别适合开发测试。

方案2:手动安装(Linux环境)

# Ubuntu示例 sudo apt-get install qt5-default qttools5-dev-tools libqt5webkit5-dev \ python3-dev python3-pip xvfb pip3 install splash

需要安装Qt依赖库和X虚拟帧缓冲,适合生产环境部署。注意要配置xvfb-run避免图形界面弹窗。

三、核心API实战

基础渲染:render.html

import requests url = "https://example.com/dynamic-page" splash_url = "http://localhost:8050/render.html" params = { "url": url, "wait": 3, # 等待3秒确保JS执行完成 "timeout": 30, # 超时时间 "resource_timeout": 10, # 资源加载超时 } response = requests.get(splash_url, params=params) with open("rendered.html", "w", encoding="utf-8") as f: f.write(response.text)

这个最简单的接口能返回渲染后的HTML。关键参数wait控制等待时间,对SPA(单页应用)尤其重要,建议设置2-5秒。

截图功能:render.png

params = { "url": url, "wait": 2, "width": 1920, "height": 1080, "render_all": True # 滚动到页面底部截图 } response = requests.get("http://localhost:8050/render.png", params=params) with open("screenshot.png", "wb") as f: f.write(response.content)

做数据验证时特别有用,比如检查页面布局是否正确,广告位是否加载。render_all参数能处理长页面截图。

高级控制:Lua脚本

当需要精细控制渲染过程时,可以写Lua脚本:

lua_script = """ function main(splash, args) splash:go(args.url) splash:wait(2) local title = splash:evaljs("document.title") local scroll_position = splash:jsfunc("window.scrollY")() return { title = title, scroll = scroll_position, html = splash:html() } end """ params = {"url": url, "lua_source": lua_script} response = requests.post("http://localhost:8050/execute", json=params)

通过evaljs可以直接执行任意JS代码,获取动态计算的值。jsfunc能把JS函数转为Lua可调用的形式。

四、实战案例:爬取某新闻网站

需求分析

目标网站使用React构建,文章内容通过AJAX分页加载,评论区需要滚动触发无限加载。直接请求API接口有反爬机制,决定用Splash模拟浏览器行为。

完整代码

import requests from urllib.parse import urljoin BASE_URL = "https://news.example.com" SPLASH_URL = "http://localhost:8050/execute" def get_article_links(category_url): params = { "url": category_url, "wait": 2, "lua_source": """ function main(splash, args) splash:go(args.url) splash:wait(1.5) local links = {} for _, link in ipairs(splash:select_all("a.article-link")) do table.insert(links, link.node.attributes.href) end return links end """ } resp = requests.post(SPLASH_URL, json=params).json() return [urljoin(BASE_URL, url) for url in resp] def get_article_content(article_url): params = { "url": article_url, "wait": 3, "resource_timeout": 15, "lua_source": """ function main(splash, args) splash:go(args.url) splash:wait(2) -- 滚动加载评论 for i = 1, 5 do splash:runjs("window.scrollTo(0, document.body.scrollHeight)") splash:wait(1) end return { title = splash:evaljs("document.querySelector('h1').innerText"), content = splash:evaljs("document.querySelector('.article-content').innerHTML"), comments = splash:evaljs( "Array.from(document.querySelectorAll('.comment-text')).map(el => el.innerText)" ) } end """ } return requests.post(SPLASH_URL, json=params).json() # 使用示例 links = get_article_links(BASE_URL + "/category/tech") for link in links[:3]: # 只处理前3篇测试 data = get_article_content(link) print(f"标题: {data['title']}") print(f"评论数: {len(data['comments'])}")

关键点解析

  1. 滚动加载处理:通过循环执行scrollTowait模拟用户滚动行为
  2. 元素选择:使用CSS选择器精准定位元素,比正则表达式更可靠
  3. 数据提取:直接在Lua脚本里处理JS数组,减少Python端的数据清洗工作

五、性能优化技巧

1. 代理池配置

params = { "url": target_url, "proxy": "http://proxy-ip:port", # 配置代理 "wait": 2 }

对于大规模爬取,建议:

  • 使用住宅代理(如BrightData、Smartproxy)
  • 每请求更换IP
  • 配合User-Agent轮换

2. 缓存策略

from functools import lru_cache @lru_cache(maxsize=100) def get_splash_result(url): # 调用Splash的逻辑 pass

对相同URL的请求可以缓存渲染结果,但要注意:

  • 设置合理的过期时间(如10分钟)
  • 区分不同参数的URL
  • 缓存大小不宜过大

3. 异步处理

import asyncio import aiohttp async def fetch_with_splash(url): async with aiohttp.ClientSession() as session: params = {"url": url, "wait": 2} async with session.get("http://splash:8050/render.html", params=params) as resp: return await resp.text() # 并发处理多个URL urls = [...] results = await asyncio.gather(*[fetch_with_splash(u) for u in urls])

使用asyncio可以显著提升爬取速度,实测比同步方式快3-5倍。

六、常见问题Q&A

Q1:被网站封IP怎么办?
A:立即启用备用代理池,建议使用住宅代理(如站大爷IP代理),配合每请求更换IP策略。可以在Splash参数中添加proxy字段,或通过中间件统一处理。

Q2:Splash返回502错误?
A:通常是请求超时或资源过大。检查:

  • 增加timeoutresource_timeout参数
  • 降低wait时间避免长时间占用
  • 检查目标网站是否对Splash的User-Agent有特殊限制

Q3:如何处理登录状态?
A:两种方案:

  1. 在Lua脚本中使用splash:set_cookies()预先设置cookie
  2. 先访问登录页获取session,再携带cookie访问目标页
    -- 示例:携带cookie访问 function main(splash) splash:init_cookies() splash:set_cookie("sessionid", "abc123", "/", {domain="example.com"}) splash:go("https://example.com/dashboard") return splash:html() end

Q4:内存占用过高怎么解决?
A:调整Splash启动参数:

docker run -d -p 8050:8050 \ -e SPLASH_MEMORY_LIMIT=2048 \ # 限制内存 -e SPLASH_SLOTS=5 \ # 并发槽位数 scrapinghub/splash

或优化Lua脚本,避免长时间运行。

Q5:如何处理HTTPS证书错误?
A:在Lua脚本中添加:

splash:set_custom_headers({ ["Accept-Encoding"] = "gzip, deflate", ["User-Agent"] = "Mozilla/5.0..." }) splash:on_request(function(request) request.opts.verify_cert = false -- 跳过证书验证(不推荐生产环境使用) end)

更安全的方式是导入正确的CA证书。

七、总结

Splash为动态网页爬取提供了强大而灵活的解决方案。通过合理配置代理、优化等待时间、善用Lua脚本,可以应对90%以上的JavaScript渲染场景。实际项目中建议:

  1. 优先使用Docker部署,保持环境隔离
  2. 对关键页面使用显式等待而非固定等待
  3. 建立完善的错误处理和重试机制
  4. 定期监控Splash服务性能指标

随着前端技术的演进,像Splash这样的工具会越来越重要。掌握它不仅能解决当前问题,更能为未来更复杂的爬取需求打下基础。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 18:51:43

金融行业广告投放:在合规的赛道上,实现精准增长

在金融行业做营销&#xff0c;您是否常常感觉在“走钢丝”&#xff1f;一边是严苛的监管红线和平台审核规则&#xff0c;稍有不慎便面临素材驳回、账户封禁&#xff1b;另一边是激烈的市场竞争&#xff0c;需要高效触达高意向客户&#xff0c;并建立至关重要的信任。传统的广撒…

作者头像 李华
网站建设 2026/6/24 16:39:46

长安汽车11月销量28.3万辆,同比增长2.3%

长安汽车2025年11月销量快报发布&#xff0c;全月总计销量28.3万辆&#xff0c;对比去年同期的27.7万辆&#xff0c;同比增长2.3%。新能源与海外市场成为增长亮点。新能源领域单月销量12.5万辆&#xff0c;同比增长23%&#xff0c;并实现连续3个月销量破10万辆的佳绩。阿维塔销…

作者头像 李华
网站建设 2026/6/25 0:58:17

1688 商品详情接口深度解析:从百川签名突破到供应链数据重构

一、接口核心机制与 B 端风控体系拆解 1688 商品详情接口&#xff08;核心接口alibaba.item.get&#xff0c;基于阿里百川开放平台架构&#xff09;作为 B2B 电商供应链数据核心入口&#xff0c;采用「百川签名验证 商家等级权限校验 IP 白名单绑定」的三重防护架构&#xf…

作者头像 李华
网站建设 2026/6/23 19:21:12

LobeChat心理情绪日记分析工具

LobeChat心理情绪日记分析工具 在数字时代&#xff0c;人们越来越依赖技术来管理生活、提升效率。但与此同时&#xff0c;心理健康问题也日益凸显——压力、焦虑、孤独感成为现代人的共同挑战。传统的纸质日记或简单的笔记应用虽然能帮助记录情绪&#xff0c;却缺乏互动性与洞…

作者头像 李华
网站建设 2026/6/24 9:53:15

一文搞懂纸老虎-布隆过滤器

在工程里&#xff0c;我们经常遇到一种很现实的需求&#xff1a;我只想快速判断某个值“在不在集合里”。 最好别占太多内存&#xff0c;速度还要快。如果你把所有元素都放进 HashSet 或数据库索引里&#xff0c;当然能做到“准确判断”&#xff0c;但代价可能是&#xff1a;内…

作者头像 李华
网站建设 2026/6/23 20:42:02

LobeChat周年庆感恩回馈活动

LobeChat&#xff1a;构建下一代开源AI对话门户的技术实践 在大语言模型&#xff08;LLM&#xff09;席卷全球的今天&#xff0c;几乎每个人都体验过与AI“聊天”的奇妙感受。从最初的GPT-3到如今动辄千亿参数的超大规模模型&#xff0c;技术演进的速度令人惊叹。但当我们真正想…

作者头像 李华