使用Puppeteer进行数据抓取(二)

2,302 阅读3分钟

前言        

       基于上一篇文章,我本来想着按照正常的开发流程,一定是加载首页的列表,通过传递一个id号,然后跳转到新的页面。发起请求,来查询详情页面的内容,然后。。。。。。但是我看了一眼发现,他们家的网站不按常理出牌,他们是直接在 a 标签里面 通过 href 跳到他们已经写好的页面了。好了我们我们开始分析先。

一、先分析列表数据

       首先我们可以从puppteer 的 文档知道 有一个 _page.click()_ 的方法,然后执行点击后发现,的确是进到详情页面了。这时候我才发现,他们跳进来新页面是不传参数的,我们也没法实现拦截http获取相应信息的,然而问题来了,我们怎么去获取到新页面的信息。(本人学艺不精,恳请大神为我解惑。)所以我决定按照之前的思路。循环去通过 _page.goto() _来加载新页面。

       不难发现他们的 a标签 都有一个 href="/xxx/xxx.html",所以我们在爬取数据得时候,可以给他们拼接一个域名前缀上去,因为是我爬取的是“第一财经”这个网站,所以根据需求,各自分析吧。

我临时写个地址去赋值了。

二、循环遍历打开页面爬数据

依旧,我们还是通过自执行函数执行

 ; (async () => {
                let allResult = []
                for (let i = 0; i < result.length; i++) {
                    let detailInfo = '';// 文章详情
                    await page.goto(result[i].detailPage, {
                        waitUntil: 'networkidle2'
                    });
     }
})();

按照老思路,我们先获取到对应的元素节点。这里就要分析一下了,我发现他们详情页数据有的是视频,有的是文章,甚至有的是单张图片。这边暂时获取到文章就行,其他有需要的可以自行研究。

// 先判断元素是否存在
                    const element = await page.$(".m-text");
                    if (element) {
                        await page.waitForSelector('.m-text')
                    }
                    var obj = await page.evaluate((result, i) => {
                        let obj = {}
                        let $ = window.$;
                        var pageDom = $('#multi-text')
                        if (pageDom) {
                            detailInfo = pageDom[0].innerHTML // 我这边直接拿到html的标签+内容
                        }
                        result[i].detailInfo = detailInfo
                        return obj = result[i]
                    }, result, i)

特别要注意的一点

在page.evaluate(()=>{}) 这个里面打的 log 在我们的编辑工具是看不到的,这个是运行在我们浏览器的,所以这里打印的日志是只能在浏览器调试模式下查看。

三、把我们获取的数据push到对应的数组中。

 allResult.push(obj)
 await page.waitFor(10000); // 这个时间我是为了观察日志的,给时间爬取数据
 await page.goBack(); // 这边是回退到前一页,意思就是我们进去新页面爬取数据完成后,关闭当前页面,再打开一个新的页面继续爬取

最后完成后把我们的浏览器关闭

page.close();

四、完整代码展示(有机会再考虑存mysql或是 mongodb)

const puppeteer = require('puppeteer')
const baseUrl = 'https://www.yicai.com';
// 等待3000毫秒
const sleep = time => new Promise(resolve => {
    setTimeout(resolve, time);
})
    ; (async () => {
        const browser = await puppeteer.launch({
            headless: false, //浏览器界面启动
            slowMo: 200,   //放慢浏览器执行速度,方便测试观察
            args: ['--no-sandbox'],
            dumpio: false,
            devtools: true, //开发模式
        });
        const page = await browser.newPage();
        await page.goto(baseUrl, {
            waitUntil: 'networkidle2'
        });
        await page.waitFor(2000);        // 等待某个节点的加载
        await page.waitForSelector('.u-btn')
        for (let index = 0; index < 1; index++) {
            await page.waitFor(2000); 
           await page.click('.u-btn')
        }
        const result = await page.evaluate(() => {
            let apiUrl = 'https://www.yicai.com'; 
           let $ = window.$;
            // var items = $('.m-con a')  //  不可用这个,由于页面是通过display:none or block来控制的,爬取的数据会是多个页面的叠加 
           var items = $('#headlist').children('a')
            var links = []
            if (items.length >= 1) {
                items.each((index, item) => {
                    let it = $(item)
                    let articleTitle = it.find('h2').text()
                    let articleIntroduction = it.find('p').text()
                    let imageAddress = it.find('img').attr('src')
                    let createdTime = it.find('span').text()
                    let detailPage = it.attr('href')
                    links.push({
                        articleTitle,
                        articleIntroduction, 
                        imageAddress,
                        createdTime,
                        detailPage: apiUrl + detailPage
                    })
                })
            }
            return links
        })
        console.log(result);
        // 暂缓1s
         await sleep(1000)
            ; (async () => { 
               let allResult = []
                for (let i = 0; i < result.length; i++) {
                    let detailInfo = '';// 文章详情
                    await page.goto(result[i].detailPage, {
                        waitUntil: 'networkidle2'
                    }); 
                   // 先判断元素是否存在
                    const element = await page.$(".m-text");
                    if (element) {
                        await page.waitForSelector('.m-text')
                    }
                    var obj = await page.evaluate((result, i) => {
                        let obj = {}
                        let $ = window.$; 
                       var pageDom = $('#multi-text')
                        if (pageDom) {
                            detailInfo = pageDom[0].innerHTML
                        }
                        result[i].detailInfo = detailInfo
                        return obj = result[i]
                    }, result, i)
                    allResult.push(obj) 
                   await page.waitFor(10000);
                   await page.goBack();
                }
                await page.close();            })();

    })();

五、寻求大神指点

可能对这puppetter认识不够充分,觉得在爬取过程中,不太流畅。希望有大牛指点指点。