and 🎨

This commit is contained in:
huqi 2020-11-29 13:03:19 +08:00
parent 08b5a73e2b
commit 0c4413f376
9 changed files with 321 additions and 61 deletions

20
mod.ts
View File

@ -1,11 +1,11 @@
import { zhihuVideo } from './zhihu-video.ts' import { zhihuVideo } from "./zhihu-video.ts";
import { zhihuQuestions } from './zhihu-questions.ts' import { zhihuQuestions } from "./zhihu-questions.ts";
import { zhihuSearch } from './zhihu-search.ts' import { zhihuSearch } from "./zhihu-search.ts";
import { weiboSearch } from './weibo-search.ts' import { weiboSearch } from "./weibo-search.ts";
import { toutiaoSearch } from './toutiao-search.ts' import { toutiaoSearch } from "./toutiao-search.ts";
zhihuVideo() zhihuVideo();
zhihuQuestions() zhihuQuestions();
zhihuSearch() zhihuSearch();
weiboSearch() weiboSearch();
toutiaoSearch() toutiaoSearch();

View File

@ -5,9 +5,15 @@ import { join } from "std/path/mod.ts";
import { exists } from "std/fs/mod.ts"; import { exists } from "std/fs/mod.ts";
import type { ToutiaoTopSearch, ToutiaoWord } from "./types.ts"; import type { ToutiaoTopSearch, ToutiaoWord } from "./types.ts";
import { createArchive4Toutiao, createReadme4Toutiao, mergeWords4Toutiao } from "./utils.ts"; import {
createArchive4Toutiao,
createReadme4Toutiao,
mergeWords4Toutiao,
} from "./utils.ts";
const response = await fetch("https://is-lq.snssdk.com/api/suggest_words/?business_id=10016"); const response = await fetch(
"https://is-lq.snssdk.com/api/suggest_words/?business_id=10016",
);
if (!response.ok) { if (!response.ok) {
console.error(response.statusText); console.error(response.statusText);
@ -26,11 +32,10 @@ if (await exists(fullPath)) {
wordsAlreadyDownload = JSON.parse(content); wordsAlreadyDownload = JSON.parse(content);
} }
const wordsAll = mergeWords4Toutiao(words, wordsAlreadyDownload); const wordsAll = mergeWords4Toutiao(words, wordsAlreadyDownload);
export const ToutiaoSearchData = wordsAll.map(x=> { export const ToutiaoSearchData = wordsAll.map((x) => {
x.url = `https://so.toutiao.com/search?keyword=${x.word.replace(' ','+')}`; x.url = `https://so.toutiao.com/search?keyword=${x.word.replace(" ", "+")}`;
return x; return x;
}); });
@ -46,5 +51,4 @@ export async function toutiaoSearch() {
const archiveText = createArchive4Toutiao(wordsAll, yyyyMMdd); const archiveText = createArchive4Toutiao(wordsAll, yyyyMMdd);
const archivePath = join("archives/toutiao-search", `${yyyyMMdd}.md`); const archivePath = join("archives/toutiao-search", `${yyyyMMdd}.md`);
await Deno.writeTextFile(archivePath, archiveText); await Deno.writeTextFile(archivePath, archiveText);
} }

View File

