puppeteer在客户端渲染页面爬虫中的应用

478 阅读1分钟

前情提要:客户端渲染,当前页面加载渲染完成后,拿到列表数据才能获得子页面信息,进而抓取子页面的数据

Puppeteer是一个nodeJs库,能够提取动态数据,网页抓取能力更强大。

安装依赖包

npm i puppeteer

我这里node版本v18.16.0, 安装下来的puppeteer版本是 "puppeteer": "^23.2.1",

初始化

页面入口:

(async () => {
    const {page, browser} = await init()
    handlePage(baseUrl, page, browser)
})()

初始化函数:

async function init(){
    const browser = await puppeteer.launch({headless: false})
    const page = await browser.newPage()
    // 如果当前页面有访问权限,需要设置cookie
    await page.setCookie({
        name: 'key',
        value: 'value',
        domain: '.XXX.com',
        path: '/'
    })
    return {page, browser};
}

如果页面需要登录,可以查看登录后的网页cookie,自己设置一下

开始处理页面


async function handlePage(url, page, browser){
    await page.goto(url);
    const id = ...; // 通过地址栏获取当前页面id
    console.log(`当前页面id为${id}`); 
    const targetDomList = await page.$$('.dom-class')
    console.log(`当前页面有${targetDomList.length}个子页面`);
    
    const response = await axios.get(`https://www.XXX.com/XXX/XXX?a=1&b=2`)
    const items = ... // 拿到接口返回的数据
    const allSubUrls = []
    for(let i = 0; i < items.length; i++) {
        const {contentId, articleInfo} = items[i]
        if(articleInfo){
            allSubUrls.push(`https://www.XXX.com/XXX/${contentId}?sid=${id}`)
        }
    }
    console.log('拿到了所有子页面地址');
    console.log(allSubUrls);
    await handleSubPages(allSubUrls, page, browser)
    await browser.close();  
}

问:这里为什么不直接点击跳转?

答:点击后页面跳转,原始的page对象不再指向当前活动的页面;由于是客户端渲染,这里直接拿到接口数据,进而拿到了子页面地址

爬取子页面数据

async function handleSubPages(urls, page, browser){
    const data = [];
    for (let i = 0; i < urls.length; i++) {
        const url = urls[i]
        await page.goto(url)
        await page.waitForSelector('#dom-id') // 等待dom-id容器渲染完成
        const domContent = await page.$eval('#dom-id', el => el.innerHTML)
        const $ = cheerio.load(domContent) // 使用cheerio加载HTML页面
        const videoNodes = $('video') // 获取节点
        // console.log(`当面页面找到${videoNodes.length}个视频`);
        const videoList = [];
        videoNodes.each((index, node) => {
            const url = $(node).attr('src')
            videoList.push(url.split('?')[0])
        })
        data.push(...videoList)
    }

    console.log(data);
    // 将数据写入JSON文件
    fs.writeFile('./output.json', JSON.stringify(data), 'utf-8', () => { 
        console.log('生成json文件成功!')
    })
    await browser.close()
}

最后将目标数据通过 output 文件输出;

除了 puppeteer,这里还用到了 axioscheerio