Puppeteer之爬蟲入門

譯者按: 本文通過簡單的例子介紹如何使用Puppeteer來爬取網(wǎng)頁數(shù)據(jù)创南,特別是用谷歌開發(fā)者工具獲取元素選擇器值得學(xué)習(xí)。

為了保證可讀性崇众,本文采用意譯而非直譯掂僵。另外,本文版權(quán)歸原作者所有顷歌,翻譯僅用于學(xué)習(xí)锰蓬。

我們將會學(xué)到什么?

在這篇文章眯漩,你講會學(xué)到如何使用JavaScript自動化抓取網(wǎng)頁里面感興趣的內(nèi)容芹扭。我們將會使用Puppeteer,Puppeteer是一個Node庫,提供接口來控制headless Chrome冯勉。Headless Chrome是一種不使用Chrome來運(yùn)行Chrome瀏覽器的方式。

如果你不知道Puppeteer摹芙,也不了解headless Chrome灼狰,那么你只要知道我們將要編寫JavaScript代碼來自動化控制Chrome就行。

準(zhǔn)備工作

你需要安裝版本8以上的Node浮禾,你可以在這里找到安裝方法交胚。確保選擇Current版本,因?yàn)樗?+盈电。

當(dāng)你將Node安裝好以后蝴簇,創(chuàng)建一個新的文件夾,將Puppeteer安裝在該文件夾下匆帚。

npm install --save puppeteer

例1:截屏

當(dāng)你把Puppeteer安裝好了以后熬词,我們來嘗試第一個簡單的例子。這個例子來自于Puppeteer文檔(稍微改動)吸重。我們編寫的代碼將會把你要訪問的網(wǎng)頁截屏并保存為png文件互拾。

首先,創(chuàng)建一個test.js文件嚎幸,并編寫如下代碼颜矿。

const puppeteer = require('puppeteer');

async function getPic() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://google.com');
  await page.screenshot({path: 'google.png'});

  await browser.close();
}

getPic();

我們來一行一行理解一下代碼的含義。

  • 第1行:引入我們需要的庫Puppeteer嫉晶;
  • 第3-10行:主函數(shù)getPic()包含了所有的自動化代碼骑疆;
  • 第12行:調(diào)用getPic()函數(shù)。

這里需要提醒注意getPic()函數(shù)是一個async函數(shù)替废,使用了ES 2017 async/await特性箍铭。該函數(shù)是一個異步函數(shù),會返回一個Promise椎镣。如果async最終順利返回值坡疼,Promise則可以順利reslove,得到結(jié)果衣陶;否則將會reject一個錯誤柄瑰。

因?yàn)槲覀兪褂昧?code>async函數(shù),我們使用await來暫停函數(shù)的執(zhí)行剪况,直到Promise返回教沾。

接下來我們深入理解一下getPic()

  • 第4行:

      const broswer = await puppeteer.launch();
    

    這行代碼啟動puppeteer,我們實(shí)際上啟動了一個Chrome實(shí)例译断,并且和我們聲明的browser變量綁定起來授翻。因?yàn)槲覀兪褂昧?code>await關(guān)鍵字,該函數(shù)會暫停直到Promise完全被解析。也就是說成功創(chuàng)建Chrome實(shí)例或則報錯堪唐。

  • 第5行:

      const page = await browser.newPage();
    

    我們在瀏覽器中創(chuàng)建一個新的頁面巡语,通過使用await關(guān)鍵字來等待頁面成功創(chuàng)建。

  • 第6行:

      await page.goto('https://google.com');
    

    使用page.goto()打開谷歌首頁淮菠。

  • 第7行:

       await page.screenshot({path: 'google.png'});
    

    調(diào)用screenshot()函數(shù)將當(dāng)前頁面截屏男公。

  • 第9行:

       await browser.close();
    

    將瀏覽器關(guān)閉。

執(zhí)行實(shí)例

使用Node執(zhí)行:

  node test.js

下面截取的圖片google.png

現(xiàn)在我們來使用non-headless模式試試合陵。將第4行代碼改為:

  const browser = await puppeteer.launch({headless: false});

然后運(yùn)行試試枢赔。你會發(fā)現(xiàn)谷歌瀏覽器打開了,并且導(dǎo)航到了谷歌搜索頁面拥知。但是截屏沒有居中踏拜,我們可以調(diào)節(jié)一下頁面的大小配置。

