爬取某位大佬简书上全部文章并保存为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 」便可获取完整代码。

相关文章
相关标签/搜索