免费编程软件「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一键部署(推荐)
python
docker pull scrapinghub/splash
docker run -d -p 8050:8050 scrapinghub/splash
三行命令就能在本地启动服务,访问http://localhost:8050 看到控制台界面。这种方式隔离性好,版本兼容问题少,特别适合开发测试。
方案2:手动安装(Linux环境)
python
# 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
python
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
python
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脚本:
python
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模拟浏览器行为。
完整代码
python
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'])}")
关键点解析
- 滚动加载处理 :通过循环执行
scrollTo和wait模拟用户滚动行为 - 元素选择:使用CSS选择器精准定位元素,比正则表达式更可靠
- 数据提取:直接在Lua脚本里处理JS数组,减少Python端的数据清洗工作
五、性能优化技巧
1. 代理池配置
python
params = {
"url": target_url,
"proxy": "http://proxy-ip:port", # 配置代理
"wait": 2
}
对于大规模爬取,建议:
- 使用住宅代理(如BrightData、Smartproxy)
- 每请求更换IP
- 配合User-Agent轮换
2. 缓存策略
python
from functools import lru_cache
@lru_cache(maxsize=100)
def get_splash_result(url):
# 调用Splash的逻辑
pass
对相同URL的请求可以缓存渲染结果,但要注意:
- 设置合理的过期时间(如10分钟)
- 区分不同参数的URL
- 缓存大小不宜过大
3. 异步处理
python
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:通常是请求超时或资源过大。检查:
- 增加
timeout和resource_timeout参数 - 降低
wait时间避免长时间占用 - 检查目标网站是否对Splash的User-Agent有特殊限制
Q3:如何处理登录状态?
A:两种方案:
-
在Lua脚本中使用
splash:set_cookies()预先设置cookie -
先访问登录页获取session,再携带cookie访问目标页
python-- 示例:携带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启动参数:
python
docker run -d -p 8050:8050 \
-e SPLASH_MEMORY_LIMIT=2048 \ # 限制内存
-e SPLASH_SLOTS=5 \ # 并发槽位数
scrapinghub/splash
或优化Lua脚本,避免长时间运行。
Q5:如何处理HTTPS证书错误?
A:在Lua脚本中添加:
python
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渲染场景。实际项目中建议:
- 优先使用Docker部署,保持环境隔离
- 对关键页面使用显式等待而非固定等待
- 建立完善的错误处理和重试机制
- 定期监控Splash服务性能指标
随着前端技术的演进,像Splash这样的工具会越来越重要。掌握它不仅能解决当前问题,更能为未来更复杂的爬取需求打下基础。