随着Web3.0时代的到来,主流网站采用三大动态加载技术提升用户体验:
传统动态渲染方案存在明显瓶颈:
创新架构:
特性 | Selenium | Splash | 组合方案 |
---|---|---|---|
渲染方式 | 真实浏览器 | WebKit内核 | 智能分流 |
执行速度 | 较慢(完整浏览器启动) | 快(无GUI渲染) | 动态平衡 |
内存占用 | 500MB+/实例 | 80MB/实例 | 资源池优化 |
交互能力 | 支持全类型事件 | 基础事件支持 | 优势互补 |
并发能力 | 低(受硬件限制) | 高(Docker集群) | 弹性扩展 |
# 单节点部署
docker run -d -p 8050:8050 scrapinghub/splash
# 集群部署(3节点)
docker run -d -p 8050:8050 --name splash1 scrapinghub/splash
docker run -d -p 8051:8050 --name splash2 scrapinghub/splash
docker run -d -p 8052:8050 --name splash3 scrapinghub/splash
# 安装WebDriver管理器
pip install webdriver-manager
# 自动管理浏览器驱动
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
class RenderMiddleware:
def process_request(self, request, spider):
# 需要复杂交互的页面
if request.meta.get('need_full_interaction'):
return self.selenium_render(request)
# 常规动态页面
else:
return self.splash_render(request)
def selenium_render(self, request):
driver = get_from_browser_pool() # 从浏览器池获取实例
driver.get(request.url)
# 执行滚动操作
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
# 处理模态弹窗
try:
driver.switch_to.alert.accept()
except NoAlertPresentException:
pass
html = driver.page_source
release_browser(driver) # 释放回资源池
return HtmlResponse(url=request.url, body=html, encoding='utf-8')
def splash_render(self, request):
lua_script = """
function main(splash)
splash:set_user_agent('Mozilla/5.0...')
splash:go(splash.args.url)
splash:wait(2)
splash:runjs("document.querySelector('button.load-more').click()")
splash:wait(3)
return splash:html()
end
"""
return SplashRequest(
request.url,
endpoint='execute',
args={'lua_source': lua_script},
cache_args=['lua_source']
)
function main(splash)
-- 设置自定义HTTP头
splash:set_custom_headers({
["X-Requested-With"] = "XMLHttpRequest"
})
-- 执行页面跳转
splash:go("https://example.com")
-- 处理Cookie
splash:init_cookies(splash.args.cookies)
-- 执行JavaScript交互
splash:runjs([[
document.querySelector('#search_input').value = 'Python书籍';
document.querySelector('#search_btn').click();
]])
-- 等待元素加载(智能等待)
splash:wait_for_resume([[
function main(splash) {
var checkExist = setInterval(function() {
if(document.querySelector('.result-item')) {
clearInterval(checkExist);
splash.resume();
}
}, 500);
}
]], 10) -- 超时10秒
-- 返回多类型数据
return {
html = splash:html(),
png = splash:png(),
cookies = splash:get_cookies()
}
end
from concurrent.futures import ThreadPoolExecutor
from selenium.webdriver import ChromeOptions
class BrowserPool:
def __init__(self, size=5):
self._pool = []
options = ChromeOptions()
options.add_argument("--headless")
for _ in range(size):
driver = webdriver.Chrome(options=options)
self._pool.append(driver)
def get_driver(self):
return self._pool.pop() if self._pool else None
def release_driver(self, driver):
driver.get("about:blank") # 清理历史记录
self._pool.append(driver)
import asyncio
from splash_async import SplashClient
async def async_render(url):
async with SplashClient('http://localhost:8050') as client:
response = await client.render_html(url, timeout=60)
return response.content
# 在Scrapy中间件中使用
html = await asyncio.to_thread(async_render, request.url)
class EcommerceSpider(scrapy.Spider):
name = "global_shop"
def start_requests(self):
# 使用Selenium执行登录
yield SeleniumRequest(
url="https://example.com/login",
callback=self.handle_login,
script="""
document.getElementById('username').value = 'user123';
document.getElementById('password').value = 'pass456';
document.querySelector('button[type=submit]').click();
"""
)
def handle_login(self, response):
# 获取登录后的Cookies
cookies = response.driver.get_cookies()
# 使用Splash抓取列表页
yield SplashRequest(
url="https://example.com/products",
args={'lua_source': scroll_script},
cookies=cookies
)
def parse_products(self, response):
# 解析Splash返回的HTML
products = response.css('.product-card')
for product in products:
yield {
"name": product.css('h3::text').get(),
"price": product.attrib['data-price']
}
场景 | 纯Selenium | 纯Splash | 混合方案 |
---|---|---|---|
登录验证流程 | 8.2s | 失败 | 9.1s |
无限滚动加载 | 14s | 6s | 7s |
实时价格监控 | 不支持 | 3s | 3s |
Python爬虫介绍 | Python爬虫(1)Python爬虫:从原理到实战,一文掌握数据采集核心技术 |
HTTP协议解析 | Python爬虫(2)Python爬虫入门:从HTTP协议解析到豆瓣电影数据抓取实战 |
HTML核心技巧 | Python爬虫(3)HTML核心技巧:从零掌握class与id选择器,精准定位网页元素 |
CSS核心机制 | Python爬虫(4)CSS核心机制:全面解析选择器分类、用法与实战应用 |
静态页面抓取实战 | Python爬虫(5)静态页面抓取实战:requests库请求头配置与反反爬策略详解 |
静态页面解析实战 | Python爬虫(6)静态页面解析实战:BeautifulSoup与lxml(XPath)高效提取数据指南 |
Python数据存储实战 CSV文件 | Python爬虫(7)Python数据存储实战:CSV文件读写与复杂数据处理指南 |
Python数据存储实战 JSON文件 | Python爬虫(8)Python数据存储实战:JSON文件读写与复杂结构化数据处理指南 |
Python数据存储实战 MySQL数据库 | Python爬虫(9)Python数据存储实战:基于pymysql的MySQL数据库操作详解 |
Python数据存储实战 MongoDB数据库 | Python爬虫(10)Python数据存储实战:基于pymongo的MongoDB开发深度指南 |
Python数据存储实战 NoSQL数据库 | Python爬虫(11)Python数据存储实战:深入解析NoSQL数据库的核心应用与实战 |
Python爬虫数据存储必备技能:JSON Schema校验 | Python爬虫(12)Python爬虫数据存储必备技能:JSON Schema校验实战与数据质量守护 |
Python爬虫数据安全存储指南:AES加密 | Python爬虫(13)数据安全存储指南:AES加密实战与敏感数据防护策略 |
Python爬虫数据存储新范式:云原生NoSQL服务 | Python爬虫(14)Python爬虫数据存储新范式:云原生NoSQL服务实战与运维成本革命 |
Python爬虫数据存储新维度:AI驱动的数据库自治 | Python爬虫(15)Python爬虫数据存储新维度:AI驱动的数据库自治与智能优化实战 |
Python爬虫数据存储新维度:Redis Edge近端计算赋能 | Python爬虫(16)Python爬虫数据存储新维度:Redis Edge近端计算赋能实时数据处理革命 |
反爬攻防战:随机请求头实战指南 | Python爬虫(17)反爬攻防战:随机请求头实战指南(fake_useragent库深度解析) |
反爬攻防战:动态IP池构建与代理IP | Python爬虫(18)反爬攻防战:动态IP池构建与代理IP实战指南(突破95%反爬封禁率) |
Python爬虫破局动态页面:全链路解析 | Python爬虫(19)Python爬虫破局动态页面:逆向工程与无头浏览器全链路解析(从原理到企业级实战) |
Python爬虫数据存储技巧:二进制格式性能优化 | Python爬虫(20)Python爬虫数据存储技巧:二进制格式(Pickle/Parquet)性能优化实战 |
Python爬虫进阶:Selenium自动化处理动态页面 | Python爬虫(21)Python爬虫进阶:Selenium自动化处理动态页面实战解析 |