+
InternetArchiveの検索結果のURL: {iaSearchPageUrl}
+
iframeのURL: {iframeUrl}
+
※アキバ総研のページをIneternetArchiveで見る時はJavascriptを無効化しないと、強制的にリダイレクトされてしまいます
+
+
以下のページはInternetArchiveに保存されている内容です。
+
+
+
+ );
+}
+function parseParam(paramString: string) {
+ let m: RegExpMatchArray | null = null;
+ if (m = paramString.match(/^article-(\d+)$/)) {
+ return `https://akiba-souken.com/article/${m[1]}/`
+ } else if (m = paramString.match(/^article-(\d+)-(\d+)$/)) {
+ return `https://akiba-souken.com/article/${m[1]}/?page=${m[2]}`
+ } else if (m = paramString.match(/^anime-(\d+)$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/`;
+ } else if (m = paramString.match(/^anime-(\d+)-review$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/review/`;
+ } else if (m = paramString.match(/^anime-(\d+)-review-p(\d+)$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/review/?page=${m[2]}`;
+ } else if (m = paramString.match(/^anime-(\d+)-review-(\d+)$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/review/${m[2]}/`;
+ }
+ return null;
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..9d5b2b9
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,28 @@
+import type { Metadata } from "next";
+import "./globals.css";
+import { NavigationHeader } from "./_components/navigationHeader";
+
+export const metadata: Metadata = {
+ title: "アキバ総研アーカイブ",
+ //description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+ アキバ総研(
https://akiba-souken.com/ ) の記事一覧をアーカイブしたページです。このサイト内にアキバ総研の記事本文はありません。
+ アキバ総研は2024/08/01にサービス終了のアナウンスが行われ、2024/08/31に記事の更新が停止しました。
+ このアーカイブは2024/08/31~2024/09/30 の間に取得していますが、特定の瞬間のスナップショットではないので、データの不整合がある可能性があります。
+ アキバ総研は記事ページ内にドメインがakiba-souken.com でない場合にトップページにリダイレクトされるjavascriptが仕込まれていますので、InternetArchiveから閲覧する時はdevtool等でjsをオフにする必要があります。
+ このページはGithubActions&GithubPagesでデプロイされています。生データが欲しい方はレポジトリ https://github.com/fushihara/akiba-souken-archive をチェックアウトする事をおすすめします。
+
アキバ総研とは?
+ アキバ総研はカカクコムが2002/08から2024/09まで公開していたアキバ系ニュースサイトです。
+ 初期のURLは http://kakaku.com/akiba/ 形式で、価格.comのサブディレクトリにありました。
+ 2007年頃から http://akiba.kakaku.com/ に移動し、2011年頃から https://akiba-souken.com/ ドメインに移動しました。
+ 過去の記事の継続性についてですが、少なくともkakaku.comドメイン配下のURLは2024/09時点で全てakiba-souken.comのトップページにリダイレクトされてアクセス出来ませんでした。
+ akiba-souken.com内の最古の記事のタイムスタンプは2006/12/14なので、akiba.kakaku.comから akiba-souken.com への移行は行われたのかもしれませんが、最初期の記事はInternetArchiveにしか無い模様です
+ その他にもPCパーツのレビュー機能が2014/09/30のリニューアルで削除されていたりします。記事がメインコンテンツではありますが、それ以外のサブコンテンツは既に永久に失われているものがあります。
+ https://mevius.5ch.net/test/read.cgi/esite/1690495133/595 も参照。
+
このアーカイブサイトについて
+ ソースコード、データは以下のレポジトリに一式があります。nodeのnext.jsのSSGでGithubPagesサイトを構築しています。
+ https://github.com/fushihara/akiba-souken-archive
+ このサイトの記事一覧などが欲しい場合は、スクレイピングをするより上記レポジトリからファイルを落としたほうが早いです。
+ スクレイピングに使ったツール一式は以下の通りです。こちらのスクリプトはdenoを使っています。DLしたhtmlをsqliteに保存して、同じ内容で複数アクセスが起きないように工夫しています。
+ xxxxx
+
+ このアーカイブサイトは閉鎖決定時点の全てのコンテンツを網羅している訳ではありません。
+ 通常のarticle形式以外の、投票( https://akiba-souken.com/vote/ )やアニメまとめ( https://akiba-souken.com/anime/matome/ )、アニメランキング( https://akiba-souken.com/anime/ranking/ )は抜けています。
+
+ );
+}
diff --git a/src/app/style.css b/src/app/style.css
new file mode 100644
index 0000000..f8a5c5f
--- /dev/null
+++ b/src/app/style.css
@@ -0,0 +1,3 @@
+h1,h2,h3 {
+ all: revert;
+}
diff --git a/src/app/style.module.css b/src/app/style.module.css
new file mode 100644
index 0000000..fd9a86f
--- /dev/null
+++ b/src/app/style.module.css
@@ -0,0 +1,3 @@
+.a {
+ all: revert;
+}
\ No newline at end of file
diff --git a/src/util/animeLoader.ts b/src/util/animeLoader.ts
new file mode 100644
index 0000000..120123b
--- /dev/null
+++ b/src/util/animeLoader.ts
@@ -0,0 +1,93 @@
+import { readFile } from "fs/promises";
+import { z } from "zod";
+const zodType = z.array(
+ z.strictObject({
+ animeId: z.number().int().min(0),
+ title: z.string(),
+ primaryCategory: z.string(),
+ startSeason: z.string(),
+ titleReviewScore: z.object({
+ story: z.number().min(0).nullable(),
+ sakuga: z.number().min(0).nullable(),
+ character: z.number().min(0).nullable(),
+ music: z.number().min(0).nullable(),
+ originality: z.number().min(0).nullable(),
+ storyboard: z.number().min(0).nullable(),
+ voice: z.number().min(0).nullable(),
+ song: z.number().min(0).nullable(),
+ manzokudo: z.number().min(0).nullable(),
+ }),
+ titleReviewList: z.array(
+ z.object({
+ reviewId: z.number().int(),
+ userIconUrl: z.string().url(),
+ userName: z.string(),
+ score: z.number(),
+ isSpoiler: z.boolean(),
+ timestampSec: z.number().int().min(0),
+ commentCount: z.number().int().min(0),
+ iineCount: z.number().int().min(0)
+ }),
+ ),
+ titleHitokotoList: z.array(
+ z.object({
+ hitokotoId: z.number().int(),
+ userIcon: z.string().url(),
+ userName: z.string(),
+ reviewHtml: z.string(),
+ timestampSec: z.number().int(),
+ })
+ ),
+ episodeList: z.array(z.object({
+ episodeId: z.number().int().min(0),
+ subTitle: z.string(),
+ reviewCount: z.number().int().min(0),
+ score: z.number().nullable(),
+ })),
+ episodeHitokotoList: z.array(z.object({
+ episodeId: z.number().int().min(0),
+ hitokotoList: z.array(z.object({
+ hitokotoId: z.number().int().min(0),
+ userIcon: z.string(),
+ userName: z.string().min(1),
+ reviewHtml: z.string().min(1),
+ timestampSec: z.number().int().min(0),
+ }))
+ })),
+ })
+);
+const MAX_ITEM_LIMIT = process.env["AKIBA_SOUKEN_MAX_ITEM_LIMIT"];
+export type AnimeLoaderData = z.infer