現現在,咱們處於一個信息碎片化的信息時代,遇到好的文章都有隨手收藏的習慣。但過一段時間,當你想要從新查看這篇文章的時候,發現文章已經被移除或莫名其妙地消失了。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 」便可獲取完整代碼。