用 JavaScript 輕鬆撰寫 PTT 的各大看板的網頁爬蟲,透過設定正確的 cookie , 就連 18 禁看板都無所阻礙!
透過Puppeteer,我們可以藉由這個無介面瀏覽器(Headless Chrome)操作DOM、模擬使用者交互…,更重要的的原因是 — 能夠使用最純粹、原生的 Web API ,簡而言之就像是按下 F12 ,直接進入 console …另外也能將其撰寫為可 import 的獨立檔案,並注入頁面環境。
專案連結: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 來爬取動態資料…
在我接觸前端之後
就我個人的感覺,前端的 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事件
作請求攔截。
為了要能進入成人看板
比如像西斯版 (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 。
注意:這顯然是前端 (client-side) 的 JavaScript
後續就是依樣畫葫蘆了,在依文章內文做舉例,這裡用了兩個小技巧,因為網頁版的 PTT 內文,並不是用單一標籤給包覆,而是 Text 與各種 tag 交替出現,所以我們搜集所有 nodeType 為 3 的資料。
如果多圖看板,如美妝版 (makeup) 、表特版 (beauty) ,或是多外部連結看板,如八卦版的新聞,我們也將它搜集起來,包括外部連結的 queue ,以及外部連結以jpg/png/gif結尾的圖片資源,有另外的 queue 。
(這裡使用正則表達式,不得不說正則在解析的過程中,讓人省了不少功)
結論
爬蟲這種東西,有點多說無益,儘管貼再多的 code ,如果不親自爬取一遍,觀看者還是不知道筆者在說什麼…
只是本文透過另一種方式去解釋 解析 這件事,其實 HTML解析 不過就像是我們平常寫前端程式時,對 DOM 去做操作,拿出 input value 或是 h3 的 innerText … 啊你把它全部 log 下來,並分類一波,不就是爬蟲了呢?
ptt-parser
如果你只是需要一堆文字資料做 training 的玩家,直接用我的 code 來跑吧,操作方法、選擇爬取看板、頁數放在 repository 的文檔中。
#爬蟲 #crawler #PTT #PTT爬蟲 #JavaScript #Puppeteer #DOM #ES7