用 JavaScript 輕鬆撰寫 PTT 的各大看板的網頁爬蟲,透過設定正確的 cookie , 就連 18 禁看板都無所阻礙!

realdennis
7 min readOct 4, 2018

--

透過Puppeteer,我們可以藉由這個無介面瀏覽器(Headless Chrome)操作DOM、模擬使用者交互…,更重要的的原因是 — 能夠使用最純粹、原生的 Web API ,簡而言之就像是按下 F12 ,直接進入 console …另外也能將其撰寫為可 import 的獨立檔案,並注入頁面環境。

“woman using black and gray laptop computer” by rawpixel on Unsplash

專案連結:ptt-parser

寫在前頭

這篇文章面向於前端人員,卻未曾接觸頁面爬蟲,並能簡單地將熟悉地操作變成有力的解析工具。

實際上,像是 PTT 的網頁版這種所有資料一同載入的網站,不需要用到 Web driver (比如上述的 Puppeteer / Selenium ),只需要發請求把 html 拿回來,並解析重要資料即可。

這樣子的效能更好,不過如果你想 write once run anywhere 或是後續要爬取 ajax 動態加載動態內容時,無痛升級,你可以考慮繼續閱讀這篇文章。

古早的時候

我撰寫的爬蟲是用 Python + requests 發起請求,再使用特定的 HTML parser ,比如美湯( Beautiful soup ),你甚至把此篇文章標題 “ PTT 爬蟲 ” 拿去餵狗,找到的前幾個無不是用Python+以上或類似的函式庫撰寫。

Node.js 裡頭亦有 request (請求) + cheerio (類 jQuery 語法)

甚至撰寫過 Python + Selenium 來爬取動態資料…

在我接觸前端之

“programming code” by Irvan Smith on Unsplash

就我個人的感覺,前端的 selector 比起 XPath 或是 find … 都好用,甚至 cheerio 都用得有些彆扭,並且瀏覽器的 HTML model 能力也不遑多讓,當然這種事是非常主觀的。

不過,能在 server-side 的環境用 JavaScript ( Node.js )操作 Browser 並引入 JavaScript ( Client-side )的大概只有 JavaScript 能夠做到了…可謂學一招打天下。

唯一 Dependency

puppeteer — 一切都靠它,你可以想像它就是一個 Chrome ,實際上它也是 Google 所推出的。

發請求出去 ES7的JavaScript寫法

const browser = await puppeteer.launch({ headless: true });//如果為false則會開啟瀏覽器,適合用作於debug時。const page = await browser.newPage();await page.goto(url);

根本連發請求都無需操勞,以上 3 個操作,翻譯成中文的話,根本是你每次打開瀏覽器的過程。

攔截非必要請求

雖然明知道效能偏低 (畢竟開了一個瀏覽器) ,但我們還是盡可能的優化一下,比如不浪費流量去載入不必要的圖片、廣告…這時可以透過 page 上的 request事件 作請求攔截。

“man looking through the window blind inside room” by Joshua Oluwagbemiga on Unsplash
註冊request事件,斬斷鎖鏈、斷開連結,比如圖片、樣式表、字體…

為了要能進入成人看板

“person in clear glass shower stall” by NOTAVANDAL on Unsplash

比如像西斯版 (sex) 或是八卦版 (gossiping) 我們必須把 cookie over18 設定為1,這個設定在Puppeteer如下:

page.setCookie({name: 'over18',value: '1'});

接下來把JavaScript腳本傳入瀏覽器

我們有兩個 lib 需要在 browser 上跑起來,分別是 articleParser.js & pageParser.js

articleParser.js — 針對文章內容作解析,包括標題、作者、內容、內容中的連結、內容中的圖片、時間、看板、三種推文數量、各推文內容、推文時間、推文ID、推文IP

pageParser.js — 針對該看板的特定頁數,取得此頁中所有文章之有效連結、上下頁是否存在、上下頁之頁數、過濾公告文章…

將其引用進程式空間,並透過 page.evaluate 傳入,大概長這樣

const aParser = require(‘./articleParser’);const result = await page.evaluate(aParser); 
//這裡會把browser會去執行aParser而非Node執行

result則為 console的執行結果

所以說那個醬汁勒?

扯了這麼多發現還是沒講怎麼解析,抱歉抱歉。

我們就用一篇 post 上方的資訊來舉例吧,且以下皆是透過 querySelector 輕鬆選出我們要的 tag

觀察一下必要資訊,沒想到竟然class都長一個樣。
此function只做一件事,就是拿到看板藍藍的地方,裡頭有文章作者/看版/標題

注意:這顯然是前端 (client-side) 的 JavaScript

後續就是依樣畫葫蘆了,在依文章內文做舉例,這裡用了兩個小技巧,因為網頁版的 PTT 內文,並不是用單一標籤給包覆,而是 Text 與各種 tag 交替出現,所以我們搜集所有 nodeType 為 3 的資料。

如果多圖看板,如美妝版 (makeup) 、表特版 (beauty) ,或是多外部連結看板,如八卦版的新聞,我們也將它搜集起來,包括外部連結的 queue ,以及外部連結以jpg/png/gif結尾的圖片資源,有另外的 queue

(這裡使用正則表達式,不得不說正則在解析的過程中,讓人省了不少功)

Line 10 我們針對a tag的內文去做正則分析

結論

爬蟲這種東西,有點多說無益,儘管貼再多的 code ,如果不親自爬取一遍,觀看者還是不知道筆者在說什麼…

只是本文透過另一種方式去解釋 解析 這件事,其實 HTML解析 不過就像是我們平常寫前端程式時,對 DOM 去做操作,拿出 input value 或是 h3 innerText … 啊你把它全部 log 下來,並分類一波,不就是爬蟲了呢?

ptt-parser

如果你只是需要一堆文字資料training 的玩家,直接用我的 code 來跑吧,操作方法、選擇爬取看板、頁數放在 repository 的文檔中。

https://github.com/realdennis/ptt-parser

#爬蟲 #crawler #PTT #PTT爬蟲 #JavaScript #Puppeteer #DOM #ES7

--

--

Responses (1)