💬

帰ってきたニコニコからコメントを取得する

2024/08/13に公開

祝🎉ニコニコ動画復活

バージョンが帰ってきたニコニコとなり、コメントの取得方法が変わりました。
変更点と実装など述べていきます
間違ってたらコメント下さい

過去バージョンからの変更点

コメント取得にはThreadIDとThreadKeyが必要です。
過去バージョンではidにjs-initial-watch-dataを持つdivタグがHTML内にあり、それをパースして取得していました。
新バージョンではそのdivタグがなくなり、代わりにscriptタグに埋め込まれたJSONから値を取得できるようになりました
下の実装では正規表現で抜き取っています

あと、これは新バージョンからなのかわからないんですけど、ヘッダーにx-frontend-idを指定してあげないと400で怒られます

それ以外は多分過去バージョンと一緒です

実装

言語はTypeScript
動作確認はDeno

import { ThreadIdFetchError, ThreadKeyFetchError } from "./error.ts";

interface ThreadRequestBody{
    params: {
        targets: {
            id: string;
            fork: "owner" | "main" | "easy";
        }[];
        language: "ja-jp";
    }
    threadKey: string;
    additionals: Record<string | number | symbol, never>;
}

interface ThreadResponse{
    meta: {
        status: number;
    },
    data: {
        globalComments: {
            id: string;
            count: number;
        }[];
        threads: {
            id: number;
            fork: "owner" | "main" | "easy";
            commentCount: number;
            comments: {
                id: string;
                no: number;
                vposMs: number;
                body: string;
                commands: string[];
                userId: string;
                isPremium: boolean;
                score: number;
                postedAt: string;
                nicoruCount: number;
                nicoruId: string | null;
                source: "trunk" | "leaf" | "nicoru";
                isMyPost: boolean;
            }[];
        }[];
    }
}

const url = "https://www.nicovideo.jp/watch/sm34238873" //ここに取得先のURL

const endpoint = "https://public.nvcomment.nicovideo.jp/v1/threads";
const threadIdRegex = /threadIds&quot;:\[\{&quot;id&quot;:(.*?),&quot;/;
const threadKeyRegex = /{&quot;threadKey&quot;:&quot;(eyJ0eXAiOiJKV1Qi.*?)&quot/;

const videoPage = await (await fetch(url)).text();
const threadId = videoPage.match(threadIdRegex)?.[1];
const threadKey = videoPage.match(threadKeyRegex)?.[1];
if(!threadId){
    throw new ThreadIdFetchError();
}
if(!threadKey){
    throw new ThreadKeyFetchError();
}
const payload: ThreadRequestBody = {
    params: {
        targets: [
            {
                id: threadId,
                fork: "owner"
            },
            {
                id: threadId,
                fork: "main"
            },
            {
                id: threadId,
                fork: "easy"
            }
        ],
        language: "ja-jp"
    },
    threadKey: threadKey,
    additionals: {}
}

const threadResponse = await (await fetch(endpoint, {
    method: "POST",
    body: JSON.stringify(payload),
    headers: {
        "x-frontend-id": "6"
    }
}));

const response = await threadResponse.json() as ThreadResponse;

参考文献

https://qiita.com/vram/items/d70ed02b308fd9e70b2e
https://qiita.com/tor4kichi/items/1d57e33b55c832f197e2
https://gist.github.com/otya128/9c7499cf667e75964b43d46c8c567e37

Discussion