await page.setViewport({width: 1000, height: 500});

截屏的效果會更加漂亮低剔。

下面是最終版本的代碼:

const puppeteer = require('puppeteer');

async function getPic() {
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();
  await page.goto('https://google.com');
  await page.setViewport({width: 1000, height: 500})
  await page.screenshot({path: 'google.png'});

  await browser.close();
}

getPic();

例2:爬取數(shù)據(jù)

首先速梗,了解一下Puppeteer的API。文檔提供了非常豐富的方法不僅支持在網(wǎng)頁上點(diǎn)擊襟齿,而且可以填寫表單镀琉,讀取數(shù)據(jù)。

接下來我們會爬取Books to Scrape蕊唐,這是一個偽造的網(wǎng)上書店專門用來練習(xí)爬取數(shù)據(jù)屋摔。

在當(dāng)前目錄下,我們創(chuàng)建一個scrape.js文件替梨,輸入如下代碼:

const puppeteer = require('puppeteer');

let scrape = async () => {
  // 爬取數(shù)據(jù)的代碼
  
  // 返回數(shù)據(jù)
};

scrape().then((value) => {
    console.log(value); // 成功钓试!
});

第一步:基本配置

我們首先創(chuàng)建一個瀏覽器實(shí)例,打開一個新頁面副瀑,并且導(dǎo)航到要爬取數(shù)據(jù)的頁面弓熏。

let scrape = async () => {
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();
  await page.goto('http://books.toscrape.com/');
  await page.waitFor(1000);
  // Scrape
  browser.close();
  return result;
};

注意其中有一行代碼讓瀏覽器延時關(guān)閉。這行代碼本來是不需要的糠睡,主要是方便查看頁面是否完全加載挽鞠。

await page.waitFor(1000);

第二步:抓取數(shù)據(jù)

我們接下來要選擇頁面上的第一本書,然后獲取它的標(biāo)題和價格狈孔。

查看Puppeteer API信认,可以找到定義點(diǎn)擊的函數(shù):

page.click(selector[, options])

  • selector <string> 一個選擇器來指定要點(diǎn)擊的元素。如果多個元素滿足均抽,那么默認(rèn)選擇第一個嫁赏。

幸運(yùn)的是,谷歌開發(fā)者工具提供一個可以快速找到選擇器元素的方法油挥。在圖片上方右擊潦蝇,選擇檢查(Inspect)選項款熬。

<div style="text-align: center;">
<img style="width:75%;" src="guide-to-automating-scraping-the-web-with-js/first_book_inspect.png" />
</div>

谷歌開發(fā)者工具的Elements界面會打開,并且選定部分對應(yīng)的代碼會高亮攘乒。右擊左側(cè)的三個點(diǎn)贤牛,選擇拷貝(Copy),然后選擇拷貝選擇器(Copy selector)则酝。

接下來將拷貝的選擇器插入到函數(shù)中殉簸。

await page.click('#default > div > div > div > div > section > div:nth-child(2) > ol > li:nth-child(1) > article > div.image_container > a > img');

加入了點(diǎn)擊事件的代碼執(zhí)行后會直接跳轉(zhuǎn)到詳細(xì)介紹這本書的頁面。而我們則關(guān)心它的標(biāo)題和價格部分堤魁。

為了獲取它們喂链,我們首選需要使用page.evaluate()函數(shù)返十。該函數(shù)可以讓我們使用內(nèi)置的DOM選擇器妥泉,比如querySelector()

const result = await page.evaluate(() => {
// return something
});

然后洞坑,我們使用類似的手段獲取標(biāo)題的選擇器盲链。

使用如下代碼可以獲取該元素:

let title = document.querySelector('h1');

但是,我們真正想要的是里面的文本文字迟杂。因此刽沾,通過.innerText來獲取。

let title = document.querySelector('h1').innerText;

價格也可以用相同的方法獲取排拷。


let price = document.querySelector('.price_color').innerText;

最終侧漓,將它們一起返回,完整代碼如下:

const result = await page.evaluate(() => {
  let title = document.querySelector('h1').innerText;
  let price = document.querySelector('.price_color').innerText;
return {
  title,
  price
}
});

所有的代碼整合到一起监氢,如下:

