爬取某位大佬簡書上全部文章並保存爲pdf


目 標 場 景


現現在,咱們處於一個信息碎片化的信息時代,遇到好的文章都有隨手收藏的習慣。但過一段時間,當你想要從新查看這篇文章的時候,發現文章已經被移除或莫名其妙地消失了。css


若是當時能將這些文章以 pdf 格式保存到本地,待空閒的時候慢慢地看,就不用擔憂這個問題了。node


本文的目標是利用 Google 推出的「puppeteer」,配合無頭瀏覽器爬取某位大佬在簡書上發佈的全部文章,並對頁內元素進行優化樣式後,以「pdf」格式保存下載到本地。web



準 備 工 做


和前面爬蟲方式不同,此次的爬蟲是在「Node.js」環境下執行的,因此須要提早安裝好 node js。npm


而後經過 npm 安裝「puppeteer」模塊。瀏覽器


npm i puppeteer 複製代碼


我這裏使用 Chrome 的無頭瀏覽器模式,因此須要提早下載好「chromium」放在本地。bash



分 析 思 路


爲了便於觀察,首先咱們利用 puppeteer 以有頭模式啓動瀏覽器。less

const browser = await puppeteer.launch({        
     // 設置false能夠看到頁面的執行步驟        
     headless: false,    
});複製代碼

再設置好瀏覽器的大小,而後打開文章列表頁面。ide

BASE_URL = 'https://www.jianshu.com';

//文章列表地址
HOME_URL = `${BASE_URL}/u/f46becd1ed83`;

const viewport_size = {        
    width: 0,        
    height: 0,    
};

const page = await browser.newPage();

//設置瀏覽器的寬、高p
age.setViewport(viewport_size);

//打開文章主頁await page.goto(HOME_URL);複製代碼



因爲默認只顯示第一頁的文章,後面的文章須要屢次從下到上的滑動才能加載出來。函數


這裏須要定義一個函數不停的做滑動操做,直到滑動到最底部,待頁面全部元素加載完成,才中止滑動。post


function autoScroll(page) {    
    return page.evaluate(() => {        
        return new Promise((resolve, reject) => {            
                var totalHeight = 0;            
                var distance = 100;            
                var timer = setInterval(() => {                
                        console.log('執行間斷函數');                
                        var scrollHeight = document.body.scrollHeight;                
                        window.scrollBy(0, distance);                
                        totalHeight += distance;                
                        if (totalHeight >= scrollHeight) {                    
                                console.log('滑動到底');                    
                                clearInterval(timer);                    
                                resolve();                
                           }           
                 }, 100);        })    });}複製代碼


待全部的文章都加載出來後,就能夠經過「eval」函數獲取文章元素,而後再經過 css 選擇器獲取到文章標題和頁面地址。


const articles = await page.$eval('.note-list', articles_element => {        
    const article_elements = articles_element.querySelectorAll('li');        
    const articleElementArray = Array.prototype.slice.call(article_elements);        
    return articleElementArray.map(item => {            
               const a_element = item.querySelector('.title');            
               return {                h
                        ref: a_element.getAttribute('href'),                
                        title: a_element.innerHTML.trim(),         
               };     });});複製代碼



獲取到全部文章的連接地址以後,就能夠經過遍歷列表去打開每一篇文章。


for (let article of articles) {        
        const articlePage = await browser.newPage();        
        articlePage.setViewport(viewport_size);        
        articlePage.goto(`${BASE_URL}${article.href}`, {            
            waitUntil: 'networkidle2'        });        
        articlePage.waitForSelector('.post');        
        console.log('文章詳情頁面加載完成');
}複製代碼


等文章詳情頁面加載徹底後,一樣須要滑動頁面到最底部,保證當前文章的文字信息、圖片都加載徹底。



爲了保證最後保存的頁面的美觀性,須要利用「CSS樣式」隱藏包含網站頂部、底部、評論、導航條等多餘的元素。


await articlePage.$eval('body', body => {            
    body.querySelector('.navbar').style.display = 'none';            
    body.querySelector('#note-fixed-ad-container').style.display = 'none';            
    body.querySelector('.note-bottom').style.display = 'none';           
    body.querySelector('.side-tool').style.display = 'none';           
    // body.querySelector('.author').style.display = 'none';            
    body.querySelector('.meta-bottom').style.display = 'none';            
    body.querySelector('#web-note-ad-1').style.display = 'none';            
    body.querySelector('#comment-list').style.display = 'none';            
    body.querySelector('.follow-detail').style.display = 'none';            
    body.querySelector('.show-foot').style.display = 'none';            
    Promise.resolve();        });複製代碼


最後利用「pdf」函數把當前頁面保存爲 pdf 格式的文件。


await page.emulateMedia('screen');        
await articlePage.pdf({            
    path: fileFullPath,            
    format: 'A4' });複製代碼


須要注意的是,爲了保證上面的函數正常的執行,須要修改瀏覽器打開的方式爲無頭模式,即:


const browser = await puppeteer.launch({        
    headless: true,    
});複製代碼



結 果 結 論


經過 node 命令就能夠執行這個 js 文件。


node jian_shu.js 複製代碼


因爲使用的是無頭瀏覽器執行的,這裏除了控制檯能顯示日誌信息,沒有任何操做。


待程序執行完畢以後,發現全部的文章都以 pdf 的形式保存到本地了。



我本文首發於公衆號「 AirPython 」,後臺回覆「 pdf 」便可獲取完整代碼。

相關文章
相關標籤/搜索