背景#
從來沒有在工作中寫過爬蟲,前不久領導給了個活從 birdeye 上爬兩個代幣的歷史價格信息。作為前端,最直接的感受就是請求都是從網頁過去的,那麼我應該可以在這個網站的控制台,去編寫請求的代碼,這樣拿到數據後再去請求我本地的接口,把數據攜帶過去就可以了。
獲取原始請求信息#
谷歌瀏覽器控制台提供了很便捷的功能,可以快速複製一個請求下來。
之所以在瀏覽器控制台寫腳本,是因為在本地寫腳本去請求,需要考慮很多額外的事情。
比如分析這個請求需要攜帶什麼請求頭、請求方法等等,試錯成本很高。既然如此直接複製一個fetch
請求,在瀏覽器控制台裡直接運行,大概率都是行得通的,該有的都會帶上,可以聚焦於數據處理上。
嘗試運行,可行
編寫腳本#
// 想要爬取的代幣 token
const token = "xxx";
// 開始時間
const startDate = new Date("2023-01-01");
// 結束時間
const endDate = new Date("2023-01-02");
// 間隔30分鐘
const type = "30m";
(async () => {
const getToken = (start, end) => {
return new Promise((resolve) => {
fetch(
`https://api.birdeye.so/amm/ohlcv?address=${token}¤cy=usd&type=${type}&time_from=${start}&time_to=${end}`,
{
headers: {
accept: "application/json, text/plain, */*",
"accept-language": "zh-CN",
"agent-id": "93bd6afd-cbf4-4acc-a014-b62d88374ea6",
"cf-be":
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2Nzk0Njk3ODIsImV4cCI6MTY3OTQ3MDA4Mn0.eWkKWRUsPKMZCqNJB5KL_Z_Eurn4wCMN5dyzCR5oJ_U",
"sec-ch-ua": '"Not;A=Brand";v="99", "Chromium";v="106"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"x-cypress-is-xhr-or-fetch": "true",
Referer: "https://birdeye.so/",
"Referrer-Policy": "strict-origin-when-cross-origin",
},
body: null,
method: "GET",
}
)
.then((res) => res.json())
.then((res) => {
try {
resolve(res.data.items);
} catch (error) {
console.error(error);
}
});
});
};
for (
let currentDate = startDate;
currentDate <= endDate;
currentDate.setDate(currentDate.getDate() + 1)
) {
const startTimestamp = Math.floor(currentDate.getTime() / 1000);
const endTimestamp = startTimestamp + 86399;
try {
console.log("開始讀取數據...", [startTimestamp, endTimestamp]);
// 緩解下人家的伺服器壓力
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
const data = await getToken(startTimestamp, endTimestamp);
console.log(
`${new Date(startTimestamp * 1000).toLocaleString()} - ${new Date(
endTimestamp * 1000
).toLocaleString()} 數據讀取完成`
);
// 把數據發送給自己的本地接口服務
fetch("http://localhost:3000", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
} catch (error) {
console.log(error, "數據讀取失敗");
// 這裡很關鍵,打印這個日誌,是為了考慮接口被限制請求次數的情況。失敗後,根據這裡打印的信息,把最上面的 startDate 調整一下,過一會兒好繼續爬取數據
console.log(startTimestamp, endTimestamp);
break;
}
}
})();
本地接口服務#
import express from "express";
import bodyParser from "body-parser";
import cors from "cors";
const origin = "https://birdeye.so";
const filePath = "./data.json";
const app = express();
app.use(bodyParser.json());
app.use(cors({ origin }));
app.post("/", async (req, res) => {
// 自行處理數據格式
console.log(req.body);
res.send("ok");
});
app.listen(3000);
總結#
一開始本來想套一個 Cypress
來塞腳本進去實現的,這樣對工程化很友好。但是這個功能畢竟是臨時性的,後面也沒有多的需求了。就簡單實現好了;還好谷歌的控制台提供了很方便的東西,不然一個一個看請求頭、看 cookie 真挺麻煩的。