從一個白嫖的角度作一個node爬蟲

故事背景

今天原本是想寫一篇webpack的博客的,而後呢因爲擔憂拉下或者講不清楚一些細節,本着負責任的原則,想在網上覆習一下再寫css

而後我找到了一個webpack的網站,可是正在我看的津津有味的時候,網頁忽然給我蹦出來一個彈窗html

image.png

大概是這麼個意思,我在控制檯刪除元素或者試圖取消該網站的監聽事件都無果,刪不乾淨,總之就是不讓你舒舒服服的往下看,不嫌墨跡的話也能看node

不是說好的技術無界限的嘛,爲啥又要這樣。。。,難不成還要買一本?jquery

當發現他實際上是有完整文章渲染出來,只是阻攔了往下瀏覽的時候,我得錢包提醒了我一下白嫖的時候到了webpack

這麼多網頁所有手動保存是比較墨跡的,顯然寫一個node爬蟲是比較不錯的選擇ios

注: 最後因爲考慮到爬這種加密的可能不太合適,因此後來找了一個其餘的一個公開資源的網站,畢竟博客仍是要寫的o( ̄▽ ̄)ブ

核心模塊

主要用到的模塊以下web

  • cheerio //頁面抓取
  • axios //請求處理
  • fs //文件模塊
npm i cheerio axios
const cheerio = require('cheerio') 獲取頁面內容
const axios = require('axios'); 
const fs = require('fs');

這裏請求的工具選擇的axios,由於平時用到的比較多,比較熟悉npm

fs 操做本地文件,node的一個內置的模塊axios

而後cheerio瀏覽器

cheerio

cheerio是jquery核心功能的一個快速靈活而又簡潔的實現,主要是爲了用在服務器端須要對DOM進行操做的地方

在node環境裏邊直接抓dom是比較墨跡的,cheerio這個模塊能夠用來解析html,和jquery同樣。

有了這個就好說了,首先把整個網站的導航拿下來,而後根據網站的導航地址遍歷對應的鏈接,最後根據本身的需求把須要的內容下載到本地就能夠了

怎麼拿到頁面的html

  • 咱們平時在寫業務的時候,都是發送請求到服務端,而後服務端返回給數據,那麼html怎麼獲取
  • 回想一下咱們打開一個網址以後,瀏覽器事怎麼把html渲染到頁面上的,好比說我打開一個百度的首頁

image.png

  • 能夠在控制檯裏邊看到,有一個和域名同樣的請求,類型是document,打開詳情後能夠看到裏邊是baidu的首頁,也就是說其實就是一個get請求,只是返回的東西不同

獲取頁面導航

上邊說了那麼多,如今進入正題

// 配置一下請求地址
`axios.defaults.baseURL = 'https://xxxxxx.cn/'`

`axios.get('/').then(res => {console.log(res.data)})`

嘗試着請求一下首頁,獲得結果以下

image.png

是咱們想要的東西沒錯

而後接下來 咱們按照jquery的方法 找到導航那塊html

image.png

假如說咱們須要的模塊在.summary這個class下邊,而後須要獲取他裏邊全部有內容的標題路徑的時候,代碼以下

/**
 * @Date: 2020-04-10 16:11:02
 * @information: 獲取頁面內容
 */
async init() {
    let result = await axios.get('/')
    let page = result.data
    let $ = cheerio.load(page)
    let navNode = $('.summary')
    // 遞歸標題和路徑
    navNode.children().each((navIndex, item) => {
        let itemNode = $(item)
        let ulNode = itemNode.find('ul')
        if (!ulNode.length) return
        // 獲取全部大標題
        let title = itemNode.find('a').first().text().replace(/\s/g, "")
        this.urlMap.set(title, new Map())
        let contentNode = $(ulNode)
        contentNode.children().each((contentIndex, item) => {
            let body = $(item).find('a')
            let contentMap = this.urlMap.get(title)
            contentMap.set(body.text().replace(/\s/g, ""), body.attr('href'))
        })
    })
}

打印一下結果
image.png

獲取頁面內容

在獲取完導航的路由以後剩下的就是遍歷取內容了,首先建立一個保存文件的文件夾路徑

// 建立保存的路徑文件夾
    let writeUrl = `xxxxxx`
    //在建立以前判斷一下是否是已經有了這個文件夾
    let hasDir = fs.existsSync(writeUrl);
    !hasDir && fs.mkdirSync(writeUrl);

而後從剛纔獲取的導航的map 裏邊讀取路徑

```
    // 創建子文件夾和內容
    this.urlMap.forEach((item, titleIndex) => {
        let dirName = `${writeUrl}\\${titleIndex}`
        let hasSubDir = fs.existsSync(dirName);
        !hasSubDir && fs.mkdirSync(dirName)
        // 抓文件並寫文件
        item.forEach(async (item, index) => {
            let result = await axios.get(`/${encodeURI(item)}`)
            let page = result.data
            let $ = cheerio.load(page)
            let contentNode = $('.search-noresults')
            fs.writeFile(`${dirName}\\${index}.html`, contentNode, () => {
                // console.log(`${index}:寫入成功`)
            })
        })
    })
```

把請求到的頁面拿到對應dom,直接往html裏邊一塞,完活,打開看一下

emm 看卻是能看 不過樣式實在是太難看了,打開爬取的網站的源代碼找到他的資源路徑加進去

` contentNode.before('<link rel="stylesheet" href="https://xxxxxx/style.css"></link>')`

還有圖片加載不出來的問題,同理查找全部的img標籤,若是有圖片的話把src屬性的值拿下來取對應的地址下載

```
            let imgNodeArr = contentNode.find('img')
            if (!imgNodeArr.length) return
            imgNodeArr.each(async (i, el) => {
                let src = $(el).attr('src')
                let imgPath = `/${encodeURI(titleIndex.split("/")[0].replace('第', "").replace('章', ""))}/${encodeURI(src)}`
                if ($(el).attr('src').includes('http')) return
                // 下載圖片存儲到本地
                let result = await axios.get(`${imgPath}`, { responseType: 'stream' })
                let hasImgDir = fs.existsSync(`${dirName}\\img`);
                !hasImgDir && fs.mkdirSync(`${dirName}\\img`)
                result.data.pipe(fs.createWriteStream(`${dirName}/${src}`));
                // console.log(`寫入圖片成功:${dirName}\\${src}`)
            })
```

http地址的圖片就不須要下載了,既然他能拿到,那麼我們直接用就行了

這裏請求的時候必定要加{ responseType: 'stream' },否則的話圖片下載回來會打不開格式

`result.data.pipe(fs.createWriteStream(`${dirName}/${src}`));`

把文件流寫入到文件裏邊生成圖片,運行一下看看效果

image.png

運行結果

ok,至此一個簡單的爬蟲就完成了,最後放上效果圖

image.pngimage.pngimage.pngimage.png

喜歡的點個贊把

完事撒花o( ̄▽ ̄)ブ,過幾天繼續webpack,若有不足之處,請斧正

相關文章
相關標籤/搜索