溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

使用Puppeteer爬取微信文章的實(shí)現(xiàn)

發(fā)布時(shí)間:2020-09-06 08:34:32 來源:腳本之家 閱讀:260 作者:劉一筆 欄目:開發(fā)技術(shù)

一朋友在群里問有沒有什么辦法能夠一次性把這個(gè)鏈接 里的文章保存下來。點(diǎn)開可以看到,其實(shí)就是一個(gè)文章合集。所以需求就是,把這個(gè)文檔中的鏈接里的文章挨個(gè)保存下來。保存形式可以有很多種,可以是圖片,也可以是網(wǎng)頁。這里因?yàn)槭褂?puppeteer 庫的原因,故選擇保存格式格式為PDF。

需求解構(gòu)

完成整個(gè)動作,主要分為這兩個(gè)部分。獲取文檔內(nèi)所有文章的鏈接;把每個(gè)鏈接里的內(nèi)容保存為PDF文件。

對于獲取鏈接,有兩條路,一是使用request模塊請求該網(wǎng)址獲取文檔;二是把網(wǎng)頁保存到本地使用fs模塊獲取文檔內(nèi)容。拿到文檔也就是整個(gè)HTML文檔后,一開始沒想到什么好法子來拿到全部文章鏈接。如果直接在網(wǎng)頁那就好辦,直接DOM的 quertSelectorAll API配合CSS選擇器就可以非常方便地拿到所有 a 鏈接中的 href 屬性。但這里是Node,是DOM外之地。又想到的是直接使用正則匹配,后來還是放棄了這個(gè)做法。在google搜了下才發(fā)現(xiàn)竟然忘了 cheerio 這個(gè)好東西。 cheerio 是一個(gè)專門為服務(wù)端設(shè)計(jì)的快速靈活而簡潔得jQuery實(shí)現(xiàn)。

對于保存網(wǎng)頁內(nèi)容,我所知道的常規(guī)操作是保存為PDF文件,恰巧之前剛知道的 puppeteer 滿足這樣的需求。 puppeteer 是一個(gè)由 chrome devtools 團(tuán)隊(duì)維護(hù)的提供了控制chrome瀏覽器高級API的一個(gè)Node庫。除去爬取網(wǎng)頁內(nèi)容保存為PDF文件外,它還可以作為服務(wù)端渲染的一個(gè)方案以及實(shí)現(xiàn)自動化測試的一個(gè)方案。

需求實(shí)現(xiàn)

獲取鏈接

先上這部分代碼

const getHref = function () {
 let file = fs.readFileSync('./index.html').toString()
 const $ = cheerio.load(file)
 let hrefs = $('#sam').find('a')
 for (e in hrefs) {
  if (hrefs[e].attribs && hrefs[e].attribs['href']) {
   hrefArr.push({
    index: e,
    href: hrefs[e].attribs['href']
   })
  }
 }
 fs.writeFileSync('hrefJson.json', JSON.stringify(hrefArr))
}

因?yàn)楹竺娴拇a都依賴到讀取的文件,所以這里用的是readFileSync方法。如果沒有聲明返回內(nèi)容的格式,那默認(rèn)是Buffer格式。可以選擇填寫 utf8 格式,或者直接在該方法后面使用 toString 方法。

兩行代碼用cheerio拿到所有所有鏈接的DOM元素后,挨個(gè)將其處理為方便后面要用到的格式??紤]到可能存在a標(biāo)簽沒有href屬性的情況,這里還對其進(jìn)行了判斷,不過這也是后面調(diào)試程序時(shí)才發(fā)現(xiàn)的bug。

如果需要將所有的鏈接另外保存起來,使用 writeFile 方法。

存為PDF

同樣,先上這部分代碼。

const saveToPdf = function () {
 async () => {
  const browser = await puppeteer.launch({
   executablePath: './chrome-win/chrome.exe',
  });

  // 鏈接計(jì)數(shù)
  let i = 0

  async function getPage() {
   const page = await browser.newPage();
   await page.goto(hrefArr[i]['href'], { waitUntil: 'domcontentloaded' });

   // 網(wǎng)頁標(biāo)題
   let pageTitle

   if (hrefArr[i]['href'].includes('weixin')) {
    pageTitle = await page.$eval('meta[property="og:title"]', el => el.content)
   } else {
    pageTitle = await page.$eval('title', el => el.innerHTML)
   }

   let title = pageTitle.trim()
   // 去掉斜桿
   let titlea = title.replace(/\s*/g, "")
   // 去掉豎線
   let titleb = titlea.replace(/\|/g, "");
   
   await page.pdf({ path: `${i}${titleb}.pdf` });

   i++

   if (i < hrefArr.length) {
    getPage()
   } else {
    await browser.close();
   }
  }
  getPage()
 }
}

因?yàn)樾枰却齝hrome瀏覽器的打開,以及其他可能的異步請求。最外層使用了async 配合箭頭函數(shù)將真正的執(zhí)行代碼包住。

在用 npm 安裝 puppetter 時(shí),因?yàn)槟J(rèn)會下載chrome瀏覽器,而服務(wù)器在國外,一般都無法下載成功。當(dāng)然也有相應(yīng)的解決方案,這里我就不展開了。如果安裝 puppeteer ,可以參開 這篇文章 或者直接谷歌搜下。

在前一部分說到,我們需要把不止一個(gè)鏈接里的內(nèi)容保存為PDF,所以使用了變量 i 來標(biāo)識每一次需要訪問的鏈接。

對于獲取網(wǎng)頁標(biāo)題,當(dāng)時(shí)確實(shí)費(fèi)了點(diǎn)時(shí)間才處理好拿到已有鏈接的網(wǎng)頁標(biāo)題。所以鏈接中主要有兩種網(wǎng)站的鏈接,一類是微信公眾號文章,另一類是新浪財(cái)新這種網(wǎng)站。微信文章里頭沒有像新浪這樣直接給出 title 內(nèi)容。

使用Puppeteer爬取微信文章的實(shí)現(xiàn) 

使用Puppeteer爬取微信文章的實(shí)現(xiàn)

這個(gè)時(shí)候就要用到 page 類中的 $eval 方法, $eval 方法主要有兩個(gè)參數(shù),一是選擇器,二是在瀏覽器上下文中執(zhí)行的函數(shù)。$eval方法會頁面中運(yùn)行document.querySelector方法,并將其返回值傳遞給第二個(gè)參數(shù),也就是我們寫好的方法中。以獲取新浪網(wǎng)頁文章title為例, title 為傳入選擇器,我們需要的是其標(biāo)簽內(nèi)容。

pageTitle = await page.$eval('title', el => el.innerHTML)

在產(chǎn)生文件名的過程中,由于文件夾還是文件路徑的一部分。此時(shí)還需要考慮到windows文件路徑規(guī)范。但網(wǎng)頁中的標(biāo)題并不受此規(guī)范限制,由此產(chǎn)生矛盾。這個(gè)問題也是后面調(diào)試的時(shí)候才發(fā)現(xiàn),一開始寫代碼并沒有想到這個(gè)問題。即需要去除標(biāo)題中的斜杠豎桿還有空格等字符。

每獲取完一個(gè)鏈接的內(nèi)容后,就將鏈接位置標(biāo)識 i + 1,知道所有鏈接內(nèi)容保存完畢,關(guān)閉打開的網(wǎng)頁。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI