爱收集资源网

网站时间线跟踪助您诊断性能问题

网络整理 2023-09-12 16:01

puppeteer

google chrome团队出品的puppeteer 是依赖nodejs和chromium的自动化测试库,它的最大优点就是可以处理网页中的动态内容,如JavaScript,能够更好的模拟用户。

有些网站的反爬虫手段是将部份内容隐藏于个别javascript/ajax恳求中,致使直接获取a标签的形式不奏效。甚至有些网站会设置隐藏元素“陷阱”,对用户不可见网页点赞,脚本触发则觉得是机器。这种情况下,puppeteer的优势就展现下来了。

它可实现如下功能:

生成页面的屏幕截图和PDF。抓取SPA并生成预先呈现的内容(即“SSR”)。自动表单递交,UI测试,键盘输入等。创建一个最新的自动化测试环境。使用最新的JavaScript和浏览器功能,直接在最新版本的Chrome中运行测试。捕获跟踪您网站的时间线,以帮助确诊性能问题。

开源地址:github.com/GoogleChrom…

安装

npm i puppeteer

注意先安装nodejs, 并在nodejs文件根目录下执行(npm文件同级)。

安装过程中会下载chromium,大约120M。

用三天(大约10小时)摸索网页点赞,绕过了相当多的异步的坑,笔者对puppeteer和nodejs有了一定的把握。

一张长图,抓取blog文章列表:

qq点赞一次点十次_微信点赞_网页点赞

抓取blog文章

以csdn blog为例,文章内容须要点击“阅读全文”来获取,这就造成只能读取dom的脚本失效。

/**
* load blog.csdn.net article to local files
**/
const puppeteer = require('puppeteer');
//emulate iphone
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1';
const workPath = './contents';
const fs = require("fs");
if (!fs.existsSync(workPath)) {
        fs.mkdirSync(workPath)
}
//base url
const rootUrl = 'https://blog.csdn.net/';
//max wait milliseconds
const maxWait = 100;
//max loop scroll times
const makLoop = 10;
(async () => {
    let url;
    let countUrl=0;
    const browser = await puppeteer.launch({headless: false});//set headless: true will hide chromium UI
    const page = await browser.newPage();
    await page.setUserAgent(userAgent);
    await page.setViewport({width:414, height:736});
    await page.setRequestInterception(true);
    //filter to block images
    page.on('request', request => {
    if (request.resourceType() === 'image')
      request.abort();
    else
      request.continue();
    });
    await page.goto(rootUrl);
    
    for(let i= 0; iwindow.scrollTo(0, document.body.scrollHeight));
            await page.waitForNavigation({timeout:maxWait,waitUntil: ['networkidle0']});
        }catch(err){
            console.log('scroll to bottom and then wait '+maxWait+'ms.');
        }
    }
    await page.screenshot({path: workPath+'/screenshot.png',fullPage: true, quality :100, type :'jpeg'});
    //#feedlist_id li[data-type="blog"] a
    const sel = '#feedlist_id li[data-type="blog"] h2 a';
    const hrefs = await page.evaluate((sel) => {
        let elements = Array.from(document.querySelectorAll(sel));
        let links = elements.map(element => {
            return element.href
        })
        return links;
    }, sel);
    console.log('total links: '+hrefs.length);
    process();
  async function process(){
    if(countUrlelse{
        browser.close();
        return;
    }
    console.log('processing url: '+url);
    try{
        const tab = await browser.newPage();
        await tab.setUserAgent(userAgent);
        await tab.setViewport({width:414, height:736});
        await tab.setRequestInterception(true);
        //filter to block images
        tab.on('request', request => {
        if (request.resourceType() === 'image')
          request.abort();
        else
          request.continue();
        });
        await tab.goto(url);
        //execute tap request
        try{
            await tab.tap('.read_more_btn');
        }catch(err){
            console.log('there\'s none read more button. No need to TAP');
        }
        let title = await tab.evaluate(() => document.querySelector('#article .article_title').innerText);
        let contents = await tab.evaluate(() => document.querySelector('#article .article_content').innerText);
        contents = 'TITLE: '+title+'\nURL: '+url+'\nCONTENTS: \n'+contents;
        const fs = require("fs");
        fs.writeFileSync(workPath+'/'+tab.url().substring(tab.url().lastIndexOf('/'),tab.url().length)+'.txt',contents);
        console.log(title + " has been downloaded to local.");
        await tab.close();
    }catch(err){
        console.log('url: '+tab.url()+' \n'+err.toString());
    }finally{
        process();
    }
    
  }
})();

执行过程

录屏可以在我公众号查看,下边是截图:

执行结果

文章内容列表:

文章内容:

网页点赞_qq点赞一次点十次_微信点赞

结束语

以前就想过既然nodejs是使用JavaScript脚本语言,那么它肯定能处理网页的JavaScript内容,但并没有发觉合适的/高效率的库。直到发觉puppeteer,才下定决心试水。

话说回去,nodejs的异步真的是很难受的一件事,这上百行代码我居然折腾了10个小时。

大家可拓展下代码中process()方法,使用async.eachSeries,我使用的递归方法并不是最优解。

事实上,逐一处理并不高效,原本我写了一个异步的关掉browser方式:

let tryCloseBrowser = setInterval(function(){
        console.log("check if any process running...")
        if(countDown<=0){
          clearInterval(tryCloseBrowser);
          console.log("none process running, close.")
          browser.close();
        }
    },3000);

按照这个思路,代码的最初版本是同时打开多个tab页,效率很高,但容错率很低,大家可以试着自己写一下。

题外话

看过我的文章的人都晓得,我写文章更指出处理问题的方法/方法,给你们一些思维上的建议。

对于nodejs和puppeteer我是完全陌生的(当然,我晓得她们适宜做什么,仅此而已)。如果你们还记得《10倍速程序员》里提及的按需记忆的理念,那么你才会理解我刻意的不去系统的学习新技术。

我谈谈我接触puppeteer到完成我须要功能的所有思维逻辑:

了解puppeteer功能/特性,结合目的判定是否满足要求。快速实现getStart中的所有demo二次判定puppeteer的特点,从设计者角度出发,推测puppeteer的构架。验证构架。通读api,了解puppeteer细节。搜索puppeteer后置学习内容(以及后置学习内容所依赖的后置学习内容)。整理学习内容树,回到1。设计/分析/调试/……

2018年5月9日02点13分

网页点赞