const puppeteer = require('puppeteer');

let scrape = async () => {
    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();

    await page.goto('http://books.toscrape.com/');
    await page.click('#default > div > div > div > div > section > div:nth-child(2) > ol > li:nth-child(1) > article > div.image_container > a > img');
    await page.waitFor(1000);

    const result = await page.evaluate(() => {
        let title = document.querySelector('h1').innerText;
        let price = document.querySelector('.price_color').innerText;

        return {
            title,
            price
        }

    });

    browser.close();
    return result;
};

scrape().then((value) => {
    console.log(value); // Success!
});

運(yùn)行node scrape.js即可返回數(shù)據(jù)

{ title: 'A Light in the Attic', price: '£51.77' }

例3:進(jìn)一步優(yōu)化

從主頁獲取所有書籍的標(biāo)題和價格布蔗,然后將它們返回。

提示

和例2的區(qū)別在于我們需要用一個循環(huán)來獲取所有書籍的信息浪腐。

const result = await page.evaluate(() => {
  let data = []; // Create an empty array
  let elements = document.querySelectorAll('xxx'); // 獲取所有書籍元素 
  // 循環(huán)處理每一個元素
    // 獲取標(biāo)題
    // 獲取價格
    data.push({title, price}); // 將結(jié)果存入數(shù)組
  return data; // 返回數(shù)據(jù)
});

解法

const puppeteer = require('puppeteer');

let scrape = async () => {
    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();

    await page.goto('http://books.toscrape.com/');

    const result = await page.evaluate(() => {
        let data = []; // 初始化空數(shù)組來存儲數(shù)據(jù)
        let elements = document.querySelectorAll('.product_pod'); // 獲取所有書籍元素

        for (var element of elements){ // 循環(huán)
            let title = element.childNodes[5].innerText; // 獲取標(biāo)題
            let price = element.childNodes[7].children[0].innerText; // 獲取價格

            data.push({title, price}); // 存入數(shù)組
        }

        return data; // 返回數(shù)據(jù)
    });

    browser.close();
    return result;
};

scrape().then((value) => {
    console.log(value); // Success!
});

關(guān)于Fundebug

Fundebug專注于JavaScript纵揍、微信小程序、微信小游戲议街、支付寶小程序泽谨、React Native、Node.js和Java實(shí)時BUG監(jiān)控特漩。 自從2016年雙十一正式上線吧雹,F(xiàn)undebug累計處理了7億+錯誤事件,得到了Google涂身、360吮炕、金山軟件、百姓網(wǎng)等眾多知名用戶的認(rèn)可访得。歡迎免費(fèi)試用龙亲!

版權(quán)聲明

轉(zhuǎn)載時請注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2017/11/01/guide-to-automating-scraping-the-web-with-js/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陕凹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鳄炉,更是在濱河造成了極大的恐慌杜耙,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拂盯,死亡現(xiàn)場離奇詭異佑女,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)谈竿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門团驱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人空凸,你說我怎么就攤上這事嚎花。” “怎么了呀洲?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵紊选,是天一觀的道長。 經(jīng)常有香客問我道逗,道長兵罢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任滓窍,我火速辦了婚禮卖词,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吏夯。我一直安慰自己此蜈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布锦亦。 她就那樣靜靜地躺著舶替,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杠园。 梳的紋絲不亂的頭發(fā)上顾瞪,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機(jī)與錄音抛蚁,去河邊找鬼陈醒。 笑死,一個胖子當(dāng)著我的面吹牛瞧甩,可吹牛的內(nèi)容都是我干的钉跷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼肚逸,長吁一口氣:“原來是場噩夢啊……” “哼爷辙!你這毒婦竟也來了彬坏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤膝晾,失蹤者是張志新(化名)和其女友劉穎栓始,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體血当,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幻赚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了臊旭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片落恼。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖离熏,靈堂內(nèi)的尸體忽然破棺而出佳谦,到底是詐尸還是另有隱情,我是刑警寧澤撤奸,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布吠昭,位于F島的核電站喊括,受9級特大地震影響胧瓜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜郑什,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一府喳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蘑拯,春花似錦钝满、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至剃法,卻和暖如春碎捺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贷洲。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工收厨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人优构。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓诵叁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钦椭。 傳聞我的和親對象是個殘疾皇子拧额,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355