注意
微粒子レベルですが名探偵コナンとまじっく快斗のネタバレを含んでいます。苦手な方は注意して読み進めてください。
はじめに
皆さん、アニメ名探偵コナンはご存知でしょうか?「真実はいつも一つ!」とかのたまってる見た目は子ども、頭脳は(自称)大人なバーローが主人公のあのアニメです。
さて、アニメ名探偵コナンはytv(日本テレビ系列)が制作しているので、そこに公式Webページがあります。
そしてこれまでの放送話(事件ファイル)を公開しています。
https://www.ytv.co.jp/conan/archive/index.html
公式の事件ファイルのページはどのように作られているか
最近仕様変更が入ったのですがそれに基づいて話をしていきます。
通信をブラウザで調査すると主に2つのjsonファイルが登場します。
これをもとに自作のツールを作ろうとしていたので具体的に公式でこのjsonをどうパースしてDOMを組み立ててるかは意図的に見ないことにしました。著作権侵害とか言われても困りますしね。
これらのjsonには以下の4つに分類される情報が時系列に混ぜられて格納されています。
- 通常の放送
- 再放送(デジタルリマスター版)
- まじっく快斗の放送(名探偵コナンと同じ原作者青山 剛昌先生の作品、怪盗)
- なぜか放送話の連番から外れた「謹賀新年 毛利小五郎」(2015年1月3日放送)
- 劇場版のTV放送情報(むかしのほうだけ)
(おい、5つあるぞ)
今回は劇場版のTV放送情報は無視することにしました。
story.json
の誤りと表記ゆれの数々
YTVが提供しているこのstory.json
には誤りと表記ゆれが多数存在しています。見ていきましょう。
「名探偵コナンスペシャル 『風林火山 迷宮の鎧武者』」
当該部分のjsonは次のとおりです。
{
"oaDateId" : "20081103",
"title" : "名探偵コナンスペシャル 『風林火山 迷宮の鎧武者』",
"story_num" : "",
"url" : "k116921.html"
},
story_num
が抜けています。516
が正しい値です。
「怪盗キッドの瞬間移動魔術」
当該部分のjsonは次のとおりです。
{
"oaDateId" : "20081020",
"title" : "怪盗キッドの瞬間移動魔術",
"story_num" : "",
"url" : "k116923.html"
},
story_num
が抜けています。515
が正しい値です。
()
と()
の不統一
jsonを示すのはやめますが統一されていません。どっちかにしてくれ・・・。
story_num
がR
から始まるけどオリジナルの放送時とタイトルが一致しない例
title
内のデジタルリマスターの表現が自由すぎる
まあそもそもstory_num
がR
から始まるとデジタルリマスター版であるということになっています。ところが、title
にデジタルリマスターであることを示す文言が入っていなかったり、入り方が多岐に渡ります。
-
そもそもそんなものは含まれていない
デジタルリマスター版放映が始まった初期に多いです。 -
デジタルリマスター
で終端する
わかりやすくていいですね。最近のに多めな気がします。 -
正規表現でどうにか引っ掛けられるケース
const degitalReMasterSearchRegex = /(.+)[[(([]デジタル *リマスター.*[))\]]]$/;
のような正規表現で引っかかります。括弧が同種類で対応しないことすらあります。あと
デジタル リマスター
/デジタルリマスター
の2パターンあります。揺れ過ぎな?ちなみにこの正規表現は黒の組織との接触(デジタルリマスター特別編集版)
みたいなやつにも対応しています。しかもここからさらに
const pureTitle = executed[1].replace(/[ 〔(]+(.{1,2})編[〕) ]*$/, '($1編)'); ``` のような置換が必要です。前後編以外にも`仏滅に出る悪霊(解決編)`みたいなパターンがあります。
-
正規表現で「」の中を抜き出すことでなんとかなるケース
秋の本格的ミステリースペシャル 「呪いの仮面は冷たく笑う」(デジタルリマスター版)
(2004/9/20)が実例ですね。const bracket = /「(.+)」/.exec(title);
ただし
秋のミステリースペシャル名探偵コナン「鳥取クモ屋敷の怪(デジタルリマスター)」
(2007/10/29)のように抜いたあとにさらにデジタルリマスターのような何かを含んでいることもあるのでconst degitalReMasterSearchRegex = /(.+)[[(([]デジタル *リマスター.*[))\]]]$/; //略 const maybePureTitle = bracket[1]; const executed = degitalReMasterSearchRegex.exec(maybePureTitle);
のようにさらなる抽出が必要です。そして
鳥取クモ屋敷の怪
は初回は3話に分けて放送されていますからそれも想定してconst matched = this.pure.filter(c => c.title.includes(pureTitle));
のように全体検索で絞り込む必要が出てきます。
あらかじめstory.json
を置換しておかないときついケース
こういう努力にも関わらずうまく行かないものがたくさんあるので予めstory.json
を置換するプログラムを書くことにしました。
-
集められた名探偵~
: 再放送は分割されたので集められた名探偵!工藤新一VS怪盗キッド
に -
容疑者毛利小五郎~
: もとの話は`容疑者・毛利小五郎~ -
ブラックインパクト~
: 再放送は分割されたので名探偵コナン放送10周年記念超拡大スペシャル「ブラックインパクト!組織の手が届く瞬間」
に -
闇の男爵殺人事件~
:s/闇の男爵殺人事件・(.+)編\(デジタルリマスター\)/闇の男爵(ナイトバロン)殺人事件($1篇)/
-
園子のアブナイ夏物語
:s/園子のアブナイ夏物語((.+)編)\(デジタル・*リマスター\)/園子のアブない夏物語($1編)
const replaceList = [
['TVドラマロケ殺人事件(デジタルリマスター)', 'TVドラマロケ殺人事件'],
['標的(ターゲット)は毛利小五郎(デジタルリマスター)', '標的は毛利小五郎'],
['帝丹小7不思議事件(デジタルリマスター)', '帝丹小7不思議事件'],
['1年B組大作戦!(デジタルリマスター版)', '1年B組大作戦!'],
['黒の組織との再会', '黒の組織との再会(灰原編)'],
['名探偵コナンスペシャル「工藤新一NYの事件」', '工藤新一NYの事件(事件編)'],
['黒の組織との接触(デジタルリマスター特別編集版)', '黒の組織との接触(交渉編)'],
['そして人魚はいなくなった(デジタルリマスター特別編集版)', 'そして人魚はいなくなった(事件編)'],
['名探偵コナン10周年記念スペシャル 「コナンVS怪盗キッド」(デジタルリマスター版)', 'コナンVS怪盗キッド'],
['名探偵コナン1時間スペシャル「空飛ぶ密室 工藤新一最初の事件」', '空飛ぶ密室 工藤新一最初の事件'],
[
'名探偵コナン秋の本格ミステリー2時間スペシャル 揺れる警視庁1200万人の人質」(デジタルリマスター版) ',
'揺れる警視庁 1200万人の人質',
],
['1時間スペシャル(デジタルリマスター版)「迷宮への入り口 巨大神像の怒り」', '迷宮への入口 巨大神像の怒り'],
['怪盗キッドの驚異空中歩行', '怪盗キッドの驚異空中歩行」1時間スペシャル】'],
];
加えてこれらの手動置換が必要です。
case.json
に対する補正
フォーマットが変わったcase.json
、比較的揺れが少ないので「title
内のデジタルリマスターの表現が自由すぎる」の項目の対策だけである程度通るので助かるのですが、やっぱり補正がいらないわけではないようです。
大捜索 9つのドア
大捜索 9つのドア
という放送話ですが、じつはデジタルリマスター版で、もとの放送話は大捜索九つのドア
というタイトルです。
緋色の帰還
上記のような対策をしてもう完璧やろと安心しきっていました。しかし2021年4月、毎週回すようにしていたCIが落ちた知らせが入ります。何かと思ったら「緋色の帰還」系でやらかしてくれました。こいつもまたタイトルだけではデジタルリマスター版と見抜けないシリーズです。しかも元の放送話とタイトルの規則が違うので置換が必要です。
const replaceScarletCase = (title: string) => {
if (!title.startsWith('緋色の帰還')) return undefined;
const k = '(デジタルリマスター)';
// 2021-04-10放送のタイトルは、緋色の帰還(真相)、なんと(デジタルリマスター)って書いてない!
const t = title.endsWith(k) ? title.substring(0, title.length - k.length) : title;
return t.replace(/緋色の帰還(([^)]+))/, '緋色の$1');
};
名探偵コナン放送話検索ツールを作ったが、結局どういうフローでこれらの誤りに立ち向かっているのか
これが作ったものです。
typescriptで書いています。ビルドにはWebpackを使っていましたがesbuildに乗り換えました。テストコードの実行が律速するのでCIの実行時間的にはあまり意味はありません。
ビルドは2回行われます
-
story.json
の事前変換ツールのビルド -
story.json
の事前変換: 実行時に持っていけない辛さを予めなんとかする- 初回放送っぽいのを抜き出す
- 手動置換や正規表現を駆使して再放送と初回放送をさらに分離
-
まじっく快斗
/怪盗キッド
を含むor聖夜(イブ)は恋するゲレンデで
はまじっく快斗に分離 - 残りは特別話として扱う
- 変換された
story.json
をbundleして実行時に動かすツールをビルド
実行時にはcase.json
を取得して分類していきます。このときPureDatabase#find
関数が上記のような変換をして揺れを吸収します。ただしcase.json
に対する補正は分類の前に当てます。
教訓
世に送り出すデータがきちんと正規化されていないと、利用者はこういう膨大な苦労が必要になります。
YTVさんはべつにデータを提供しようとはさほど思っていないだろうと予想できますのでいいのですが、
データを提供しよう、という人は使う人がパースしやすい形式で正規化して提供するように心がけるといいと思います。
注意
npm packageにするとstory.json
を梱包するような形になるので著作権に控えめに言って喧嘩を売ってしまうものと思います。ましてや再頒布とかするのはアウトです。やめましょう。