puppeteer小說(shuō)爬蟲(chóng)(3)--優(yōu)化處理

????????上一篇我們講述了爬取小說(shuō)的基本邏輯灵迫,但是這樣還是遠(yuǎn)遠(yuǎn)不夠的书幕。一來(lái)沒(méi)有一些異常的處理象迎,二來(lái)爬取速度過(guò)快還容易引起小說(shuō)網(wǎng)站的反爬蟲(chóng)機(jī)制導(dǎo)致IP被禁,所以接下來(lái)我們一一把這些邏輯優(yōu)化處理下慰枕,讓我們更加優(yōu)雅的爬取到我們想要的內(nèi)容具则。

使用瀏覽器代理

????????無(wú)論怎樣,爬取小說(shuō)最好都是要使用代理服務(wù)器具帮。這樣即使觸發(fā)了反爬蟲(chóng)機(jī)制對(duì)自身的IP也不會(huì)有太大的影響博肋,這對(duì)開(kāi)發(fā)階段尤其重要,甚至對(duì)以后再次爬取也有不小的幫助蜂厅。

原理也很簡(jiǎn)單
var browser = await puppeteer.launch({
                headless:true,
                args: [
                '--proxy-server=socks5://127.0.0.1:1080'
                ]
            })

????????在創(chuàng)建瀏覽器的時(shí)候添加--proxy-server參數(shù)即可匪凡。當(dāng)然也有很多其他可選的參數(shù)。具體可以參考chrome瀏覽器運(yùn)行參數(shù)幫助掘猿。這里我提供一個(gè)chrome瀏覽器運(yùn)行參數(shù)的參考網(wǎng)站病游,里面涵蓋了幾乎所有chrome的運(yùn)行參數(shù)。chrome args helper

????????ok稠通,原理知道后我們就去找代理的服務(wù)器吧衬衬。好在這方面網(wǎng)上一點(diǎn)也不缺,隨便搜以下代理服務(wù)器改橘,我就找到不少滋尉。這里提供一個(gè)網(wǎng)站,提供 免費(fèi)代理服務(wù)器唧龄。

代理服務(wù)器地址

????????具體怎么去獲取呢?這里也不繞彎子了奸远。還是通過(guò)puppeteer來(lái)獲得這里我們所需要的信息既棺。

新建一個(gè)文件 proxyserver.js
const puppeteer = require('puppeteer')

//max retry times
const MAX_RT = 3;

function getproxylist() {

    return new Promise(async (resolve, reject) => {
        
        var tempbrowser;
        for (var i = MAX_RT; i > 0; i--) {

            if (tempbrowser) {
                break;
            }

            console.log('start to init browser...');
            tempbrowser = await puppeteer.launch({
            headless:true
            }).catch(ex => {
                if (i-1 > 0) {
                    console.log('browser launch failed. now retry...');
                } else {
                    console.log('browser launch failed!');
                }
                
            });
        }

        if (!tempbrowser) {
            reject('fail to launch browser');
            return;
        }
        const browser = tempbrowser;

        console.log('start to new page...');
        var page = await browser.newPage().catch(ex=>{
            console.log(ex);
        });
        if (!page) {
            reject('fail to open page!');
            return;
        }

        var respond;
        for (var i = MAX_RT; i > 0; i--) {

            if (respond) {
                break;
            }
            
            console.log('start to goto page...');
            respond = await page.goto("https://www.socks-proxy.net/", {
                'waitUntil':'domcontentloaded',
                'timeout':120000
            }).catch(ex=>{
                if(i-1 > 0) {
                    console.log('fail to goto website. now retry...');
                } else {
                    console.log('fail to goto website!');
                }
                
            });
        }
        if (!respond) {
            reject('fail to go to website!');
            return;
        }

        console.log('start to find element in page...');
        var layoutVisible = await page.waitForSelector('#list .container table tbody').catch(ex=>{
            console.log("oh....no...!!!, i can not see anything!!!");
        });
        if (!layoutVisible) {
            reject('layout is invisible!');
            return;
        }       
        console.log('start to get info from element...');
        var proxyModelArray = await page.evaluate(async () => {

            let list = document.querySelectorAll('#list .container table tbody tr');
            if (!list) {
                return;
            }
            let result = [];

            for (var i = 0; i < list.length; i++) {
                var row = list[i];
                var cells = row.cells;

                var ip = cells[0].textContent;
                var port = cells[1].textContent;
                var code = cells[2].textContent;
                var version = cells[4].textContent;

                var proxyServerModel = {
                    'ip' : ip,
                    'port' : port,
                    'code' : code,
                    'version' : version
                }
                result.push(proxyServerModel);              
            }
            return result;      

        });

        await browser.close().catch(ex=>{
            console.log('fail to close the browser!');
        });
        console.log('close the browser');

        //console.log(proxyModelArray);
        if (!proxyModelArray || proxyModelArray.length === 0) {
            reject();
            return;
        }
        resolve(proxyModelArray);
        
        
    })  
}

