了不起的 Deno:帶你極速獲取各大平台今日熱榜

摘要:Deno 是一個 JavaScript/TypeScript 的運行時,默認使用安全環境執行程式碼,有著卓越的開發體驗。

有人的地方就有江湖,有江湖的地方就有爭論。前些天,繼《[譯]為什麼如今 Deno 正全面取代 Node.js》之後,又有了《【譯】Deno 已經死了嗎?》,也許這便是江湖。說回標題」Deno 在手,天下我有「,其實 Deno 換成Charj、NodeJS、Java 等其他名詞都一樣,畢竟只是標題罷了,可能真正不一樣的是使用她的愉悅感。對我而言,能給我帶來 Copy 的快感,便是極好的!

源起

本周,被@justjavac 巨佬的各個熱搜榜項目吸睛了,抱著對技術的三分鐘熱情以及對優秀程式碼的學習心切,我又開始了新的程式碼拷貝旅程。二話不說,先git clone一頓操作猛如虎,定睛一看,好像這幾個項目的實現都差不多,也請教了巨佬本尊確定實現是一樣的,但為毛要開 4 個 repo 呢?也許這就是單一職責原則吧。然後,我尋思著能不能整合一下再加點別的熱搜榜就能變成trending in one,是不是很棒的想法,哈哈哈哈。接下來,我還會對今日頭條下手–」得熱搜者得天下,熱搜榜拿來吧!「,三下除二拿到了頭條熱搜榜(儘管需要輸入圖片驗證碼)。最後就是資源整合,不過發現 README.md 爆了,看來單例還是有單例的好。

即刻獲取今日熱搜榜 ➡️ trending-in-one

剖析

關於如何實現熱搜匯總,如果不看源碼,我也只能想到調用相關熱搜榜的介面來獲取,可別人的介面又怎麼會給你隨意調用呢?通過拜讀大佬的源碼,我看到了通過正則匹配 DOM 節點獲取對應的標題鏈接之類的,我看到了通過注釋定位包裹的內容並進行替換,我看到了 JavaScript 如何處理重複的數據,我看到了如何藉助 github action 實現 Deno 應用的構建……儘管只是一個微不足道的項目,卻包羅萬象,作者將各種技巧搭配自如、靈活運用,簡直是出神入化、登峰造極啊(PS:在我看來事實如此)。

其實拋開語言和平台,要想實現熱搜匯總榜,無非就是三步走:① 獲取數據 ② 處理數據 ③ 輸出數據。接下來從 Deno 的視角來詳細解說這三步是如何走的:

獲取數據

當我們遇到一個需求,可能要從它的本質出發,比如要實現熱搜匯總,首先我們就需要各大平台的熱搜數據,如今日頭條熱搜榜、知乎熱門影片、知乎熱門話題、知識熱門搜索、微博熱門搜索等等,怎麼獲取呢?常規的手段就是框按 F12 看看 Network,實在不行試試抓包工具.好在「前人栽樹後人乘涼」,於是乎我們便有了各平台的介面,獲取數據豈不是信手沾來。

頭條熱榜://is-lq.snssdk.com/api/suggest_words/?business_id=10016

微博熱搜://s.weibo.com/top/summary

知乎熱門話題://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=100

知乎熱門影片://www.zhihu.com/api/v3/feed/topstory/hot-lists/zvideo?limit=100

知乎熱搜: //www.zhihu.com/api/v4/search/top_search

當然以上介面並非永久有效,目前來看也只能是能用多久用多久了,如果您不滿足於此,亦可以試試Twitter、Medium 等等(PS:好人一生平安)。不過,上邊的介面中,微博熱搜特立獨行,她返回的是 HTML 需要用到正則匹配即可拿到熱搜標題和鏈接,正則可把我難住了:/<a href=”(\/weibo\?q=[^”]+)”.*?>(.+)<\/a>/g,似懂非懂。各位少俠可否移步評論區多多指教?

以今日頭條為例,獲取數據似乎顯示特別簡單,但捫心自問 await、fetch、TypeScript 我又了解多少呢?

const response = await fetch(
  "//is-lq.snssdk.com/api/suggest_words/?business_id=10016",
);

if (!response.ok) {
  console.error(response.statusText);
  Deno.exit(-1);
}

const result: ToutiaoTopSearch = await response.json();
const words = result.data[0].words;

處理數據

最終我們的數據可能是這樣的–參見2020-12-01 頭條熱搜

細心的小夥伴也許看到了.md 文件中的注釋,就是這注釋起到了關鍵的作用,移花接木?為他人做嫁衣?在進行數據更新的時候,我們可以巧妙地利用注釋作為參照,動態替換注釋開頭和注釋結尾中間的內容,利用 Deno 標準庫及一些內置 API 完成文件操作,比如今日頭條熱搜數據的創建及更新:

