You need to enable JavaScript to run this app. 如何在Puppeteer中确定性使用CSS选择器获取SoundCloud歌曲时长
优惠活动
大模型
产品
解决方案
定价
更多

如何在Puppeteer中确定性使用CSS选择器获取SoundCloud歌曲时长

问题解决方案

核心原因

  • SoundCloud是客户端渲染的单页应用,默认的page.goto完成后页面动态内容还没渲染完成,直接执行选择器自然返回空
  • page.waitForNavigation()放在page.goto()之后调用会报错,因为该API的作用是等待「后续触发的页面跳转动作」完成,此时导航已经结束,自然会超时抛出异常

具体优化步骤

1. 给页面跳转增加完整加载配置

修改page.goto参数,配置等待网络空闲,延长超时时间适配资源加载:

await page.goto("https://soundcloud.com/octasine/octasine-audio-example-1", {
  // 等待2秒内只有不到2个网络请求,认为动态内容加载完成
  waitUntil: 'networkidle2',
  // 超时时间调整为60秒,避免网络慢导致加载失败
  timeout: 60000
});

直接删除你注释的await page.waitForNavigation(),不需要再加这个语句。

2. 执行选择器前先等待目标元素加载完成

在调用work函数之前,先等待你要找的span.sc-visuallyhidden元素渲染完成,避免空数组:

// 等待目标元素出现,最长等10秒
await page.waitForSelector('span.sc-visuallyhidden', { timeout: 10000 });
await work();

同理,点击播放按钮前也可以加元素等待,避免点击失效:

await page.waitForSelector('.snippetUXPlayButton', { timeout: 10000 });
await page.evaluate(() => {
  let elements = document.getElementsByClassName('snippetUXPlayButton');
  for (let element of elements)
    element.click();
});

3. 优化时长提取逻辑,增加容错

你当前的mins和secs没有声明作用域,且用split提取容易受文本格式影响,建议调整:
首先在test函数顶部声明变量:

let mins = 0, secs = 0;

然后修改work函数里的提取逻辑,用正则匹配更稳定:

if (inputValue.includes('Duration')){
  console.log("DURATION");
  const match = inputValue.match(/Duration (\d+) minutes? (\d+) seconds?/);
  if (match) {
    mins = Number(match[1]);
    secs = Number(match[2]);
  }
  console.log(mins);
  console.log(secs);
  console.log(inputValue);
}

优化后完整可运行代码

// example.js -- node version v14.17.2 -- dependency installed with npm i puppeteer-stream 
const { launch, getStream }  = require("puppeteer-stream");
const fs = require("fs");

const file = fs.createWriteStream(__dirname + "/test.webm");

async function test() {
    let mins = 0, secs = 0;
    const browser = await launch();

    const page = await browser.newPage();
    // 跳转时增加加载配置
    await page.goto("https://soundcloud.com/octasine/octasine-audio-example-1", {
      waitUntil: 'networkidle2',
      timeout: 60000
    });
    
    let html_var = await page.content();
    // 写入HTML文件
    fs.writeFile("example.html", html_var, function (err) {
      if (err) return console.log(err);
    });
    console.log("Wrote html to example.html");

    // 等待播放按钮加载完成再点击
    await page.waitForSelector('.snippetUXPlayButton', { timeout: 10000 });
    await page.evaluate(() => {
        let elements = document.getElementsByClassName('snippetUXPlayButton');
        for (let element of elements)
            element.click();
    });

    const work = async () => {
        const inputsValues = [];
        const inputElements = await page.$$('span.sc-visuallyhidden');
        
        for (const element of inputElements) {
                let inputValue;
        
                inputValue = await element.getProperty('innerText');
                inputValue = await inputValue.jsonValue();
                // 时长提取增加容错
                if (inputValue.includes('Duration')){
                    console.log("DURATION");
                    const match = inputValue.match(/Duration (\d+) minutes? (\d+) seconds?/);
                    if (match) {
                      mins = Number(match[1]);
                      secs = Number(match[2]);
                    }
                    console.log(mins);
                    console.log(secs);
                    console.log(inputValue);
                }
        
            inputsValues.push(inputValue);

        }
    
        console.log(inputsValues)
    }
    // 等待目标元素加载完成再执行提取逻辑
    await page.waitForSelector('span.sc-visuallyhidden', { timeout: 10000 });
    await work();

    let page_url = await page.url();
    console.log(page_url)
    
    await page.evaluate(() => {
        let elements = document.getElementsByClassName('sc-visuallyhidden');
        for (let element of elements)
            console.log(element.innerHTML);
    });

    const stream = await getStream(page, { audio: true, video: true });
    console.log("recording");

    stream.pipe(file);
    setTimeout(async () => {
        await stream.destroy();
        file.close();
        console.log("finished");
        browser.close();
    }, 1000 * 5 + mins * 60000 + secs * 1000);

}

test();

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 19:42:03