• 使用Puppeteer进行数据抓取(三)——简单的示例


    本文以一个示例简单的介绍一下puppeteer的用法,我们的目的是:获取我博客上的文章的前十页的所有随笔的标题和链接。由于puppeteer本身是自动化chorme,因此这里我们的步骤和手动操作浏览器差不多:

    1. 打开chrome,跳转到博客首页
    2. 获取所有博客标题信息
    3. 点击下一页按钮,跳转到下一页
    4. 重复2、3两步,直到所有信息采集完毕

    获取信息

    采集过程中比较麻烦的一步就是信息的采集,和传统采集html后解析的方式不同的时,由于chrome本身有完整的js引擎,因此我们采用注入一段js,利用该js采集到我们的信息,并通过puppeteer返回给应用程序的方式。由于puppete本身是采用的chrome,因此编写采集信息函数这一过程完全可以在chrome上进行。

    首先用chrome打开我的博客的首页http://www.cnblogs.com/TianFang/,打开chrome devtool,获取标题的css path。然后我们可以利用chrome的snippets简单的写一个获取所有标题信息的函数:

      

    本身就蜘蛛程序而言,编写获取脚本内容的这个部分是比较繁琐的,需要不断的反复调试。但使用snippets直接编写函数大大简化了这一过程,它的主要好处有:

    1. 可以直接使用各种js函数,可以直接操作各种dom对象
    2. 利用snippets编写可以在chrome中实时调试,
    3. 可以实时查看生成结果

    然后我们只需要将这个函数放到node中,利用puppeteer跳转到相应的页面,使用page.evaluate函数执行这个函数即可。

    async function getTitles() {
        var titles = $('.postTitle>a');

        var result = titles.map((i, a) => ({
            text: a.text,
            href: a.href
        })).get();

        console.table(result);
        return result;


    var titles = await page.evaluate(getTitles);
    console.log(titles);

    一个需要注意的地方是,这个函数实际上是在chrome中执行的,puppeteer要求在chrome中执行的函数必须是异步的,因此需要加上async关键字。

     

    页面跳转

    下一步需要解决的问题就是如何跳转到下一页了。方法也比较简单:分析一下页面,找到下一页的连接,执行js模拟点击即可:

    page.evaluate(async() => $(`a:contains('下一页')`)[0].click());

     

    完整代码如下:

    const puppeteer = require('puppeteer');

    const chrome_exe = String.raw`${process.env["ProgramFiles(x86)"]}GoogleChromeApplicationchrome.exe`;
    const user_data_path = String.raw`${process.env.LocalAppData}GoogleChromeUser DataDefault`;

    async function run() {
        const browser = await puppeteer.launch({
            headlessfalse,
            userDataDiruser_data_path,
            executablePathchrome_exe
        });
        const page = await browser.newPage();
        page.setViewport({ width: 1600, height: 900 });
        await page.goto('http://www.cnblogs.com/TianFang');

        for (var i = 0; i < 10; i++) {

            console.log(i);
            console.log(page.url());

            var titles = await page.evaluate(getTitles);
            console.log(titles);

            await page.evaluate(async() => $(`a:contains('
    下一页
    ')`)[0].click());
            await page.waitForNavigation();

            await sleep(1000);
        }
    };

    async function getTitles() {
        var titles = $('.postTitle>a');

        var result = titles.map((i, a) => ({
            text: a.text,
            href: a.href
        })).get();

        console.table(result);
        return result;


    async function sleep(timeout) {
        return new Promise(resolve => setTimeout(resolve, timeout));
    }

    run();

    整个过程还是非常简单的, 执行结果如下:

      

    另外值得一提的是,细心的朋友可能注意到这里我们编写一个可以await的sleep函数。

    async function sleep(timeout) {
        return new Promise(resolve => setTimeout(resolve, timeout));
    }

    这个函数本身不是必须的,但在编写蜘蛛程序过程中却经常需要等待的,使用await异步等待可以大大提高代码的可读性,由于node本身没有提供可以await的sleep函数。并且这个函数非常实用,这里就自己实现了一个,记录一下,以备以后使用。

  • 相关阅读:
    BZOJ 1101 [POI2007]Zap
    BZOJ 2005 [Noi2010]能量采集
    BZOJ 1053 [HAOI2007]反素数ant
    BZOJ 4321 queue2
    ZOJ 1456 Minimum Transport Cost(Floyd算法求解最短路径并输出最小字典序路径)
    POJ 3268 Silver Cow Party(Dijkstra算法求解来回最短路问题)
    Trie(字典树)解析及其在编程竞赛中的典型应用举例
    POJ 3037 Skiing(如何使用SPFA求解二维最短路问题)
    POJ 1724 ROADS(使用邻接表和优先队列的BFS求解最短路问题)
    POJ 1860 Currency Exchange(如何Bellman-Ford算法判断图中是否存在正环)
  • 原文地址:https://www.cnblogs.com/TianFang/p/9060309.html
Copyright © 2020-2023  润新知