export function createTuotiaoList(words: ToutiaoWord[]): string {
  return `<!-- BEGIN TOUTIAO -->
<!-- 最後更新時間 ${Date()} -->
${
    words.map((x) => `1. [${x.word}](${x.url})`)
      .join("\n")
  }
<!-- END TOUTIAO -->`;
}

export async function createReadme4Toutiao(
  words: ToutiaoWord[],
): Promise<string> {
  const readme = await Deno.readTextFile("./README.md");
  return readme.replace(
    /<!-- BEGIN TOUTIAO -->[\W\w]*<!-- END TOUTIAO -->/,
    createTuotiaoList(words),
  );
}

另外再對比新舊數據的時候,用到了常用的數組迭代方法及**Object.entries()** ,感覺 MDN 還得多刷,完全不熟練,尤其是串起來的時候:

/** 合併兩次熱搜並去重 */
export function mergeWords4Toutiao(
  words: ToutiaoWord[],
  another: ToutiaoWord[],
): ToutiaoWord[] {
  const obj: Record<string, string> = {};
  for (const w of words.concat(another)) {
    obj[w.url] = w.word;
  }
  return Object.entries(obj).map(([url, word]) => ({
    url,
    word,
  }));
}

輸出數據

其實在處理數據的時候,我們基本完成了數據輸出的準備工作,鑒於本項目中只輸出為.md 文件和.json 文件,直接調用之前的處理數據方法即可,簡單粗暴:

export async function toutiaoSearch() {
  // 保存原始數據
  await Deno.writeTextFile(fullPath, JSON.stringify(wordsAll));

  // 更新 README.md
  const readme = await createReadme4Toutiao(wordsAll);
  await Deno.writeTextFile("./README.md", readme);

  // 更新 archives
  const archiveText = createArchive4Toutiao(wordsAll, yyyyMMdd);
  const archivePath = join("archives/toutiao-search", `${yyyyMMdd}.md`);
  await Deno.writeTextFile(archivePath, archiveText);
}

如果不局限於輸出數據為文件,我們可以實現的功能就可以多了,發送熱搜數據(或者任何您能獲取到的數據)到郵件、簡訊、釘釘機器人……不禁又想起了老濕常說的「帶薪拉屎」必備,想想每天早上第一泡就能享用熱乎的熱搜榜,似乎有種「家事國事天下事,事事關心」的錯覺,坐著點開一條條熱搜表示「朕已閱」……

Github Action

如果說 Serverless 是個好東西,那麼Github Action怎麼逃得過真香定律呢?在 GitHub Actions 的倉庫中自動化、自定義和執行軟體開發工作流程。您可以發現、創建和共享操作以執行您喜歡的任何作業(包括 CI/CD),並將操作合併到完全自定義的工作流程中。通俗點就是您可以為所欲為,比如定時構建 Deno 應用。為了能夠每小時更新我們的熱搜匯總,我們需要給 github 倉庫新增**.github/workflows** 文件夾,也就是前邊所說的工作流 ,然後新建 ci.yml 和 schedule.yml 告訴 Github Action 應該如何做,這樣她就是聽話的貼心小秘書了,因為她會告訴您每一次執行工作流的詳細結果,就算構建失敗也有郵件溫馨提醒。至於 yml 的內容應該寫啥,請參照官方文檔和 Deno 社區經典案例(PS:好像是我的知識盲區,用得還不熟練,怕帶坑)。 當然,如果想直接開乾的話,可以在倉庫的 Action 選項卡中獲取到 github 提供的溫馨建議。少年,亮劍吧!

尾聲

探索的腳步永不止步,不過於我,好像置身於小黑屋,無論我怎麼走都無法走出去,十多年了,依舊原地踏步踏。小時候,夢想長大以後一家子各兄弟能大幹一場實現家族企業的幻想卻奈何爺輩早逝凝聚力盡失;到中學,懷恨於內心的脆弱被班主任拿捏死失去重點中學的入場券落為雞頭放棄自我;萬幸有的大學讀,卻虛度光陰於網吧、操場、情人坡、後山,終費萬事……到此刻及未來,終究要為過去的沉淪頹廢而埋單。唉,年紀大了,還能怎麼折騰?謹以此段,奉勸家中無礦、朝中無權的年輕人,莫虛度此生,至少 Deno 真香!

完整項目地址: //github.com/hu-qi/trending-in-one

 

點擊關注,第一時間了解華為雲新鮮技術~