如何解决Selenium自动化YouTube频道滚动加载视频失效问题
YouTube频道视频爬取:滚动加载停滞问题排查与解决
根因分析
- 固定等待时间不足:代码中
Thread.sleep(1000)是固定等待,YouTube加载视频的时间受网络、页面渲染速度影响,若1秒内未完成新视频加载,会误判为已到达页面底部,停止滚动。 - 滚动容器定位错误:使用
document.documentElement.scrollHeight获取整个文档的高度,但YouTube的视频列表实际在独立的滚动容器内,整个文档的高度变化不能准确反映视频列表的加载状态。 - 滚动逻辑存在漏洞:
window.scrollTo(0, startHeight)是滚动到当前文档的高度,若此时页面还在加载中,滚动高度未更新,会重复滚动到同一位置,无法触发新视频加载。 - 反爬机制限制:频繁的JavaScript滚动操作可能被YouTube的反爬机制识别,停止返回新的视频内容。
可行的替代解决方案
方案1:针对视频列表容器滚动+动态等待
直接定位到YouTube视频列表的滚动容器,滚动该容器并等待新视频元素加载,避免依赖整个文档的高度变化:
WebDriverManager.chromedriver().setup(); WebDriver driver = new ChromeDriver(); WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); driver.get("https://www.youtube.com/@LambdaTest/videos"); JSONArray resultArray = new JSONArray(); // 处理Cookie弹窗 try { wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@aria-label='Accept all']"))).click(); } catch (Exception e) {} driver.manage().window().maximize(); JavascriptExecutor js = (JavascriptExecutor) driver; // 定位视频列表的滚动容器 WebElement scrollContainer = driver.findElement(By.cssSelector("ytd-rich-grid-renderer #contents")); int previousVideoCount = 0; do { previousVideoCount = driver.findElements(By.cssSelector("ytd-rich-item-renderer #content")).size(); // 滚动到容器底部 js.executeScript("arguments[0].scrollTop = arguments[0].scrollHeight", scrollContainer); // 等待新视频加载,最多等待5秒,直到视频数量增加 try { wait.until(d -> driver.findElements(By.cssSelector("ytd-rich-item-renderer #content")).size() > previousVideoCount); } catch (TimeoutException e) { // 超时说明没有新视频加载,退出循环 break; } } while (true); // 爬取视频数据 List<WebElement> allVideos = driver.findElements(By.cssSelector("ytd-rich-item-renderer #content")); for (WebElement video : allVideos) { WebElement title = video.findElement(By.cssSelector("#video-title-link")); List<WebElement> metaData = video.findElements(By.cssSelector("#metadata-line span")); JSONObject videoData = new JSONObject(); videoData.put("video_title", title.getText()); videoData.put("video_views", metaData.get(0).getText()); videoData.put("video_days", metaData.get(1).getText()); resultArray.add(videoData); } System.out.println(resultArray); driver.quit();
方案2:模拟用户键盘滚动(PageDown)
用模拟键盘按下PageDown的方式代替JavaScript滚动,更贴近真实用户操作,降低被反爬识别的概率:
// 省略初始化、Cookie处理等重复代码 Actions actions = new Actions(driver); int previousCount = 0; int currentCount; do { previousCount = driver.findElements(By.cssSelector("ytd-rich-item-renderer #content")).size(); // 连续按下PageDown键模拟滚动 for (int i = 0; i < 6; i++) { actions.sendKeys(Keys.PAGE_DOWN).perform(); Thread.sleep(400); // 短等待让页面响应 } currentCount = driver.findElements(By.cssSelector("ytd-rich-item-renderer #content")).size(); } while (currentCount > previousCount); // 后续爬取代码同方案1
方案3:优化原滚动逻辑,替换固定等待为显式等待
保留原JavaScript滚动的思路,但用显式等待替代固定Thread.sleep,确保等待到页面加载完成:
// 省略初始化、Cookie处理等重复代码 JavascriptExecutor js = (JavascriptExecutor) driver; long lastScrollHeight = 0; boolean hasNewContent; do { lastScrollHeight = (long) js.executeScript("return document.documentElement.scrollHeight"); js.executeScript("window.scrollTo(0, document.documentElement.scrollHeight)"); // 等待3秒,检查滚动高度是否变化 hasNewContent = new WebDriverWait(driver, Duration.ofSeconds(3)).until(d -> { long currentScrollHeight = (long) js.executeScript("return document.documentElement.scrollHeight"); return currentScrollHeight > lastScrollHeight; }); } while (hasNewContent); // 后续爬取代码同方案1
额外注意事项
- 反爬应对:避免过于频繁的操作,适当调整等待时间;可以通过
driver.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")隐藏Selenium的webdriver标识。 - 元素定位优化:优先使用CSS选择器(如
ytd-rich-item-renderer #content),比XPath更稳定,不易因页面结构小变化失效。
内容的提问来源于stack exchange,提问作者Sohail M
相关产品推荐
相关产品推荐

