新手爬蟲,教你爬掘金(二)

木魚木心發表於2019-03-03

距離上次教程已經過了快兩週了,沒辦法啊,學業繁忙(¬、¬) (¬_¬)

本文用到的三個工具為

  • cheerio:jQuery語法,幫助你在非瀏覽器環境下解析網頁用的
    • 上次沒用到,這個肯定用到啦
  • segment 一個基於盤古詞庫的中文分詞工具,cnode大神寫的,手動@leizongmin大神

cheerio用法

const cheerio = require('cheerio'),
    $ = cheerio.load('<h2 class="title">Hello world</h2>');

$('h2.title').text('Hello there!');
$('h2').addClass('welcome');

$.html();
//=> <h2 class="title welcome">Hello there!</h2>
複製程式碼

額外用法戳這裡

segment 用法

const Segment = require('segment');
// 建立例項
const segment = new Segment();
// 使用預設的識別模組及字典,載入字典檔案需要1秒,僅初始化時執行一次即可
segment.useDefault();

// 開始分詞
console.log(segment.doSegment('這是一個基於Node.js的中文分詞模組。'));
// [ { w: '這是', p: 0 },
//   { w: '一個', p: 2097152 },
//   { w: '基於', p: 262144 },
//   { w: 'Node.js', p: 8 },
//   { w: '的', p: 8192 },
//   { w: '中文', p: 1048576 },
//   { w: '分詞', p: 4096 },
//   { w: '模組', p: 1048576 },
//   { w: '。', p: 2048 } ]

複製程式碼

但是我們一般不需要輸出詞性,也不需要輸出多餘的標點符號,所以

const result = segment.doSegment(text, {
  simple: true,          //不輸出詞性
  stripPunctuation: true //去除標點符號
});
//  [ '這是', '一個', '基於', 'Node.js', '的', '中文', '分詞', '模組' ]
複製程式碼

更高階用法見segment

全部程式碼見github

基本用法也瞭解了,接下來進入正題吧╰(●’◡’●)╮

爬取圖片

image.png
image.png
爬取到的圖片

image.png

可以看到img元素上面src和自定義的data-src屬性都帶有圖片地址,至於為什麼再下面的程式碼中我沒有獲取src的值 完全是我太菜了◔ ‸◔?,img.eq(i).src 獲取不到值,只能 prop('data-src')

自定義屬性相容性很差勁

Internet Explorer 11+ Chrome 8+ Firefox 6.0+ Opera 11.10+ Safari 6+

熟悉正則的同學,稍微分析下圖片的地址就可以通過正則來獲取url了,以下是我給出的示例 /(https:\/\/user-gold-cdn).+?\/ignore-error\/1/g 需要注意的是/的轉義,以及惰性匹配.+?,關於惰性匹配我這裡不打算說了(稍微提一下下(//▽//),其實就是匹配符合要求的最短串),要是說起來又可以寫一大堆了

想詳細瞭解的同學可以看看這個解釋

/**
 * 
 * @param {any} $ cheerio
 * @param {any} request 請求函式
 */
function saveImg($, request) {
  const img = $('.lazyload');
  const origin = request.default();  //這裡是我對request進行了一個簡單的封裝,default返回未封裝的request
  for (let i = 0; i < img.length; ++i) {
    //data.body.match(/(https:\/\/user-gold-cdn).+?\/ignore-error\/1/g)
    let src = img.eq(i).prop('data-src');
    let name = src.match(/\/.{16}\?/g) && src.match(/\/.{16}\?/g)[0].slice(1, -1); //匹配出圖片名稱
    if (name) {
      origin.get(src).pipe(fs.createWriteStream(`./images/${name}.png`)); //愉快的下載圖片
    }
  }
}
複製程式碼

資料處理

介紹下用的資料結構Map,用來儲存詞頻(詞-詞出現的次數)

類似於物件,也是鍵值對的集合,但是“鍵”的範圍不限於字串,各種型別的值(包括物件)都可以當作鍵。也就是說,Object 結構提供了“字串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的資料結構,Map 比 Object 更合適。

其實對於本文來講,鍵都為字串,用物件也完全沒有問題,使用Map完全是為了嚐鮮 (●’◡’●)ノ 關於Map複製的解釋,這一點和物件又不一樣

Map複製

image.png
Object複製,作為引數傳進建構函式並不可以複製
image.png

async function getPage(request, url) {
  const data = await request.get({ url });
  const $ = cheerio.load(data.body);
  saveImg($, request);
  //獲取內容
  let length = $('p').length;
  for (let i = 0; i < length; ++i) {
    let result = segment.doSegment(
      $('p')  //大部分內容都是p標籤包裹的,這裡不做過複雜的處理
        .eq(i)
        .text(),
      {
        simple: true, //不輸出詞性
        stripPunctuation: true //去除標點符號
      }
    );
    result.forEach((item, key) => {
      map.set(item, map.get(item) + 1 || 1); //1 + undefined || 1 => 1
    });
  }
  map = sortToken(map);
}

function sortToken(map) {
  const words = {}; //儲存詞
  let mapCopy = new Map(map); //獲取副本,Map直接賦值應該也是地址引用,參見上文
  map.forEach((value, key) => {
    //分詞長度大於1
    if (value !== 1 && key.length > 1) { //詞頻大於1且不是單個字的留下,單字沒有什麼號分析的吧?
      words[key] = value;
    }
    if (value === 1) { //詞頻過低,直接刷了
      mapCopy.delete(key);
    }
  });
  const keys = Object.keys(words);
  //排序
  keys.sort((a, b) => {
    return words[b] - words[a];
  });
  //  每篇文章詞頻最高的20個詞,有興趣瞭解的同學可以去看看top k演算法(我們是獲取前k個,它是獲取第k個,但是它這樣需要把前k個都儲存下來,用來比較哪些是前k大)
  //  我這個方法只是粗略的獲取詞頻最高的20個詞,實際上會有偏差,假設第一次排序,第十一個詞詞頻為23,而第二次排序,第十個詞詞頻為12,這樣本來之前詞頻高的反被刷了
  //  但這樣的好處是節省記憶體(其實是假的),真正的可以利用最大堆和利用資料庫儲存,這樣就不用存在記憶體了
  //  最後爬取完了,從資料庫取出資料,再參照top k思想演算法得出結果
  keys.slice(0, 20).forEach(item => {
    console.log(item, words[item]);
  });
  //返回分詞中詞頻為1的分詞
  return mapCopy;
}
複製程式碼

image.png
我爬取了最新評論前100個文章的內容進行了分析,得出了以上結果 可以看到,程式碼方法函式物件執行呼叫元件等等跟程式碼有關的中文詞語都出現了 不過還是一個最受歡迎,出現次數快1000次了 (」゜ロ゜)」 有興趣的同學,可以使用英文分詞進行分析,分析下程式設計師們寫文章喜歡寫什麼程式碼
image.png
以上是我分析的一篇文章裡面的英文

還可以再分析標題,然後還可以改進排序演算法,直接把整個article-content(class)的text進行分析,而不是像我一樣,只是分析p標籤 (๑•̀_•́๑) ,最後用視覺化工具(例如e-cahrt)把資料展示出來

喜歡的同學可以star哦github

以上,如有錯誤,歡迎大家指正

相關文章