nodejs爬蟲爬取 喜馬拉雅FM 指定主播歌單並下載

最近一直在學英語,所以寫了個爬蟲爬取歌單並下載,而後隨時均可以聽。
GitHub地址:https://github.com/leeseean/nodejs-crawler。

頁面分析

要用爬蟲下載音頻,那天然是要找到音頻連接了。而網站的音頻連接沒有直接暴露出來,所以須要分析找出獲取音頻連接的辦法。
進入喜馬拉雅官網主頁,隨便找一個主播的頁面進入,我這裏找的是[英語主播Emily](http://www.ximalaya.com/29101549/album/2801092)的主頁,而後裏面有她的播單,F12打開一看這些歌單都沒有直接寫音頻連接。可是仍是有一些規律的,每一個音頻都有一個ID,後面點擊這個音頻的時候,會有一個AJAX請求,請求的連接就包含這個ID,這個請求會返回音頻的真實連接,這樣就拿到了音頻連接。如圖

clipboard.png

點擊音頻發出AJAX請求,請求返回數據裏面包含真實音頻連接,如圖

clipboard.png

寫爬蟲

需求分析完了,那如今固然是寫爬蟲了,首先爬取主播頁,拿到ID,而後根據ID發送AJAX請求拿到真實音頻地址。
用的模塊有cheerio和request。
const request = require('request');

   const fs = require('fs');
   const cheerio = require('cheerio');
   //這個時間後面設置cookie須要用到
   const formatTime = (new Date()).toLocaleString().replace(/\//g, '-').replace(/[\u4E00-\u9FA5]/g, '');

直接用request(url,callcack)發現返回一個301重定向的頁面,所以要設置uset-agent和cookie才能正確訪問到頁面。如圖,
圖片描述
相應設置代碼以下:html

function setOptions({
    url,
    cookies,
    contentType
}) {
    const jar = request.jar();
    jar.setCookie(request.cookie(cookies), url);
    return {
        url: url,
        jar: jar,
        method: 'GET',
        headers: {
            "Connection": "keep-alive",
            "Content-Type": contentType,
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
        }
    };
}
請求到頁面到拿到全部音頻的ID,並根據音頻ID發送AJAX請求,代碼以下:
function getListIdsCallback(error, response, body) {
    if (!error && response.statusCode === 200) {
        const listIds = [];//存放全部音頻ID的數組
        const $ = cheerio.load(body);
        $('div.album_soundlist > ul > li > div > a.title').each((index, item) => {
            listIds.push($(item).attr('href').split('/')[3]);
        });   
        getAudioJson(listIds);//獲取音頻真實連接並下載音頻
    }
}

獲取音頻真實連接並下載音頻

代碼以下:
function getAudioJson(listIds) {
    if (listIds.length === 0) {
        return;
    }
    const id = listIds.shift();
    request(setOptions({
        url: `http://www.ximalaya.com/tracks/${id}.json`,
        cookies: `_xmLog=xm_1510364052559_j9unrdjjmwt7gx; login_from=qq; nickname=All2005; login_type=QQ; 1&remember_me=y; 1&_token=96575028&ecb632710362104767080ce01362b33cc881; trackType=web; x_xmly_traffic=utm_source%3A%26utm_medium%3A%26utm_campaign%3A%26utm_content%3A%26utm_term%3A%26utm_from%3A; Hm_lvt_4a7d8ec50cfd6af753c4f8aee3425070=1510364053; Hm_lpvt_4a7d8ec50cfd6af753c4f8aee3425070=1510376453; _ga=GA1.2.1519968795.1510364053; _gat=1; 1_l_flag=96575028&ecb632710362104767080ce01362b33cc881_${formatTime}; msgwarn=%7B%22category%22%3A%22%22%2C%22newMessage%22%3A0%2C%22newNotice%22%3A0%2C%22newComment%22%3A0%2C%22newQuan%22%3A0%2C%22newFollower%22%3A0%2C%22newLikes%22%3A0%7D`,
        contentType: 'text/html;charset=utf-8',
    }), (error, response, body) => {
        return getAudioJsonCallback(error, response, body, listIds);
    });
}

function getAudioJsonCallback(error, response, body, listIds) {
    if (!error && response.statusCode === 200) {
        const JsonData = JSON.parse(body);
        downloadFile(JsonData['play_path'], `${JsonData['id']}.m4a`, (e) => {
            console.log(`下載完畢${JsonData['id']}`);
            getAudioJson(listIds);
        });
    }
}


function downloadFile(url, filename, callback) {
    const stream = fs.createWriteStream(filename);
    request(url).pipe(stream).on('close', callback);
}

代碼技巧

這裏獲取到listIds一開始是使用一個循環去下載,後面發現一會兒同時開啓幾十個下載node根本吃不消,下下來的音頻不完整,後面採用一個一個下載的辦法,就徹底解決的這個問題。相關代碼以下:
const id = listIds.shift();//每次取出一個id去請求
downloadFile(JsonData['play_path'], `${JsonData['id']}.m4a`, (e) => {
    console.log(`下載完畢${JsonData['id']}`);
    getAudioJson(listIds);//下載完後再次啓動請求
});

總結

先寫到這了。爬蟲的關鍵就是找出真實地址,而後抓頁面的時候若是抓不到記得補充cookie,設置user-agent等一類參數。
相關文章
相關標籤/搜索