🍎

ニコニコ動画のスナップショット検索API v2のクライアントを作った

2023/08/10に公開

概要

以下の記事を見て使いたかったのですが、PythonがきらいなのでTypeScriptで作りました。
https://zenn.dev/javakky/articles/3f9cd733554846

成果物はこちらです。
https://www.npmjs.com/package/nico-snapshot

環境

  • TypeScript v5.1.6
  • MacOS Ventura v13.0.1
  • npm v9.6.6

使い方

nico-snapshotからNicoSnapshotをimportします。

const { NicoSnapshot } = require("nico-snapshot");

const client = new NicoSnapshot();

Query

検索クエリ用のメソッドは5つ用意されています。

  • AddAndQuery()
  • SetAndQuery()
  • AddOrQuery()
  • SetOrQuery()
  • AddNotQuery()
    例えば
client.AddAndQuery("音MAD", "クールポコ");

だとAND条件のクエリを追加するので「音MAD」「クールポコ」の両方を含むクエリになります。また、

client.AddAndQuery("音MAD").AddOrQuery("クールポコ", "デスノート");

だと「音MAD」を含むうちOR条件の「クールポコ」または「デスノート」のどちらかを含むクエリになります。

client
    .AddAndQuery("音mad")
    .AddOrQuery("クールポコ", "デスノート")
    .AddNotQuery("R18");

だと先ほどの条件に加え、NOT条件の「R18」を含むものが検索結果から除外されます。(こんなものはないのですが)
あと、NOTは単独で入れるとエラーが発生するのでSetNotQuery()はありません。

Field

fieldとは先ほど設定したクエリの検索対象です。例えば

client.SetFields(Field.title, Field.description);

だと動画のタイトル・概要欄が検索クエリが対象なので、「音mad クールポコ」というタグがついている動画でもタイトルや概要欄がクエリに一致しないと検索結果に含まれません。
4つのメソッドが用意されています。

  • SetFields()
  • AddFields()
  • TagsExact() (タグ完全一致)
  • Keyword() (タイトル・概要欄・タグ)

フィールド一覧

Field 意味
contentId コンテンツID (https://nico.ms/ の後に連結すると動画リンクになる)
title タイトル
description 動画の概要欄
userId 投稿者のユーザーID (ユーザー投稿動画の場合のみ)
channelId 投稿チャンネルのチャンネルID (チャンネル投稿動画の場合のみ)
view_count 再生数
mylist_count マイリストされた数
like_count いいねされた数
playtime 再生時間
thumbnail_url サムネのURL
startTime 動画投稿時間
lastComments 最新のコメント (string型なので場合に応じて配列にparseする必要あり)
comment_count コメント数
categoryTags カテゴリタグ
tags タグ
tagsExact タグ完全一致
genre ジャンル
genre ジャンル完全一致

Target

targetとはレスポンスで返ってくるfieldのことです。
2つのメソッドが用意されています。

  • AddTargets()
  • SetTargets()
client.SetTargets(Field.title, Field.contendId);

だと条件に適した動画のタイトル・コンテンツIDが取得できます。

Filter

filterとは検索結果の範囲を絞るプロパティです。
2つのメソッドが用意されています。

  • AddFilter()
  • SetFilter()
    例えば
client.AddFilter(FilterType.gte, Field.view_count, "1000");

だと検索結果が「再生数」が「1000」「以上」の範囲に絞られます。
FilterTypeの早見表

Type 意味
equal 同値
gt より大きい
gte 以上
lt より小さい
lte 以下

Context

contextはアプリケーション名です。SetContext()で変更できます。初期値は"NicoSnapshot v0.1.0"です。

Limit

limitは取得するコンテンツの上限数です。SetLimit()で変更できます。初期値は10です。

Offset

offsetは取得するコンテンツの取得オフセットです。SetOffset() AddOffset()で変更できます。初期値は0です。

Sort

sortはどのフィールドで降順・昇順かを指定するものです。SetSort()で変更できます。初期値は「再生数」で「降順」です。(Descendingが降順、Ascendingが昇順)

client.SetSorter(SortType.Descending, Field.view_count);

データ取得

Fetch()でデータが取得できます。fieldで設定したフィールドのinterfaceなりを作ってparseすることを推奨します。

client.Fetch(); //非同期

URL

BuildURL()で設定した条件のURLを取得できます。

まとめ

  • タグ完全一致
  • 「音MAD」を含む
  • 「クールポコ」または「デスノート」を含む
  • 「R18」を含まない
  • タイトル・コンテンツIDを取得する
  • 再生数が1000以上
  • 再生数で降順
index.js
const { NicoSnapshot, Field, FilterType, SortType } = require("nico-snapshot");

const client = new NicoSnapshot();

client
    .AddAndQuery("音mad")
    .AddOrQuery("クールポコ", "デスノート")
    .AddNotQuery("R18");

client.TagsExact();

client.SetFields(Field.title, Field.contendId);

client.AddFilter(FilterType.gte, Field.view_count, "1000");

client.SetSorter(SortType.Descending, Field.view_count);

client.Fetch().then((e) => console.log(e));
log
{
  data: [
    {
      title: 'カッコつけて日本の上空にミサイルを飛ばした北の国の将軍がいたんですよ〜',
      contentId: 'sm26218617'
    },
    { title: '忍たま馬鹿野郎', contentId: 'sm28312508' },
    { title: 'ですのフレンズ【デスノート×けものフレンズ】', contentId: 'sm30726054' },
    { title: 'ヒトリノ夜神', contentId: 'sm15180564' },
    { title: 'ですのうりん', contentId: 'sm22901036' },
    { title: '新新新世界【キラの名は。】', contentId: 'sm30739182' },
    { title: 'マツダダダダ天使', contentId: 'sm34185820' },
    { title: '空想バカヤロヲ 【デ/スノ×未来日記】', contentId: 'sm19936552' },
    { title: 'ですのフレンズ', contentId: 'sm30720197' },
    { title: 'キラキラ動画流星群', contentId: 'sm20149626' }
  ],
  meta: {
    id: 'bf07edee-e895-4c3b-8419-a353c62ee561',
    totalCount: 256,
    status: 200
  }
}

これからも随時更新していきます。良いニコニコライフを!

Discussion