如何实现URL截图与SSL证书查询的异步展示?async/await尝试未果
解决方案思路
核心是把「获取SSL证书信息」和「生成页面截图」拆分为两个独立任务:
- 同步快速返回SSL过期时间与基础页面框架
- 后台异步执行Selenium截图任务,完成后自动更新前端显示
一、后端架构调整(PHP + Python 协同)
1. 拆分任务流程
用户提交URL后,PHP先同步完成快速的SSL信息查询,立即返回前端页面;同时启动后台异步进程,调用Python脚本处理耗时的截图任务,彻底脱离主请求流程。
2. PHP端实现后台任务
方式一:用exec启动独立后台进程(快速上手)
// 1. 同步获取SSL证书过期时间(示例实现) $url = $_POST['url']; $sslExpiry = getSslExpiryDate($url); // 2. 启动后台截图进程,避免阻塞主请求 $pythonScript = __DIR__ . '/selenium_function.py'; // 用>/dev/null 2>&1 &让进程脱离终端,不占用主请求资源 exec("python3 {$pythonScript} {$url} > /dev/null 2>&1 &"); // 3. 立即返回结果,标记截图状态为"生成中" echo json_encode([ 'ssl_expiry' => $sslExpiry, 'screenshot_status' => 'pending', 'target_url' => $url ]); // 辅助函数:获取SSL证书过期时间 function getSslExpiryDate($url) { $parsed = parse_url($url); $host = $parsed['host']; $context = stream_context_create(['ssl' => ['capture_peer_cert' => true]]); $socket = stream_socket_client("ssl://{$host}:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); $cert = stream_context_get_params($socket)['options']['ssl']['peer_certificate']; $certInfo = openssl_x509_parse($cert); return date('Y-m-d H:i:s', $certInfo['validTo_time_t']); }
方式二:消息队列(高并发场景推荐)
如果需要处理大量请求,用Redis/RabbitMQ做消息队列更可靠:
- PHP将URL发送到消息队列
- 单独编写Python守护进程,监听队列并执行截图任务
- 截图完成后,将结果路径存入Redis,关联请求标识
3. Python截图脚本调整
让脚本支持接收URL参数,完成截图后保存状态到Redis,方便前端查询:
import sys from selenium import webdriver import redis import hashlib def generate_screenshot(url): # 无头模式启动Chrome options = webdriver.ChromeOptions() options.add_argument('--headless=new') options.add_argument('--disable-gpu') driver = webdriver.Chrome(options=options) try: driver.get(url) driver.implicitly_wait(5) # 等待元素加载 # 生成唯一文件名 url_hash = hashlib.md5(url.encode()).hexdigest() save_path = f"screenshots/{url_hash}.png" driver.save_screenshot(save_path) # 保存结果到Redis r = redis.Redis(host='localhost', port=6379, db=0) r.set(f"screenshot:{url}", save_path) r.expire(f"screenshot:{url}", 3600) # 1小时后自动清理 finally: driver.quit() if __name__ == "__main__": target_url = sys.argv[1] generate_screenshot(target_url)
二、前端交互实现
前端拿到初始结果后,通过轮询或WebSocket监听截图状态,完成后自动更新显示:
1. 轮询方案(简单易实现)
<div class="result-section"> <h3>SSL证书信息</h3> <p>过期时间:<?= $sslExpiry ?></p> </div> <div id="screenshot-area"> <p>截图生成中,请稍候...</p> </div> <script> const targetUrl = "<?= $url ?>"; const checkInterval = setInterval(() => { fetch(`/check_screenshot.php?url=${encodeURIComponent(targetUrl)}`) .then(res => res.json()) .then(data => { if (data.status === 'done') { clearInterval(checkInterval); document.getElementById('screenshot-area').innerHTML = ` <h3>页面截图</h3> <img src="/${data.path}" alt="目标页面截图" style="max-width: 100%;"> `; } }); }, 2000); // 每2秒查询一次状态
对应的check_screenshot.php:
<?php $url = $_GET['url']; $redis = new Redis(); $redis->connect('localhost', 6379); $screenshotPath = $redis->get(f"screenshot:{url}"); if ($screenshotPath) { echo json_encode(['status' => 'done', 'path' => $screenshotPath]); } else { echo json_encode(['status' => 'pending']); } ?>
2. WebSocket方案(实时性更佳)
如果追求无延迟更新,可使用PHP的Ratchet扩展搭建WebSocket服务,截图完成后主动向前端推送结果,替代轮询。
三、之前async/await无效的原因
你之前的尝试没达到预期,大概率是因为:
- PHP的async/await仅适用于协程处理HTTP请求或内部IO,调用外部Python进程时若未彻底脱离主请求,仍会阻塞。
- 原生Selenium是同步阻塞的,即使Python用async语法,也无法让截图任务真正异步执行,必须将任务完全剥离到独立进程中。
内容的提问来源于stack exchange,提问作者yushi
相关产品推荐
相关产品推荐