// async function test() {
//
//  var proxylist;
//  for (var i = 0; i < MAX_RT; i++) {
//
//      if (proxylist) {
//          break;
//      }
//
//      console.log('start get proxylist from web...');
//      proxylist = await getproxylist().catch(ex=> {
//          if (i+1<MAX_RT) {
//              console.log('fail to get proxylist. now retry...');
//          } else {
//              console.log('fail to get proxylist. end!!!');
//          }
//      });
//  }
//  if (!proxylist) {
//      console.log('fail to get proxylist!!!');
//      return;
//  }
//  console.log(proxylist);
// }
// test();

module.exports.getProxyList = getproxylist;
把test的注釋取消運(yùn)行下可以得到以下結(jié)果:
start get proxylist from web...
start to init browser...
start to new page...
start to goto page...
start to find element in page...
start to get info from element...
close the browser
[ { ip: '103.214.200.58',
    port: '1080',
    code: 'BD',
    version: 'Socks4' },
  { ip: '45.55.202.229',
    port: '10080',
    code: 'US',
    version: 'Socks5' },
  { ip: '150.129.207.75',
    port: '6667',
    code: 'IN',
    version: 'Socks5' },
  { ip: '72.250.134.235',
    port: '6001',
    code: 'US',
    version: 'Socks4' },
    ......
這樣我們就獲取到代理服務(wù)器的最關(guān)鍵部分了。當(dāng)然啦懒叛,這個(gè)網(wǎng)站上的代理對(duì)于國(guó)內(nèi)的IP支持并不好丸冕,大家可以找國(guó)內(nèi)的代理服務(wù)器網(wǎng)站來(lái)抓取,仿照來(lái)寫(xiě)一個(gè)薛窥。

設(shè)定重試次數(shù)并使用休眠

????????不知道大家發(fā)現(xiàn)沒(méi)有胖烛,在 proxyserver.js 中眼姐,我們獲取瀏覽器實(shí)例到打開(kāi)頁(yè)面,都使用了一個(gè)循環(huán)來(lái)處理佩番。循環(huán)里面才是執(zhí)行的邏輯众旗。這個(gè)循環(huán)就是重試機(jī)制,他設(shè)定了最大重試次數(shù) MAX_RT 趟畏。即失敗會(huì)再次請(qǐng)求當(dāng)前操作贡歧。

????????有這個(gè)必要嗎浓冒?----還真的有3膊簟!我試過(guò)很多次嘗試發(fā)現(xiàn)逞壁,很多小說(shuō)網(wǎng)站就是矯情猎莲。第一次死活打不開(kāi)绍弟。當(dāng)然啦,循環(huán)也是為了更加穩(wěn)健而已著洼,一次就獲取成功也會(huì)馬上跳出循環(huán)的樟遣。

休眠代碼如下

function sleep(time = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, time);
    })
}

使用方法是(注意只能在異步函數(shù)中使用)

(async ()=> {
    //休眠3秒
    await sleep(3000);
})()

????????休眠的作用就不說(shuō)了,反正盡量裝的像個(gè)人唄9年碘!

啟動(dòng)多個(gè)瀏覽器實(shí)例來(lái)加快獲取內(nèi)容

????????人多力量大!1個(gè)人的力量總是有限的展鸡。同理1個(gè)瀏覽器抓取大量的內(nèi)容速度也是有限的屿衅,而且風(fēng)險(xiǎn)也大。這里我同時(shí)啟動(dòng)了20個(gè)帶代理服務(wù)器的瀏覽器實(shí)例莹弊,相當(dāng)于20個(gè)IP不同的主機(jī)在同時(shí)抓取我們所需要的內(nèi)容涤久。前面我們獲取到了代理服務(wù)器列表,這里可以這樣用:

