1. 程式人生 > >nodejs 爬取前端面經並生成詞雲

nodejs 爬取前端面經並生成詞雲

前言

最近有 CVTE 的面試但是一直沒有到我,昨天下午牛客網上 CVTE 前端的面經突然多了起來,大致看了一下,和自己之前整理的知識點差的不多,但是基本都問了 nodejs 的問題。正好之前的爬蟲都沒有做過詞雲,藉著這個機會爬一下牛客網的前端面經,順便生成詞雲,看看面試中哪些比較重要

準備工作

基本的步驟前面幾篇爬蟲都有了,不過還是重寫寫一下吧。

  • 建立一個資料夾,我這裡叫 spider
  • 右鍵點選這個資料夾,有個 CMD 快速通道,點選開啟 CMD
  • 執行 npm init 命令,一路回車,最後 yes
    這裡寫圖片描述
    執行完以後,會多出一個 package.json 的資料夾,裡面放的是一些專案的資訊
  • 在 spider 資料夾下新建一個 index.js 檔案用來寫我們的程式碼。
  • 建立一個 data 資料夾用於放我們所需要的資料
  • 安裝專案所需要的依賴包
    npm istall cheerio --save 這個是用來提取 html 頁面內容的
    npm istall async --save 這個是用來非同步併發爬蟲的
    npm istall node-gyp --save 這個用於編譯原生C++擴充套件模組
    npm istall nodejieba --save 這個是用來分詞的
    其中安裝的時候有點小坑,需要有VC++庫、python庫,可以參考這篇文章 nodejieba安裝記(Windows)

網頁結構分析

基本的準備工作做完了,下面開始分析牛客網的網頁,其實沒什麼難的,很容易分析出來我們需要的網頁在這
這裡寫圖片描述
即類名為 discuss-mainclearfix 下面的第一個 <a> 元素。程式碼如下

$('li .discuss-main.clearfix').each(function(){
    var title=$(this).children().first().text();     
    // 這裡是為了根據關鍵詞查詢,如果標題有我們設定的關鍵詞,再把連結放到陣列中          
          if(title.indexOf(keyWord)>=0
){ var search=$(this).children().first().attr('href'); let nextLink = "https://www.nowcoder.com" + search; urlList.push(nextLink); } })

接下來就是頁面裡面的實際內容,也很容易分析
這裡寫圖片描述
即類名為 post-topic-des 下的文字
至此頁面分析工作做完,接下來就是使用 nodejieba 模組來分詞

分詞生成詞雲

關於 nodejieba 的用法可以參考這篇文章 使用 Node.js 對文字內容分詞和關鍵詞抽取
由於 const result = nodejieba.extract(data, 40); 得到的結果是物件,所以寫入檔案之前需要將其轉換為 JSON 字串,用 JSON.stringify(result)。然後對字串進行處理
程式碼如下

function wordCluod(){
    fs.readFile('./data/word.txt', 'utf8', function(err, data){
        nodejieba.load({
            userDict: './user.utf8',
        });
        const result = nodejieba.extract(data, 20); 
        let text = "";
        for(let i in result){
            text += text[i].word + " " + Math.ceil(text[i].weight) + "\n";
        }
        fs.writeFile('./data/'+'wordCloud'+'.txt',text, 'utf-8', function (err) {
          if (err) {
            console.log(err);
          }
        });
    }); 
}

但是這樣有一個問題,因為他是根據詞頻選取的,所以有一些沒用的詞比如面試官,一面等詞語就會混入到我們的詞中,所以我們需要將有用的資訊過濾出來

const tagList = ['原型', '閉包', 'HTTP', 'CORP', 'TCP', 'https','跨域','XSS','安全','事件','VUE','CSS','演算法','執行緒','NODE'];
let textNo = JSON.stringify(result.filter(item => tagList.indexOf(item.word.toUpperCase()) >= 0));  