@ -32,7 +32,7 @@ export type ZhihuQuestionList = {
export type SearchWord = { export type SearchWord = {
query: string; query: string;
display_query: string; display_query: string;
url?: string url?: string;
}; };
export type TopSearch = { export type TopSearch = {
@ -50,12 +50,12 @@ export type Word = {
export type ToutiaoTopSearch = { export type ToutiaoTopSearch = {
data: [ data: [
{ {
words: ToutiaoWord[] words: ToutiaoWord[];
} },
] ];
} };
export type ToutiaoWord = { export type ToutiaoWord = {
word: string; word: string;
url?: string url: string;
}; };

View File

@ -1,14 +1,99 @@
#!/usr/bin/env -S deno run --unstable --allow-net --allow-read --allow-write --import-map=import_map.json #!/usr/bin/env -S deno run --unstable --allow-net --allow-read --allow-write --import-map=import_map.json
import { assertEquals, assertStringIncludes } from "std/testing/asserts.ts"; import { assertEquals, assertStringIncludes } from "std/testing/asserts.ts";
import type { Question } from "./types.ts"; import type { Question, SearchWord, Word, ToutiaoWord } from "./types.ts";
import { import {
createArchive, createArchive,
createList, createTuotiaoList,
createReadme, createReadme,
mergeWords,
mergeQuestions, mergeQuestions,
mergeWords4Toutiao,
mergeWords4Weibo,
createWeiboList,
createQuestionList,
createReadme4Toutiao,
createArchive4Toutiao,
createArchive4Weibo,
createReadme4Weibo,
createReadme4Search,
createArchive4Search,
createSearchList,
} from "./utils.ts"; } from "./utils.ts";
Deno.test("mergeWords4Toutiao", function (): void {
const words1: ToutiaoWord[] = [];
const words2: ToutiaoWord[] = [{ word: "foo", url: "bar" }];
const words3: ToutiaoWord[] = [{ word: "foo", url: "hello" }];
const words4: ToutiaoWord[] = [{ word: "hello", url: "world" }];
const words5: ToutiaoWord[] = [
{ word: "foo", url: "bar" },
{ word: "hello", url: "world" },
];
assertEquals(mergeWords4Toutiao(words1, words2), words2);
assertEquals(mergeWords4Toutiao(words1, words5), words5);
assertEquals(mergeWords4Toutiao(words2, words2), words2);
assertEquals(
mergeWords4Toutiao(words2, words3),
[
{ word: "foo", url: "bar" },
{ word: "foo", url: "hello" },
],
);
assertEquals(mergeWords4Toutiao(words4, words5), [
{ word: "hello", url: "world" },
{ word: "foo", url: "bar" },
]);
assertEquals(
mergeWords4Toutiao(words3, words5),
[
{ word: "foo", url: "hello" },
{ word: "foo", url: "bar" },
{ word: "hello", url: "world" },
],
);
});
Deno.test("createTuotiaoList", function (): void {
const words: ToutiaoWord[] = [
{ word: "foo", url: "bar" },
{ word: "hello", url: "world" },
];
assertStringIncludes(createTuotiaoList(words), "<!-- BEGIN TOUTIAO -->");
assertStringIncludes(createTuotiaoList(words), "<!-- END TOUTIAO -->");
assertStringIncludes(createTuotiaoList(words), "foo");
assertStringIncludes(createTuotiaoList(words), "world");
assertStringIncludes(createTuotiaoList(words), "hello");
});
Deno.test("createArchive4Toutiao", function (): void {
const words: ToutiaoWord[] = [
{ word: "foo", url: "bar" },
{ word: "hello", url: "world" },
];
assertStringIncludes(
createArchive4Toutiao(words, "2020-02-02"),
"# 2020-02-02",
);
assertStringIncludes(createArchive4Toutiao(words, "2020-02-02"), "共 2 条");
});
Deno.test("createReadme4Toutiao", async function (): Promise<void> {
const words: ToutiaoWord[] = [
{ word: "foo", url: "bar" },
{ word: "hello", url: "world" },
];
assertStringIncludes(await createReadme4Toutiao(words), "头条");
assertStringIncludes(
await createReadme4Toutiao(words),
"zhihu-trending-hot-questions",
);
});
Deno.test("mergeQuestions", function (): void { Deno.test("mergeQuestions", function (): void {
const words1: Question[] = []; const words1: Question[] = [];
const words2: Question[] = [{ title: "foo", url: "bar" }]; const words2: Question[] = [{ title: "foo", url: "bar" }];
@ -43,17 +128,23 @@ Deno.test("mergeQuestions", function (): void {
); );
}); });
Deno.test("createList", function (): void { Deno.test("createQuestionList", function (): void {
const words: Question[] = [ const words: Question[] = [
{ title: "foo", url: "bar" }, { title: "foo", url: "bar" },
{ title: "hello", url: "world" }, { title: "hello", url: "world" },
]; ];
assertStringIncludes(createList(words), "<!-- BEGIN -->"); assertStringIncludes(
assertStringIncludes(createList(words), "<!-- END -->"); createQuestionList(words),
assertStringIncludes(createList(words), "foo"); "<!-- BEGIN ZHIHUQUESTIONS -->",
assertStringIncludes(createList(words), "world"); );
assertStringIncludes(createList(words), "hello"); assertStringIncludes(
createQuestionList(words),
"<!-- END ZHIHUQUESTIONS -->",
);
assertStringIncludes(createQuestionList(words), "foo");
assertStringIncludes(createQuestionList(words), "world");
assertStringIncludes(createQuestionList(words), "hello");
}); });
Deno.test("createArchive", function (): void { Deno.test("createArchive", function (): void {
@ -72,9 +163,155 @@ Deno.test("createReadme", async function (): Promise<void> {
{ title: "hello", url: "world" }, { title: "hello", url: "world" },
]; ];
assertStringIncludes(await createReadme(words), "知乎"); assertStringIncludes(await createReadme(words), "热门");
assertStringIncludes( assertStringIncludes(
await createReadme(words), await createReadme(words),
"zhihu-trending-hot-questions", "zhihu-trending-hot-questions",
); );
}); });
Deno.test("mergeWords4Weibo", function (): void {
const words1: Word[] = [];
const words2: Word[] = [{ title: "foo", url: "bar" }];
const words3: Word[] = [{ title: "foo", url: "hello" }];
const words4: Word[] = [{ title: "hello", url: "world" }];
const words5: Word[] = [
{ title: "foo", url: "bar" },
{ title: "hello", url: "world" },
];
assertEquals(mergeWords4Weibo(words1, words2), words2);
assertEquals(mergeWords4Weibo(words1, words5), words5);
assertEquals(mergeWords4Weibo(words2, words2), words2);
assertEquals(
mergeWords4Weibo(words2, words3),
[
{ title: "foo", url: "bar" },
{ title: "foo", url: "hello" },
],
);
assertEquals(mergeWords4Weibo(words4, words5), [
{ title: "hello", url: "world" },
{ title: "foo", url: "bar" },
]);
assertEquals(
mergeWords4Weibo(words3, words5),
[
{ title: "foo", url: "hello" },
{ title: "foo", url: "bar" },
{ title: "hello", url: "world" },
],
);
});
Deno.test("createWeiboList", function (): void {
const words: Word[] = [
{ title: "foo", url: "bar" },
{ title: "hello", url: "world" },
];
assertStringIncludes(createWeiboList(words), "<!-- BEGIN WEIBO -->");
assertStringIncludes(createWeiboList(words), "<!-- END WEIBO -->");
assertStringIncludes(createWeiboList(words), "foo");
assertStringIncludes(createWeiboList(words), "world");
assertStringIncludes(createWeiboList(words), "hello");
});
Deno.test("createArchive4Weibo", function (): void {
const words: Word[] = [
{ title: "foo", url: "bar" },
{ title: "hello", url: "world" },
];
assertStringIncludes(
createArchive4Weibo(words, "2020-02-02"),
"# 2020-02-02",
);
assertStringIncludes(createArchive4Weibo(words, "2020-02-02"), "共 2 条");
});
Deno.test("createReadme4Weibo", async function (): Promise<void> {
const words: Word[] = [
{ title: "foo", url: "bar" },
{ title: "hello", url: "world" },
];
assertStringIncludes(await createReadme4Weibo(words), "微博");
assertStringIncludes(
await createReadme4Weibo(words),
"zhihu-trending-hot-questions",
);
});
Deno.test("mergeWords", function (): void {
const words1: SearchWord[] = [];
const words2: SearchWord[] = [{ query: "foo", display_query: "bar" }];
const words3: SearchWord[] = [{ query: "foo", display_query: "hello" }];
const words4: SearchWord[] = [{ query: "hello", display_query: "world" }];
const words5: SearchWord[] = [
{ query: "foo", display_query: "bar" },
{ query: "hello", display_query: "world" },
];
assertEquals(mergeWords(words1, words2), words2);
assertEquals(mergeWords(words1, words5), words5);
assertEquals(mergeWords(words2, words2), words2);
assertEquals(
mergeWords(words2, words3),
[
{ query: "foo", display_query: "bar" },
{ query: "foo", display_query: "hello" },
],
);
assertEquals(mergeWords(words4, words5), [
{ query: "hello", display_query: "world" },
{ query: "foo", display_query: "bar" },
]);
assertEquals(
mergeWords(words3, words5),
[
{ query: "foo", display_query: "hello" },
{ query: "foo", display_query: "bar" },
{ query: "hello", display_query: "world" },
],
);
});
Deno.test("createSearchList", function (): void {
const words: SearchWord[] = [
{ query: "foo", display_query: "bar" },
{ query: "hello", display_query: "world" },
];
assertStringIncludes(createSearchList(words), "<!-- BEGIN ZHIHUSEARCH -->");
assertStringIncludes(createSearchList(words), "<!-- END ZHIHUSEARCH -->");
assertStringIncludes(createSearchList(words), "foo");
assertStringIncludes(createSearchList(words), "world");
assertStringIncludes(createSearchList(words), "hello");
});
Deno.test("createArchive4Search", function (): void {
const words: SearchWord[] = [
{ query: "foo", display_query: "bar" },
{ query: "hello", display_query: "world" },
];
assertStringIncludes(
createArchive4Search(words, "2020-02-02"),
"# 2020-02-02",
);
assertStringIncludes(createArchive4Search(words, "2020-02-02"), "共 2 条");
});
Deno.test("createReadme4Search", async function (): Promise<void> {
const words: SearchWord[] = [
{ query: "foo", display_query: "bar" },
{ query: "hello", display_query: "world" },
];
assertStringIncludes(await createReadme4Search(words), "热搜");
assertStringIncludes(
await createReadme4Search(words),
"zhihu-trending-hot-questions",
);
});

View File

@ -37,7 +37,7 @@ export function mergeWords4Toutiao(
): ToutiaoWord[] { ): ToutiaoWord[] {
const obj: Record<string, string> = {}; const obj: Record<string, string> = {};
for (const w of words.concat(another)) { for (const w of words.concat(another)) {
obj[w.word] = w.word; obj[w.url] = w.word;
} }
return Object.entries(obj).map(([url, word]) => ({ return Object.entries(obj).map(([url, word]) => ({
url, url,
@ -62,24 +62,42 @@ export function mergeWords4Weibo(
export async function createReadme(words: Question[]): Promise<string> { export async function createReadme(words: Question[]): Promise<string> {
const readme = await Deno.readTextFile("./README.md"); const readme = await Deno.readTextFile("./README.md");
return readme.replace(/<!-- BEGIN ZHIHUVIDEO -->[\W\w]*<!-- END ZHIHUVIDEO -->/, createVideoList(words)) return readme.replace(
.replace(/<!-- BEGIN ZHIHUQUESTIONS -->[\W\w]*<!-- END ZHIHUQUESTIONS -->/, createQuestionList(words)); /<!-- BEGIN ZHIHUVIDEO -->[\W\w]*<!-- END ZHIHUVIDEO -->/,
createVideoList(words),
)
.replace(
/<!-- BEGIN ZHIHUQUESTIONS -->[\W\w]*<!-- END ZHIHUQUESTIONS -->/,
createQuestionList(words),
);
} }
export async function createReadme4Search(words: SearchWord[]): Promise<string> { export async function createReadme4Search(
words: SearchWord[],
): Promise<string> {
const readme = await Deno.readTextFile("./README.md"); const readme = await Deno.readTextFile("./README.md");
return readme.replace(/<!-- BEGIN ZHIHUSEARCH -->[\W\w]*<!-- END ZHIHUSEARCH -->/, createSearchList(words)); return readme.replace(
/<!-- BEGIN ZHIHUSEARCH -->[\W\w]*<!-- END ZHIHUSEARCH -->/,
createSearchList(words),
);
} }
export async function createReadme4Weibo(words: Word[]): Promise<string> { export async function createReadme4Weibo(words: Word[]): Promise<string> {
const readme = await Deno.readTextFile("./README.md"); const readme = await Deno.readTextFile("./README.md");
return readme.replace(/<!-- BEGIN WEIBO -->[\W\w]*<!-- END WEIBO -->/, createWeiboList(words)); return readme.replace(
/<!-- BEGIN WEIBO -->[\W\w]*<!-- END WEIBO -->/,
createWeiboList(words),
);
} }
export async function createReadme4Toutiao(words: ToutiaoWord[]): Promise<string> { export async function createReadme4Toutiao(
words: ToutiaoWord[],
): Promise<string> {
const readme = await Deno.readTextFile("./README.md"); const readme = await Deno.readTextFile("./README.md");
return readme.replace(/<!-- BEGIN TOUTIAO -->[\W\w]*<!-- END TOUTIAO -->/, createTuotiaoList(words)); return readme.replace(
/<!-- BEGIN TOUTIAO -->[\W\w]*<!-- END TOUTIAO -->/,
createTuotiaoList(words),
);
} }
export function createVideoList(words: Question[]): string { export function createVideoList(words: Question[]): string {
@ -133,8 +151,6 @@ ${
<!-- END TOUTIAO -->`; <!-- END TOUTIAO -->`;
} }
export function createArchive(words: Question[], date: string): string { export function createArchive(words: Question[], date: string): string {
return `# ${date}\n return `# ${date}\n
${words.length} \n ${words.length} \n
@ -142,7 +158,10 @@ ${createVideoList(words)}
`; `;
} }
export function createArchive4Search(words: SearchWord[], date: string): string { export function createArchive4Search(
words: SearchWord[],
date: string,
): string {
return `# ${date}\n return `# ${date}\n
${words.length} \n ${words.length} \n
${createSearchList(words)} ${createSearchList(words)}
@ -156,10 +175,12 @@ ${createWeiboList(words)}
`; `;
} }
export function createArchive4Toutiao(words: ToutiaoWord[], date: string): string { export function createArchive4Toutiao(
words: ToutiaoWord[],
date: string,
): string {
return `# ${date}\n return `# ${date}\n
${words.length} \n ${words.length} \n
${createTuotiaoList(words)} ${createTuotiaoList(words)}
`; `;
} }

View File

@ -5,7 +5,11 @@ import { join } from "std/path/mod.ts";
import { exists } from "std/fs/mod.ts"; import { exists } from "std/fs/mod.ts";
import type { Word } from "./types.ts"; import type { Word } from "./types.ts";
import { createArchive4Weibo, createReadme4Weibo, mergeWords4Weibo } from "./utils.ts"; import {
createArchive4Weibo,
createReadme4Weibo,
mergeWords4Weibo,
} from "./utils.ts";
const regexp = /<a href="(\/weibo\?q=[^"]+)".*?>(.+)<\/a>/g; const regexp = /<a href="(\/weibo\?q=[^"]+)".*?>(.+)<\/a>/g;
@ -36,12 +40,11 @@ if (await exists(fullPath)) {
const queswordsAll = mergeWords4Weibo(words, wordsAlreadyDownload); const queswordsAll = mergeWords4Weibo(words, wordsAlreadyDownload);
export const weiboSearchData = queswordsAll.map(x=> { export const weiboSearchData = queswordsAll.map((x) => {
x.realurl = `https://s.weibo.com/${x.url}`; x.realurl = `https://s.weibo.com/${x.url}`;
return x; return x;
}); });
export async function weiboSearch() { export async function weiboSearch() {
// 保存原始数据 // 保存原始数据
await Deno.writeTextFile(fullPath, JSON.stringify(queswordsAll)); await Deno.writeTextFile(fullPath, JSON.stringify(queswordsAll));
@ -54,6 +57,4 @@ export async function weiboSearch() {
const archiveText = createArchive4Weibo(queswordsAll, yyyyMMdd); const archiveText = createArchive4Weibo(queswordsAll, yyyyMMdd);
const archivePath = join("archives/weibo-search", `${yyyyMMdd}.md`); const archivePath = join("archives/weibo-search", `${yyyyMMdd}.md`);
await Deno.writeTextFile(archivePath, archiveText); await Deno.writeTextFile(archivePath, archiveText);
} }

View File

@ -8,7 +8,6 @@ import { exists } from "std/fs/mod.ts";
import type { ZhihuQuestionList, Question } from "./types.ts"; import type { ZhihuQuestionList, Question } from "./types.ts";
import { createArchive, createReadme, mergeQuestions } from "./utils.ts"; import { createArchive, createReadme, mergeQuestions } from "./utils.ts";
const response = await fetch( const response = await fetch(
"https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=100", "https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total?limit=100",
); );
@ -39,7 +38,6 @@ const questionsAll = mergeQuestions(questions, questionsAlreadyDownload);
export const zhihuQuestionData = questionsAll; export const zhihuQuestionData = questionsAll;
export async function zhihuQuestions() { export async function zhihuQuestions() {
// 保存原始数据 // 保存原始数据
await Deno.writeTextFile(fullPath, JSON.stringify(questionsAll)); await Deno.writeTextFile(fullPath, JSON.stringify(questionsAll));
@ -51,5 +49,4 @@ export async function zhihuQuestions() {
const archiveText = createArchive(questionsAll, yyyyMMdd); const archiveText = createArchive(questionsAll, yyyyMMdd);
const archivePath = join("archives/zhihu-questions", `${yyyyMMdd}.md`); const archivePath = join("archives/zhihu-questions", `${yyyyMMdd}.md`);
await Deno.writeTextFile(archivePath, archiveText); await Deno.writeTextFile(archivePath, archiveText);
} }

View File

@ -5,7 +5,11 @@ import { join } from "std/path/mod.ts";
import { exists } from "std/fs/mod.ts"; import { exists } from "std/fs/mod.ts";
import type { SearchWord, TopSearch } from "./types.ts"; import type { SearchWord, TopSearch } from "./types.ts";
import { createArchive4Search, createReadme4Search, mergeWords } from "./utils.ts"; import {
createArchive4Search,
createReadme4Search,
mergeWords,
} from "./utils.ts";
const response = await fetch("https://www.zhihu.com/api/v4/search/top_search"); const response = await fetch("https://www.zhihu.com/api/v4/search/top_search");
@ -28,7 +32,7 @@ if (await exists(fullPath)) {
const wordsAll = mergeWords(words, wordsAlreadyDownload); const wordsAll = mergeWords(words, wordsAlreadyDownload);
export const zhihuSearchData = wordsAll.map(x=> { export const zhihuSearchData = wordsAll.map((x) => {
x.url = `https://www.zhihu.com/search?q=${x.query}`; x.url = `https://www.zhihu.com/search?q=${x.query}`;
return x; return x;
}); });
@ -45,5 +49,4 @@ export async function zhihuSearch() {
const archiveText = createArchive4Search(wordsAll, yyyyMMdd); const archiveText = createArchive4Search(wordsAll, yyyyMMdd);
const archivePath = join("archives/zhihu-search", `${yyyyMMdd}.md`); const archivePath = join("archives/zhihu-search", `${yyyyMMdd}.md`);
await Deno.writeTextFile(archivePath, archiveText); await Deno.writeTextFile(archivePath, archiveText);
} }

View File

@ -42,7 +42,6 @@ const questionsAll = mergeQuestions(questions, questionsAlreadyDownload);
export const zhihuVideoData = questionsAll; export const zhihuVideoData = questionsAll;
export async function zhihuVideo() { export async function zhihuVideo() {
// 保存原始数据 // 保存原始数据
await Deno.writeTextFile(fullPath, JSON.stringify(questionsAll)); await Deno.writeTextFile(fullPath, JSON.stringify(questionsAll));
@ -54,6 +53,4 @@ export async function zhihuVideo() {
const archiveText = createArchive(questionsAll, yyyyMMdd); const archiveText = createArchive(questionsAll, yyyyMMdd);
const archivePath = join("archives/zhihu-video", `${yyyyMMdd}.md`); const archivePath = join("archives/zhihu-video", `${yyyyMMdd}.md`);
await Deno.writeTextFile(archivePath, archiveText); await Deno.writeTextFile(archivePath, archiveText);
} }