/**
*   獲取代理瀏覽器
*/
function getProxyBrowser(proxyList) {

    return new Promise(async (resolve, reject) => {

        var browserList = [];

        for (var i = 0; i < proxyList.length; i++) {

            var proxyserver = proxyList[i];
            var proxyOption = proxyserver.version.toLowerCase() + '://' + proxyserver.ip + ':' + proxyserver.port;

            var browser = await puppeteer.launch({
                headless:true,
                args: [
                //'--window-size="800,600"',
                //'--start-fullscreen'
                '--proxy-server='+proxyOption
                ]
                }).catch();
            if (browser) {
                browserList.push(browser);
            }
        }

        if (browserList.length == 0) {
            reject()
            return;
        }

        resolve(browserList);
    })
}

????????調(diào)用這個(gè)方法可以獲取到proxyList大小的代理瀏覽器實(shí)例的列表忍弛。這樣我們就相當(dāng)于有20個(gè)人同時(shí)幫我們干活了响迂。之前我們獲取到了小說(shuō)的所有章節(jié)的url,這里就交給這20個(gè)代理瀏覽器去獲取吧细疚!

????????幸運(yùn)的是nodejs是單進(jìn)程的蔗彤。這樣我們也不用考慮同步的問(wèn)題。這樣步驟就簡(jiǎn)單了疯兼。

單個(gè)瀏覽器的任務(wù)是:
流程圖

????????似乎到這里就已經(jīng)結(jié)束了然遏?不是的。這里還是有一些問(wèn)題吧彪。

內(nèi)容優(yōu)化

????????很多小說(shuō)網(wǎng)站的正文內(nèi)容中都有一些令人很不爽的廣告啊待侵、推薦啊之類的東西,更有甚者正文中間都有奇怪的東西姨裸。這能忍嗎秧倾?肯定不能霸乖汀!這里我們對(duì)獲取到的內(nèi)容進(jìn)行一定的處理:舉個(gè)栗子

/**
*   處理獲取到的內(nèi)容
*   @unHandleContent    未處理的內(nèi)容(可能會(huì)包含某些奇怪的東西那先,統(tǒng)統(tǒng)去掉农猬,這里寫(xiě)去除邏輯∥搁牛可以先調(diào)試一條數(shù)據(jù)試下效果)
*/
function handleContent(unHandleContent) {

    var result = unHandleContent;

    // result = result.replace(/&nbsp;/g,' ');
    // result = result.replace(/\n|\r/g, '<br>');
    // result = result.replace(/<[a-z]{1,6}\s.*\/[a-z]{1,6}>/g, '');
    // result = result.replace(/<br>/g, '\n');

    return result;
}

????????ok盛险,到這里就結(jié)束了。有什么問(wèn)題的可以獲取源碼來(lái)看看勋又。

源碼 github地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末苦掘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子楔壤,更是在濱河造成了極大的恐慌鹤啡,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹲嚣,死亡現(xiàn)場(chǎng)離奇詭異递瑰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)隙畜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)抖部,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人议惰,你說(shuō)我怎么就攤上這事慎颗。” “怎么了言询?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵俯萎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我运杭,道長(zhǎng)夫啊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任辆憔,我火速辦了婚禮撇眯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘虱咧。我一直安慰自己熊榛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布彤钟。 她就那樣靜靜地躺著来候,像睡著了一般跷叉。 火紅的嫁衣襯著肌膚如雪逸雹。 梳的紋絲不亂的頭發(fā)上营搅,一...
    開(kāi)封第一講書(shū)人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音梆砸,去河邊找鬼转质。 笑死,一個(gè)胖子當(dāng)著我的面吹牛帖世,可吹牛的內(nèi)容都是我干的休蟹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼日矫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赂弓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起哪轿,我...
    開(kāi)封第一講書(shū)人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盈魁,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后窃诉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體杨耙,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年飘痛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了珊膜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宣脉,死狀恐怖车柠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脖旱,我是刑警寧澤堪遂,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站萌庆,受9級(jí)特大地震影響溶褪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜践险,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一猿妈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巍虫,春花似錦彭则、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瓦胎,卻和暖如春芬萍,著一層夾襖步出監(jiān)牢的瞬間尤揣,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工柬祠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留北戏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓漫蛔,卻偏偏與公主長(zhǎng)得像嗜愈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子莽龟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容