[Node] 爬蟲學習筆記
目錄
[TOC]
常用套件
request套件已於 2020 年正式宣告棄用(deprecated),不再維護。建議改用axios、got或 Node.js 內建的fetch(Node 18+)。Nightmare.js已停止維護,建議改用Playwright或Puppeteer。
- Request @ GitHub(已棄用)
- Cheerio
- Async
- Nightmare.js(已停止維護)
- Puppeteer.js
npm install request cheerio async puppeteer
重要觀念
- 對於 http 來說,每次的 request 都是 stateless,也就是說,在這個 request 裡面肯定有某樣東西是驗證我們為已登入狀態。
- 有非預期的結果,或者 Postman 和 Browser 拿到的結果不同時,很有可能是 Cookie 的影響,這時候重新載入(Ctrl + R)可能沒效,可以透過清除網站資料的方式試試。

- postman 每次發出 request 後也會留有 cookie 紀錄,記得把它清除:

一些重要的 Response Headers 屬性
在 Response Headers 中:
Location:告訴瀏覽器要轉址,通常會在 304 Found 之後給予要轉址的路徑,瀏覽器會自動轉址。Set-Cookie:透過這個屬性可以在瀏覽器中設置 Cookie ,而 Cookie 預設會自動帶入下一次的 Request Headers 中。
取得 Response Headers 中的 Cookie
如過是使用 requestJS 的話,可以透過 res.headers['set-cookie'] 取得 Set-Cookie 的內容:
request('url', (err, res, body) => {
let Cookie = res.headers['set-cookie']
})
9-1 get-comics
keywords: 解碼
這篇比較有技術內容,因為它的圖片有加密,所以特別留筆記補充。
首先透過 inspector 點選圖片會發現 <div id="iBody"></div> 這段:

但是這段在透過 Postman 取得時並不存在,表示他是透過 JS 重新轉譯的:

可以看到在這個 <div> 附近包了兩個 function 分別是 window_onload() 和 dfImg()。
從 <head></head> 去找可以看到 分別載入了 view.js 和 ajaxClass.js,看了一下內容,可以發現這兩個 function 藏在 view.js 中:

接著可以在 Chrome Inspector 中的 Sources 中找到這支檔案:

其中在 window_onload() 這個 function 中可以找到 unsuan function ,這是用來解碼圖片網址的函式:
Watch Video @ Youtube
雖然可以透過 debugger 的方式進入這個函式,而且也可以直接在 console 中使用 unsuan 這個函式,但是卻找不到這個函式藏在哪裡。
原來它藏在 view.js 的最上面,經過一些特殊的編碼試圖混淆 我們,讓我們找不到:

把整段貼入 console 中就可以把它打回原形了:

而圖片的網址則藏在 <img> 的 name 屬性中:

10-1 ibon-upload(mobile)
keywords: 檔案上傳, file upload
這篇要注意的是:
- 要先透過
getASPNetToken()取得 ASP.NET 特定使用的 Token sendFile()之後的 response header 中有一個Set-Cookie屬性,裡面有ASP.NET_SessionId,必須把這個 Cookie 往後帶到getFileCode()的 header 中,才能正確取得檔案編碼:

10-2 ibon-upload(web)
這篇要注意的是
- 要先透過
getASPNetToken()取得 ASP.NET 特定使用的 Token,和手機版不同之處是,Cookie 中的ASP.NET_SessionId需樣打另一道網址才會取得:

- 另外電腦版要記得把 Cookie 清掉,才能看到 Set-Cookie 的 Header
12-2 get-fb-friends-birthday
- FB 傳會來的 response 會以
for (;;);'{ ... }開頭,先把它移除,後面會是 JSON 物件 - html 的 template 會藏在
JSON.parse(body.replace('for (;;);', '')).domops[0][3].__html裡面 - 其中裡面的文字透過 HTML encode 和 URI encode 過,因此需要先盡進行 HTML decode 接著進行
decodeURIComponent才可以看到「看的懂」的中文和 HTML。
經過 HTML decoder 後才可以看到中文:

接著透過 decodeURIComponent 可以轉換成可使用的 HTML:


最後透過 chrome console 中右下角的複製貼到編輯器中排版:

[Unit14] ithelp-oauth-login
由於每次 response 回來的 Headers 中大多帶有 set-cookie 指令,因此使用餅乾盒的概念({jar: true}),把每一次去得的 cookie 都帶到下一次使用會比較方便:
const request = require('request').defaults({ jar: true });
14-1 iThelp 登入
在 clickLoginFromIThelp() 的時候,從 Postman 帶回來的 header 可以直接看到 Location 並且沒有 <body>;但是透過 Node 的 requestJS 會帶有 <body> ,而原本的 Location 則會藏在 res.request._redirect.redirects 當中。
function clickLoginFromIThelp(callback) {
request('https://ithelp.ithome.com.tw/users/login', (err, res, body) => {
let redirectLocation = res.req._headers.referer;
console.log('LocationInResponse', LocationInResponse);
});
}

14-2 Get "Code" for callback and "LOGIN" Cookie
在 14-2 Get "Code" for callback and "LOGIN" Cookie 可以取得兩個重要的 Cookie ITHOME_LOGIN 和 laravel_session 需要帶到後面的 request 使用:

在 Body 中也可以取得所需要的 code

14-3 取得 OAuth set-cookie 中的 Token
將 14-2 的 Cookie 和 code 帶入後,可以在 Set-Cookie 中看到非常重要的 _token:

但要留意的是,一組 code 只能用一次 ,如果使用過取得 _token 之後,再次發送此 request,只會取得 404 Not Found,此時只能回到 14-2 重新取得一次新的 code:

14-4 成功登入
接著把 14-3 中的兩個重要 Cookie _token 和 ithelp2016_desktop 帶入這個 request 中,即可成功登入 ithelp。
參考
- 爬蟲始終來自於惰性 @ iThome
- 工欲善其事,必先利其器(下) @ iThome 爬蟲始終來自於墮性