生成資料如下
這裡寫圖片描述
和我想象的還是有些差距的,可能程式並不是太完善,然後就可以把資料匯入到任何一個線上詞雲裡面了

完整程式碼

const https=require('https');
const fs=require('fs');
const request=require('request');
const async=require('async');
const cheerio = require('cheerio');
const nodejieba = require('nodejieba');
const startPage =0;//開始頁
const endPage = 4;//結束頁
const keyWord = "";//關鍵詞
const keyWord2 = "前端";
let page=startPage;
let i=0;
//初始url
const url={ 
    hostname: 'www.nowcoder.com',
    path: '/discuss?type=2&order=' + startPage,
    headers: {
        'Content-Type': 'text/html',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36',  
  }
}
let urlList=[];//儲存圖片頁面地址
//獲取圖片所在頁面
function getUrl(url){
    //採用http模組向伺服器發起一次get請求  
    https.get(url,function(res){
        var html='';
        //res.setEncoding('binary');
        //監聽data事件,每次取一塊資料
        res.on('data',function(chunk){
            html+=chunk;
        });
        res.on('end',function(){
            var $ = cheerio.load(html); //採用cheerio模組解析html
            $('li .discuss-main.clearfix').each(function(){
                var title=$(this).children().first().text();               
                if(title.indexOf(keyWord2)>=0){
                    var search=$(this).children().first().attr('href');
                    //console.log(search);
                    let nextLink = "https://www.nowcoder.com" + search;
                    urlList.push(nextLink);
                }
            })
            page++;     
            if(page<=endPage){
                let tempUrl='https://www.nowcoder.com/discuss?type=2&order=' + page;
                getUrl(tempUrl);        
            }else{
                fetchPage();
            }
        })
    }).on('err',function(err){
        console.log(err);
    })
}
function fetchPage(){
    //非同步控制併發
    async.mapLimit(urlList,5,function(url,callback){
        https.get(url,function(res){
            //console.log(url);
            let html='';
            //res.setEncoding('binary');
            res.on('data',function(chunk){
                html+=chunk;
            })
            res.on('end',function(){
                //console.log(html);
                var $ = cheerio.load(html); //採用cheerio模組解析html
                var content = $('.post-topic-des').text().trim();
                //console.log(content);
                appendText(content);
            })

        }).on('err',function(err){
                console.log(err);
            });
        callback(null,'成功');
    },
    function(err,result){
        if (err){
                console.log(err)
            }
        else{
            console.log('結束');
            wordCluod();
            }           
        })
}
function appendText(text){
    fs.appendFile('./data/word.txt', text, 'utf-8', function (err) {
      if (err) {
        console.log(err);
      }
    });
}
// 生成詞雲資料
function wordCluod(){
    fs.readFile('./data/word.txt', 'utf8', function(err, data){
        nodejieba.load({
            userDict: './user.utf8',
        });
        const result = nodejieba.extract(data, 120);
        const tagList = ['原型', '閉包', 'HTTP', 'CORP', 'TCP', 'HTTPS','跨域','XSS','安全','事件迴圈','VUE','CSS','演算法','執行緒','NODE','','快取','記憶體','作用域鏈','垂直居中','佈局','狀態碼','原型鏈','ES6','箭頭函式',"PROMISE",'垃圾回收','優化'];
        let textNo = JSON.stringify(result.filter(item => tagList.indexOf(item.word.toUpperCase()) >= 0));      
        let text = JSON.parse(textNo);
        let temp = "";
        for(let i in text){
            temp += text[i].word + " " + Math.ceil(text[i].weight) + "\n";
        }
        fs.writeFile('./data/'+'wordCloud'+'.txt',temp, 'utf-8', function (err) {
          if (err) {
            console.log(err);
          }
        });
    });

}


getUrl(url);//主程式開始執行

效果圖

最終效果如圖所示
這裡寫圖片描述

github 地址