了不起的 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,实在不行试试抓包工具.好在“前人栽树后人乘凉”,于是乎我们便有了各平台的接口,获取数据岂不是信手沾来。

头条热榜:https://is-lq.snssdk.com/api/suggest_words/?business_id=10016

微博热搜:https://s.weibo.com/top/summary

知乎热门话题:https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=100

知乎热门视频:https://www.zhihu.com/api/v3/feed/topstory/hot-lists/zvideo?limit=100

知乎热搜: https://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(
  "https://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 真香!

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

 

点击关注,第一时间了解华为云新鲜技术~

已标记关键词 清除标记