例:pppeteer headlessを使ってJSページをキャプチャする

8498 ワード

pppeteer
google chromeチームが出品するpppeteerはnodejsとchromiumの自動化テストライブラリに依存しています.最大の利点は、JavaScriptなどのウェブページのダイナミックコンテンツを処理することができます.より良いアナログユーザーができます.一部のサイトのアンチ爬虫剤は一部のコンテンツをいくつかのjavascript/ajax要求に隠しています.直接にaタグを取得する方式は効果がありません.隠し要素の「落とし穴」を設定するサイトもありますが、ユーザーには見えません.この場合、pppeteerの強みが浮き彫りになります.これは次のような機能を実現できます.
  • は、ページのスクリーンショットとPDFを生成する.
  • は、SPAをキャプチャし、予め提示されたコンテンツ(すなわち「SSR」)を生成する.
  • 自動フォーム提出、UIテスト、キーボード入力など.
  • は最新の自動化試験環境を作成します.最新のJavaScriptとブラウザ機能を使って、最新バージョンのChromeで直接テストを実行します.
  • は、あなたのサイトの時間線を追跡して、性能の問題を診断するのを助けます.
  • オープンソースアドレス:github.com/GoogleChrom…
    インストール
    npm i puppeteer
    
    注意先にnodejsをインストールし、nodejsファイルのルートディレクトリの下で実行します.インストール中にchromiumをダウンロードします.約120 Mです.
    二日間(約10時間)の模索で、かなりの非同期の穴を迂回しました.私はpppeteerとnodejsをある程度把握しました.長い写真を持って、ブログの記事リストを取ります.
    ブログ記事をつかむ
    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+'
    URL: '
    +url+'
    CONTENTS:
    '
    +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()+'
    '
    +err.toString()); }finally{ process(); } } })();
    実行プロセス
    録画画面は私の公衆番号で見ることができます.下はスクリーンショットです.
    実行結果
    記事の内容リスト:
    記事の内容:
    結尾語
    以前から考えていましたが、nodejsはJavaScriptスクリプト言語を使っているので、きっとウェブページのJavaScript内容を処理できます.pppeteerを発見するに至って、試水に踏み切った.ところで、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);
    
    この考え方では、コードの最初のバージョンは同じ時間に複数のタブを開いて、効率が高いですが、フォールトトレランス率が低いので、自分で書いてみてください.
    余談をする
    私の文章を読んだ人はみんな知っています.文章を書くと問題の解決方法がもっと強調されます.nodejsとpppeteerについてはまったく知らないです.「10倍速プログラマー」に記載されている記憶が必要とされている理念を覚えていると、システムに行かずに新しい技術を勉強しているということが分かります.私はpppeteerに接触して完成するまでに必要な機能のすべての思考ロジックを言います.
  • は、pppeteer機能/特性を理解し、目的に合わせて要求を満たすか否かを判断する.
  • get Startの中のすべてのdemo
  • を迅速に実現します.
  • は、pppeteerの特性を二次的に判断し、設計者の立場からpppeteerのアーキテクチャを推測する.
  • 認証アーキテクチャ.
  • apiを通して、pppeteerの詳細を知る.
  • pppeteer前置学習内容(及び前置学習内容に依存する前置学習内容)を検索します.勉強内容を整理して、帰ってきます.
  • デザイン/分析/デバッグ/…
  • 2018年5月9日02時13分