UserScript にあります。
「typescript」の言語ハイライトが機能せず、「js」は奇妙に機能します。「javascript」である必要があります。StackOverflow では「ts」と「typescript」の両方が機能しますが。
interface IPost {
id: number
username: string
post_number: number
cooked: string
}
interface ITopicResponse {
actions_summary: {}[]
archetype: string
fancy_title: string
title: string
post_stream: {
posts: IPost[]
stream: number[]
}
posts_count: number
reply_count: number
}
export async function jsonFetch<T>(url: string): Promise<T | null> {
const r = await fetch(url)
if (r.ok) {
const json = await r.json()
if (!json.errors) {
return json
}
}
logger('error', r)
return null
}
export async function fetchAll(urlBase: string) {
const r0 = await jsonFetch<ITopicResponse>(urlBase + '.json?print=true')
if (!r0) return []
const posts: IPost[] = r0.post_stream.posts
let page = 2
while (posts.length < r0.posts_count) {
const r = await jsonFetch<ITopicResponse>(
urlBase + '.json?print=true&page=' + page++
)
if (!r || !r.post_stream.posts.length) {
break
}
posts.push(...r.post_stream.posts)
await new Promise((resolve) => setTimeout(resolve, 1000))
}
return posts
}
fetchAll('https://community.wanikani.com/t/16404').then(console.log)
ts-node スクリプトで Axios を使用している場合、
(node:1102374) UnhandledPromiseRejectionWarning: Error: Request failed with status code 422
10 分ほど待てば、ページ 2 で失敗しますが、今すぐ繰り返すと、どの URL も読み込めません。
そして、print なしでは正常に動作します。
実際には、print=true を回避することで機能させることができました。
export async function fetchAll(urlBase: string) {
const r0 = await jsonFetch<ITopicResponse>(urlBase + '.json');
if (!r0) return [];
const stream = r0.post_stream.stream || [];
const chunks: number[][] = [];
while (stream.length) {
chunks.push(stream.splice(0, 300));
}
const posts: IPost[] = [];
let isContinue = true;
while (chunks.length && isContinue) {
const rs = await Promise.all(
chunks
.splice(0, 10)
.map((ids) =>
jsonFetch<ITopicPostResponse>(
urlBase +
'/posts.json?' +
ids.map((id) => `post_ids[]=${id}`).join('&'),
),
),
).then((rs) =>
rs.map((r) => {
if (!r) {
isContinue = false;
return [];
}
return r.post_stream.posts;
}),
);
rs.map((r) => {
posts.push(...r);
});
if (chunks.length) {
await new Promise((r) => setTimeout(r, 1000));
}
}
posts.push(...r0.post_stream.posts);
if (!isContinue) {
logger(
'error',
`Total posts: ${r0.posts_count} != real count: ${posts.length}, due to Rate Limit?`,
);
}
return posts;
}