You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Python+Selenium爬取YouTube评论、回复及互动数据?

解决YouTube评论及回复爬取的Selenium方案

先说说你之前代码的核心问题:

  1. 没触发回复展开:YouTube的评论回复默认是折叠状态,必须点击View X replies按钮才会加载实际内容,你之前的代码只定位了回复区域,但没触发点击操作,所以只能拿到按钮文本,拿不到真实回复内容。
  2. 定位逻辑不全:只抓取了主评论的文本内容,完全缺失了点赞数、点踩数、发布日期这些关键字段的定位逻辑。
  3. 滚动加载不够精准:单纯按END键全局滚动可能会漏掉部分评论加载,最好直接针对评论区容器滚动,避免无效操作。

修正后的完整代码

下面是能爬取主评论、评论回复、点赞数、发布日期的完整代码,我加上了详细注释:

import time
import csv
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import ElementNotInteractableException, StaleElementReferenceException

def scrape_youtube_comments(url, chrome_driver_path):
    # 初始化浏览器
    driver = webdriver.Chrome(executable_path=chrome_driver_path)
    driver.get(url)
    wait = WebDriverWait(driver, 10)
    
    # 获取视频标题
    title = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="container"]/h1/yt-formatted-string'))).text
    print(f"正在爬取视频:{title}")

    # 滚动到评论区位置
    driver.execute_script("window.scrollTo(0, document.getElementById('comments').offsetTop);")
    time.sleep(3)

    # 滚动加载所有主评论
    last_height = driver.execute_script("return document.getElementById('comments').scrollHeight")
    while True:
        # 滚动评论区到底部
        driver.execute_script("document.getElementById('comments').scrollTo(0, document.getElementById('comments').scrollHeight);")
        time.sleep(2)
        # 检查是否加载完成(高度不再变化)
        new_height = driver.execute_script("return document.getElementById('comments').scrollHeight")
        if new_height == last_height:
            break
        last_height = new_height

    # 点击所有"View X replies"按钮,展开回复内容
    reply_buttons = wait.until(EC.presence_of_all_elements_located((By.XPATH, '//paper-button[@id="more" and @aria-label]')))
    for button in reply_buttons:
        try:
            # 滚动到按钮可见位置,避免点击失败
            driver.execute_script("arguments[0].scrollIntoView();", button)
            time.sleep(1)
            button.click()
            time.sleep(2)
        except (ElementNotInteractableException, StaleElementReferenceException):
            # 跳过已经展开或无法点击的按钮
            continue

    # 准备存储所有评论数据
    all_comments_data = []

    # 遍历所有主评论块
    comment_blocks = wait.until(EC.presence_of_all_elements_located((By.XPATH, '//ytd-comment-thread-renderer')))
    for block in comment_blocks:
        try:
            # 抓取主评论核心信息
            main_comment_text = block.find_element(By.XPATH, './/yt-formatted-string[@id="content-text"]').text
            like_count = block.find_element(By.XPATH, './/span[@id="vote-count-middle"]').text
            publish_date = block.find_element(By.XPATH, './/a[@id="author-text"]/following-sibling::yt-formatted-string').text
            # YouTube现在默认隐藏点踩数,这里做特殊标注
            dislike_count = "无法直接获取(YouTube已隐藏)"

            # 添加主评论数据到列表
            all_comments_data.append({
                "类型": "主评论",
                "内容": main_comment_text,
                "点赞数": like_count,
                "点踩数": dislike_count,
                "发布日期": publish_date
            })

            # 抓取当前主评论下的所有回复
            replies = block.find_elements(By.XPATH, './/ytd-comment-replies-renderer//yt-formatted-string[@id="content-text"]')
            reply_dates = block.find_elements(By.XPATH, './/ytd-comment-replies-renderer//a[@id="author-text"]/following-sibling::yt-formatted-string')
            reply_likes = block.find_elements(By.XPATH, './/ytd-comment-replies-renderer//span[@id="vote-count-middle"]')

            for idx, reply in enumerate(replies):
                reply_text = reply.text
                reply_date = reply_dates[idx].text if idx < len(reply_dates) else "未知"
                reply_like = reply_likes[idx].text if idx < len(reply_likes) else "0"
                all_comments_data.append({
                    "类型": "回复",
                    "内容": reply_text,
                    "点赞数": reply_like,
                    "点踩数": dislike_count,
                    "发布日期": reply_date
                })
        except Exception as e:
            print(f"处理评论块时出错:{e}")
            continue

    # 将数据写入CSV文件
    with open("youtube_comments_full.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["类型", "内容", "点赞数", "点踩数", "发布日期"])
        writer.writeheader()
        writer.writerows(all_comments_data)
    
    print(f"爬取完成,共获取{len(all_comments_data)}条评论/回复,已保存到youtube_comments_full.csv")
    driver.quit()

# 调用函数,替换成你的ChromeDriver路径和目标视频URL
if __name__ == "__main__":
    CHROME_PATH = "/Users/Downloads/chromedriver"
    VIDEO_URL = "https://www.youtube.com/watch?v=qBp1rCz_yQU"
    scrape_youtube_comments(VIDEO_URL, CHROME_PATH)

关键步骤说明:

  • 精准滚动评论区:直接定位评论区DOM元素comments进行滚动,比全局滚动更高效,避免干扰页面其他元素。
  • 自动展开回复:遍历所有回复展开按钮,处理可能的元素交互异常(比如按钮已经被点击过)。
  • 完整字段抓取:针对主评论和回复分别定位内容、点赞、日期字段,适配YouTube当前的页面结构。
  • 异常处理:添加了常见的Selenium异常捕获,避免单个评论出错导致整个程序崩溃。

注意事项:

  1. 确保你的ChromeDriver版本和Chrome浏览器版本一致,否则会出现兼容性问题。
  2. 可以根据网络情况调整time.sleep的时长,避免因加载慢导致元素定位失败。
  3. YouTube有反爬机制,频繁爬取可能触发人机验证,建议适当增加延迟或使用代理。

内容的提问来源于stack exchange,提问作者sangeetha

火山引擎 最新活动