ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English

最新の記事

Astro 4 から 5 へのアップグレード記録

背景 Astro 4 + MDX + Tailwind CSS で構築していたウェブサイトで、pnpm の overrides により astro 5.x が強制的にインストールされる状態になっていました。@astrojs/mdx@2.x(Astro 4 用)と astro 5.x の間で互換性がなく、ビルドが失敗していました。 Package subpath './jsx/renderer.js' is not defined by "exports" in astro/package.json Astro 4 に戻すよりも、Astro 5 へアップグレードする方が合理的と判断し、移行を実施しました。 変更点 1. パッケージの更新 // Before "@astrojs/mdx": "^2.0.0", "astro": "^4.0.3" // After "@astrojs/mdx": "^4.0.0", "astro": "^5.0.0" @astrojs/tailwind と tailwindcss はそのままです。 2. Content Collections の type 指定を削除 Astro 5 では defineCollection の type: "content" が不要になりました。 // Before const news = defineCollection({ type: "content", schema, }); // After const news = defineCollection({ schema, }); 3. entry.slug から entry.id への変更 Astro 5 では Content Collections の slug プロパティが廃止され、id に統一されました。 ...

Chrome Headless の --screenshot で画面下部に白い余白が出る問題と対処法

Chrome Headless の --screenshot で画面下部に白い余白が出る問題と対処法

問題 Chrome の Headless モードで HTML を PNG 画像にキャプチャする際、画面下部に**白い余白(白帯)**が発生することがあります。 google-chrome --headless --screenshot=output.png \ --window-size=1920,1080 \ --hide-scrollbars \ --force-device-scale-factor=1 \ file:///path/to/slide.html HTML 側で width: 1920px; height: 1080px を指定しているにもかかわらず、生成された画像の下部に白い帯が残り、bottom で配置した要素(テロップ、フッターなど)が切れてしまいます。 原因 --window-size=1920,1080 はブラウザの外部ウィンドウサイズを指定するオプションであり、実際の**ビューポート(描画領域)**は若干小さくなります。Headless モードでもこのズレは発生します。 つまり: --window-size=1920,1080 → 実際のビューポートは約 1920×1058 程度 HTML は 1080px の高さで描画しようとする ビューポートに収まらない下部のコンテンツが見切れる スクリーンショットは 1920×1080 で出力されるが、下部はデフォルト背景色(白)で埋まる html や body に height: 1080px を指定しても、Chrome が実際に確保するビューポート高さとは一致しないため、CSS だけでは解決できません。 解決方法 ウィンドウサイズを大きめに設定し、スクリーンショット後に Pillow でクロップするのが最も確実です。 1. Chrome のウィンドウサイズを大きくする google-chrome --headless --screenshot=output.png \ --window-size=1920,1280 \ --hide-scrollbars \ --force-device-scale-factor=1 \ file:///path/to/slide.html 高さを 1280 に変更することで、1080px のコンテンツが確実にビューポート内に収まります。 ...

TEI/XMLサイトをVercelで高速デプロイ:XSLT変換をsaxon-jsで自動化する

TEI/XMLサイトをVercelで高速デプロイ:XSLT変換をsaxon-jsで自動化する

はじめに TEI (Text Encoding Initiative) に準拠したXMLデータをXSLTでHTMLに変換し、Webで公開する構成は、デジタルヒューマニティーズの分野で広く使われています。 従来、ブラウザのクライアントサイドXSLT変換(<?xml-stylesheet?>やJavaScriptによるXSLTProcessor)でXMLを表示するケースが多いですが、この方式にはいくつかの課題があります。 ページを開くたびにブラウザがXSLT変換を実行するため、表示が遅い SEO・クローラー対応が難しい ブラウザごとのXSLT実装差異 本記事では、Vercelのビルド時にXML→HTML変換を自動実行し、生成済みHTMLを静的配信する方法を紹介します。 構成 project/ ├── docs/ # Vercelの出力ディレクトリ │ ├── index.html # トップページ │ └── data/ │ ├── main.xsl # XSLTスタイルシート │ ├── main.sef.json # コンパイル済みスタイルシート │ ├── source.xml # TEI/XMLソース │ └── source.html # 生成されるHTML(ビルド時生成) ├── build.js # ビルドスクリプト ├── package.json └── vercel.json Node.jsでのXSLT処理ライブラリ比較 Vercelのビルド環境ではネイティブツール(xsltproc等)が使えないため、Node.jsのXSLTライブラリを使う必要があります。以下の3つを検証しました。 xsltproc(参考:ローカル環境) macOSに標準搭載されているC言語実装のXSLTプロセッサです。 xsltproc docs/data/main.xsl docs/data/source.xml > docs/data/source.html 一瞬で完了しますが、Vercelのビルド環境では利用できません(apt-getが使えない)。 xslt-processor(純JavaScript実装) npm install xslt-processor GoogleのAJAXSLT(2005年)をベースにES2015+に更新したライブラリです。ブラウザにXSLTサポートがなかった時代のpolyfillが起源であり、1400行程度のXMLファイルの変換でも数分以上かかり、実用に堪えませんでした。 遅い理由は以下の通りです。 XPath式を実行時に毎回パース・評価する(キャッシュや事前コンパイルの仕組みがない) 最適化戦略がない設計のため、XPath評価が累積的に重くなる 純JavaScriptのDOM実装による木構造走査のオーバーヘッド saxon-js(Saxonica社製) npm install saxon-js xslt3 XSLT 3.0仕様の編集者であるMichael Kayが率いるSaxonica社が開発した高性能XSLTプロセッサです。最大の特徴は事前コンパイル方式にあります。 ...

Three.js + Puppeteer で VRM キャラクターを動かして動画を自動生成する

Three.js + Puppeteer で VRM キャラクターを動かして動画を自動生成する

はじめに 技術ブログの記事を VTuber 風の解説動画に自動変換できたら面白いのでは――そんな思いつきから、Three.js + Puppeteer で VRM キャラクターをフレーム単位でレンダリングし、VOICEVOX の音声とリップシンクさせて動画を生成するパイプラインを作りました。 この記事では、実装で得られた知見とハマりどころを共有します。 全体のパイプライン 処理の流れは以下の通りです。 Markdown 記事を読み込み → LLM(OpenRouter API)でセクション分割された台本を生成 VOICEVOX でセクションごとに音声(WAV)と音素タイミングを生成 Three.js + @pixiv/three-vrm でヘッドレス Chrome 上に VRM モデルを描画し、音素データに基づくリップシンクアニメーションをフレーム連番 PNG として出力 スライド画像を自動生成(HTML → Chrome ヘッドレス → PNG) FFmpeg でスライド背景 + VRM アニメーション + 音声を合成し、MP4 動画を出力 Python スクリプトがオーケストレーション役を担い、VRM レンダリングは Node.js スクリプトを子プロセスとして呼び出す構成です。 使用技術 役割 技術 3D レンダリング Three.js v0.172 VRM 読み込み @pixiv/three-vrm v3.3.3 ヘッドレスブラウザ puppeteer-core (SwiftShader) 音声合成 VOICEVOX Engine (Docker) 動画合成 FFmpeg パイプライン制御 Python VRM モデル AvatarSample_C (VRoid Hub / 無料ライセンス) ヘッドレス Chrome で VRM を読み込む 課題: file:// の CORS 制限 最初の壁は、ヘッドレス Chrome 上で VRM ファイルを読み込む方法でした。ローカルの .vrm ファイルを file:// プロトコルで読もうとすると CORS エラーで弾かれます。 ...

DH週間トピックス — 2026年3月第2週

DH週間トピックス — 2026年3月第2週

デジタル人文学(DH)関連の新規ツール開発・公開情報を週次でまとめています。 今週は該当するトピックはありませんでした。 本記事は X投稿・GitHub更新・カレントアウェアネス・ポータルから自動収集した情報を基に生成しています。

DTS (Distributed Text Services) 1.0 正式リリースへの対応 ― TEI/XMLテキストAPIの仕様更新記録

はじめに 2026年2月、テキストコレクションへの標準的なアクセス手段を提供するAPI仕様「Distributed Text Services (DTS)」のv1.0が正式にリリースされました。 本記事では、DTS 1-alpha に基づいて構築していた校異源氏物語テキストDB用APIを、DTS 1.0に対応させた際の変更点を整理します。 https://github.com/distributed-text-services/specifications/releases/tag/v1.0 DTS とは DTS は、TEI/XMLなどのテキストコレクションに対する標準的なアクセスAPIを定める仕様です。以下の4つのエンドポイントで構成されます。 エンドポイント 役割 Entry Point APIの各エンドポイントURLを返す Collection テキスト間のナビゲーション(コレクション・リソースの一覧) Navigation テキスト内のナビゲーション(引用構造の探索) Document テキスト本体の取得(TEI/XMLの全体または一部) 対象プロジェクト 校異源氏物語テキストDBのDTS API実装(TypeScript/Express.js)です。 本番: https://dts-typescript.vercel.app/api/v2/dts ソースコード: https://github.com/nakamura196/dts-typescript 1-alpha から 1.0 への主な変更点 1. JSON-LD Context URLの変更 最も基本的な変更は、JSON-LDコンテキストファイルのURLです。 - "@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json" + "@context": "https://dtsapi.org/context/v1.0.json" ドメインが distributed-text-services.github.io から dtsapi.org に変わりました。これは仕様の正式公開に伴い、永続的なURLが割り当てられたことを意味します。 2. dtsVersion の更新 - "dtsVersion": "1-alpha" + "dtsVersion": "1.0" 全エンドポイントのレスポンスに含まれる dtsVersion フィールドを更新します。 3. URI Template の全パラメータ記載 1-alphaでは一部のパラメータのみ記載していましたが、1.0ではAPIが受け付ける全パラメータをURI templateに含める必要があります。 Entry Point のレスポンス例: ...

DTS Viewer の改善 ― 複数 Citation Tree 対応・階層ナビゲーション・XML ブラウザ表示

DTS Viewer の改善 ― 複数 Citation Tree 対応・階層ナビゲーション・XML ブラウザ表示

はじめに 前回の記事で、校異源氏物語テキストDB用 DTS API を 1.0 仕様に対応させ、和歌(短歌)の Citation Tree を追加しました。 本記事では、そのAPIを利用するビューアアプリ「DTS Viewer」側で行った改善について紹介します。 主な改善点は以下の3つです。 複数 Citation Tree への対応 ― tree パラメータの正しい受け渡し ナビゲーション結果の階層表示 ― カード形式からテーブル形式への変更 XML のブラウザ内表示 ― mediaType パラメータの活用 1. 複数 Citation Tree への対応 問題 DTS 1.0 では、1つのリソースに複数の Citation Tree を定義できます。校異源氏物語では「ページ/行」と「和歌」の2つのツリーを持っています。 しかし、ビューアのナビゲーションリンクが tree パラメータを付与していなかったため、和歌ツリーのリンクをクリックしてもデフォルト(ページ/行)のナビゲーションが表示される問題がありました。 対応 Citation Tree の identifier を追跡し、ナビゲーション URL に &tree=${identifier} を付与するようにしました。 const getNavigationUrl = (navigation: string, url: string, level: number, tree?: string) => { navigation = decodeURIComponent(navigation); navigation = removeVars(navigation) + `&down=${level}`; if (tree) { navigation += `&tree=${tree}`; } const combined = getDomain(url) + navigation; return encodeURIComponent(combined); }; ツリー構造の視覚化 各 Citation Tree をツリー形式で表示し、description ラベルで区別できるようにしました。 リソース詳細ページ。「ページ・行による引用構造」と「和歌(短歌)による引用構造」が視覚的に分かれて表示されている。 ...

歴史資料をAIで検索できるRAGアプリを作った技術スタック

歴史資料をAIで検索できるRAGアプリを作った技術スタック

はじめに ある研究プロジェクトの成果報告書(全10巻)を対象に、自然言語で質問すると関連資料を検索し、出典付きで回答してくれる RAG(Retrieval-Augmented Generation)アプリケーションを開発しました。 本記事では、このアプリの技術スタックと設計上の判断について紹介します。 アーキテクチャ全体像 ユーザー ↓ 質問 Next.js (App Router) ↓ API Route クエリ補完 (LLM) ↓ 補完された検索クエリ Embedding生成 (text-embedding-3-small) ↓ ベクトル Pinecone (ベクトル検索, topK=8) ↓ 関連チャンク LLM (Claude Sonnet) ← システムプロンプト + コンテキスト ↓ SSEストリーミング ユーザーに回答表示 フロントエンド Next.js 16 + React 19 + TypeScript App Router を採用し、ページ構成はシンプルに3ページです。 パス 内容 / ランディングページ(質問例へのリンク付き) /chat チャットUI /about サイト概要 チャットページでは useSearchParams を使い、ランディングページの質問例をクリックすると /chat?q=... でそのまま質問が送信される仕組みにしています。 Tailwind CSS v4 スタイリングには Tailwind CSS v4 を使用。v4 では @import "tailwindcss" だけで設定が完了するため、tailwind.config.js が不要になりました。 ...

App Store Connect APIだけでiOSアプリを審査提出する手順

App Store Connect APIだけでiOSアプリを審査提出する手順

TL;DR App Store Connect の REST API を使い、コマンドラインからiOSアプリの審査提出に必要なほぼ全作業(メタデータ・スクリーンショット・年齢レーティング・ビルド紐付け・URL設定・暗号化コンプライアンス・価格設定)を完了させた。本記事ではその手順を再現可能な形で記載する。 注意: 「App Privacy(アプリのプライバシー)」のデータ使用状況の宣言だけは、2026年3月時点でAPIが提供されておらず、App Store Connectのブラウザから設定する必要がある。 前提条件 Apple Developer Program に登録済み App Store Connect で API キーを発行済み アプリの Bundle ID が登録済み Xcode でアーカイブ・アップロード済みのビルドが存在する(xcodebuild -exportArchive でアップロード可能) Python 3 + PyJWT + cryptography がインストール済み pip install PyJWT cryptography 1. API キーの準備 1.1 API キーの発行 App Store Connect → ユーザーとアクセス → 統合 → App Store Connect API から新しいキーを発行する。 名前: 任意(例: deploy-key) アクセス: Admin(メタデータ更新・提出に必要) 発行後、以下の情報をメモする: 項目 説明 Key ID APIキーの識別子(10文字程度の英数字) Issuer ID 組織の識別子(UUID形式) ダウンロードした .p8 ファイルは安全な場所に保存する(一度しかダウンロードできない): ...

DOCX → TEI/XML 変換ツールに CETEIcean を使ったプレビュー機能を追加した

DOCX → TEI/XML 変換ツールに CETEIcean を使ったプレビュー機能を追加した

はじめに 以前の記事で、DOCX → TEI/XML 変換ツールを紹介しました。TEI Garage API を使ってブラウザだけで Word 文書を TEI/XML に変換できるツールです。 公開後、利用者の方から「変換されたタグが想定どおりに機能するかを視覚的に確認したい」というフィードバックをいただきました。XML のシンタックスハイライト表示だけでは、見出し・注釈・リスト・テーブルなどが実際にどのようにレンダリングされるか確認が難しいためです。 そこで、CETEIcean を使った TEI プレビュー機能を追加しました。 デモサイト: https://tei-converter.pages.dev/ CETEIcean とは CETEIcean は、TEI Consortium が開発している JavaScript ライブラリです。TEI/XML を HTML5 カスタム要素(Custom Elements) に変換し、ブラウザ上でそのまま表示できます。 通常、TEI/XML をブラウザで表示するには XSLT で HTML に変換する必要がありますが、CETEIcean は Web Components の仕組みを利用して、TEI の要素名をそのままカスタム要素として登録します。例えば <p> は <tei-p> に、<head> は <tei-head> に変換されます。 これにより、CSS だけで TEI 要素のスタイルを定義でき、サーバサイドの変換処理が不要になります。 追加した機能 XML / プレビュー タブ切り替え 変換結果の表示エリアに 「XML」と「プレビュー」の2つのタブを追加しました。 XML タブ: 従来どおりのシンタックスハイライト付き XML 表示 プレビュータブ: CETEIcean によるレンダリング結果 タブをクリックするだけで切り替えられます。 ...

XSLT処理を5倍高速化:Saxon-JSからSaxon-HEへの移行

まとめ TEI XML → HTML変換において、npx xslt3(Saxon-JS)からJava Saxon-HEへ切り替えたところ、ビルド時間が1分48秒から23秒に短縮された(約5倍の高速化)。 背景 校異源氏物語テキストDBは、源氏物語のデジタルエディションで、54巻分のTEI XMLファイルを持つ。ビルドスクリプト(Python)が各XMLをHTMLに変換するため、npx xslt3を54回呼び出していた。 python3 scripts/prebuild.py xsl # 全54巻のXSLT処理 この処理がビルドパイプライン全体で最も時間のかかるステップだった。 ベンチマーク ファイルごとの比較 巻 文字数 npx xslt3 (JS) saxon (Java) 高速化率 01 桐壺 11,240 1.8秒 1.1秒 1.6倍 34 若菜上 46,230 4.9秒 0.4秒 12倍 ファイルが大きいほど改善幅が大きい。JVM起動コスト(約1秒)を差し引くと、実際の変換処理は桁違いに速い。 合計(全54巻) npx xslt3 (Saxon-JS): 1分48秒 saxon (Saxon-HE): 23秒 移行手順 ローカル環境(macOS) brew install saxon ビルドスクリプト SAXON_JAR環境変数 → saxonコマンド → npx xslt3の順にフォールバックするヘルパーを追加した。 def xslt_cmd(xsl, src, dst): """Return XSLT command, preferring Saxon-HE over npx xslt3.""" saxon_jar = os.environ.get('SAXON_JAR') if saxon_jar: return ['java', '-jar', saxon_jar, f'-xsl:{xsl}', f'-s:{src}', f'-o:{dst}'] if shutil.which('saxon'): return ['saxon', f'-xsl:{xsl}', f'-s:{src}', f'-o:{dst}'] return ['npx', 'xslt3', f'-xsl:{xsl}', f'-s:{src}', f'-o:{dst}'] GitHub Actions Node.js + xslt3をJava + Saxon-HE jarに置き換えた。 ...

ethers.jsのエラーメッセージを多言語化する「ethers-i18n」を作った

ethers.jsのエラーメッセージを多言語化する「ethers-i18n」を作った

はじめに ethers.jsを使ったdApp開発で、こんな経験はありませんか? Error: insufficient funds for intrinsic transaction cost エラーの意味は分かるけれど、このメッセージをそのままエンドユーザーに見せるわけにはいきません。特に日本語圏のユーザー向けサービスでは、エラーメッセージの日本語化は避けて通れない課題です。 そこで、ethers.jsのエラーメッセージを多言語化するライブラリ ethers-i18n を作りました。 https://github.com/nakamura196/ethers-i18n ethers-i18nとは ethers-i18nは、ethers.js v6のエラーコードに対応した翻訳メッセージを提供するi18nプラグインです。 主な特徴: シンプルなAPI — setLocale("ja") と t("ERROR_CODE") だけで使える 非破壊的 — 既存のethers.jsコードに影響を与えない 型安全 — TypeScriptの型定義を完備 4言語対応 — 英語・日本語・韓国語・中国語をサポート 軽量 — 依存関係はethers.jsのみ(peerDependency) インストール npm install @nakamura196/ethers-i18n ethers.js v6がpeerDependencyとして必要です。 基本的な使い方 ロケールの設定と翻訳 import { setLocale, t, getLocale, getSupportedLocales } from "@nakamura196/ethers-i18n"; // サポートされているロケール一覧 console.log(getSupportedLocales()); // ["en", "ja", "ko", "zh"] // 日本語に設定 setLocale("ja"); console.log(getLocale()); // "ja" // エラーコードを翻訳 console.log(t("INVALID_ARGUMENT")); // "無効な引数です" console.log(t("INSUFFICIENT_FUNDS")); // "トランザクションに必要な残高が不足しています" console.log(t("NETWORK_ERROR")); // "ネットワークエラーが発生しました" エラーオブジェクトの翻訳 ethers.jsが投げるエラーオブジェクトをそのまま翻訳できます。 ...

IIIF絵巻物をAI動画で動かす:Video Annotationという新しいアプローチ

IIIF絵巻物をAI動画で動かす:Video Annotationという新しいアプローチ

はじめに IIIF(International Image Interoperability Framework)は、デジタルアーカイブの画像を相互運用可能な形で公開するための国際標準です。世界中の図書館・博物館が採用しており、高解像度画像の深層ズームや、異なる機関のコレクションを横断的に閲覧することを可能にしています。 本記事では、IIIF画像の一部領域にAI生成動画を重ねて表示する「IIIF Animated Viewer」を開発した過程を紹介します。題材は東京大学が公開する「百鬼夜行図」――妖怪たちの行列を描いた絵巻物です。 静止画の妖怪たちが、ゆらゆらと動き出す。そんな体験を、IIIF標準の枠組みの中で実現しました。 狙い 1. 絵巻物に「動き」を与える 絵巻物は本来、巻きながら読む動的なメディアです。右から左へ進む行列、風にはためく衣、揺れる炎――静止画でありながら動きを内包しています。AI動画生成でその潜在的な動きを顕在化させることで、作品の新しい鑑賞体験を提供できるのではないか、という着想がありました。 2. IIIF標準に準拠する 独自フォーマットではなく、IIIF Presentation API 3.0のマニフェストとして動画情報を記述します。これにより、他のIIIFビューアとの互換性を保ちつつ、既存のIIIFエコシステムに乗る形で動画アノテーションを提供できます。 3. 汎用的なパイプラインにする 百鬼夜行図だけでなく、他のIIIF資料にも適用可能な仕組みを目指しました。マニフェストのメタデータと全体画像からコンテキストを自動生成し、個別領域のプロンプト生成に反映するアーキテクチャにしています。 デモ デモは GitHub Pages で公開しています。 コレクション一覧ページ。IIIFコレクションからマニフェスト一覧を読み込み、サムネイル付きで表示する。 ビューア全体表示。百鬼夜行図の全体像がOpenSeadragonで表示され、下部に再生ボタンがある。 ズームすると個々の妖怪が確認でき、動画オーバーレイが元画像の上に重なって表示される。 システム構成 docs/ … GitHub Pages公開ディレクトリ index.html … コレクション一覧ページ viewer.html … OpenSeadragon + 動画オーバーレイビューア collection.json … IIIF Collection manifest.json … IIIF Manifest(動画アノテーション含む) runs/run_002/ … 生成動画ファイル scripts/ pipeline.py … 一括生成パイプライン process_raw.py … Veo生成動画のトリミング+manifest更新 workspace/ anno.json … 領域アノテーション(19件) context.txt … 自動生成コンテキスト runs/ … 中間データ(クロップ画像、プロンプト等) IIIFマニフェストでの動画アノテーション 従来のIIIFアノテーション IIIF Presentation API 3.0では、Canvasに対するアノテーションとして画像を配置します。 ...

DH週間トピックス — 2026年3月第1週

DH週間トピックス — 2026年3月第1週

デジタル人文学(DH)関連の新規ツール開発・公開情報を週次でまとめています。 今週は該当するトピックはありませんでした。 本記事は X投稿・GitHub更新・カレントアウェアネス・ポータルから自動収集した情報を基に生成しています。

TEI Garage APIを使って、DOCX → TEI/XML 変換ツールをブラウザだけで作った

TEI Garage APIを使って、DOCX → TEI/XML 変換ツールをブラウザだけで作った

はじめに TEI (Text Encoding Initiative) は、人文学分野のテキストをデジタルで構造化するための国際標準です。図書館・博物館・学術研究などで利用されていますが、TEI/XML を直接書くにはマークアップの知識が必要で、導入のハードルが高いのが実情です。 そこで活用されるのが、Microsoft Word (.docx) から TEI/XML への変換ツールです。代表的なものに TEI Garage(旧 OxGarage)がありますが、多機能ゆえに UI がやや複雑です。今回、DOCX → TEI/XML の変換に特化した、シンプルなブラウザベースのツールを作成しました。 https://github.com/nakamura196/tei-converter デモサイト: https://tei-converter.pages.dev/ 仕組み TEI Garage は REST API を公開しており、以下のエンドポイントに DOCX ファイルを POST するだけで TEI/XML が返ってきます。 POST https://teigarage.tei-c.org/ege-webservice/Conversions/ docx:application:vnd.openxmlformats-officedocument.wordprocessingml.document/ TEI:text:xml/ 本ツールはこの API を呼び出すフロントエンドです。変換処理自体は TEI Consortium が運営するサーバ上で行われます。 主な機能 ドラッグ & ドロップ .docx ファイルをブラウザにドラッグ & ドロップするだけでアップロードできます。クリックしてファイルを選択することも可能です。 整形済み XML プレビュー 変換結果はインデント付きで整形され、シンタックスハイライト付きで表示されます。タグ名、属性名、属性値がそれぞれ色分けされるため、構造を把握しやすくなっています。 コピー & ダウンロード 結果をワンクリックでクリップボードにコピーしたり、.xml ファイルとしてダウンロードしたりできます。 サンプル DOCX 内蔵 「サンプル .docx で試す」をクリックするだけで、内蔵のサンプルファイルで動作を確認できます。サンプルのダウンロードも可能です。 ...

はてなブログの記事を一括で非公開にする方法(AtomPub API)

はてなブログの記事を一括で非公開にする方法(AtomPub API)

はてなブログの記事を別サイトに移行した後、旧記事を一括で非公開にしたいケースがあります。 注意点:下書きには戻せない はてなブログのAtomPub APIでは、公開済みの記事を下書き(draft)に戻すことはできません。PUTリクエストで <app:draft>yes</app:draft> を送ると 400 Cannot Change into Draft エラーになります。 そのため、以下の2つの方法があります。 方法1:記事本文を「移転しました」に書き換える AtomPub APIのPUTで記事の <content> を書き換えることは可能です。 import requests import xml.etree.ElementTree as ET import time HATENA_ID = "your_hatena_id" BLOG_ID = "your_blog_id.hatenablog.com" API_KEY = "your_api_key" NEW_SITE_URL = "https://your-new-site.com" ATOM_NS = "http://www.w3.org/2005/Atom" def fetch_all_entries(): entries = [] url = f"https://blog.hatena.ne.jp/{HATENA_ID}/{BLOG_ID}/atom/entry" while url: resp = requests.get(url, auth=(HATENA_ID, API_KEY), timeout=30) resp.raise_for_status() root = ET.fromstring(resp.text) for entry in root.findall(f"{{{ATOM_NS}}}entry"): title_el = entry.find(f"{{{ATOM_NS}}}title") title = title_el.text or "" if title_el is not None else "" edit_link = entry.find(f"{{{ATOM_NS}}}link[@rel='edit']") edit_url = edit_link.get("href") if edit_link is not None else None if edit_url: entries.append({"title": title, "edit_url": edit_url}) next_el = root.find(f"{{{ATOM_NS}}}link[@rel='next']") url = next_el.get("href") if next_el is not None else None return entries def replace_content(entry): title = entry["title"] update_xml = f"""<?xml version="1.0" encoding="utf-8"?> <entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app"> <title>{title}</title> <content type="text/plain">この記事は {NEW_SITE_URL} に移転しました。</content> </entry>""" resp = requests.put( entry["edit_url"], auth=(HATENA_ID, API_KEY), data=update_xml.encode("utf-8"), headers={"Content-Type": "application/atom+xml; charset=utf-8"}, timeout=30, ) return resp.status_code entries = fetch_all_entries() print(f"Found {len(entries)} entries") for i, e in enumerate(entries): status = replace_content(e) print(f"[{i+1}/{len(entries)}] {status}: {e['title'][:50]}") time.sleep(0.5) 方法2:はてなブログの管理画面から一括削除 記事数が少なければ、管理画面の「記事の管理」から手動で削除する方法もあります。ただし一括選択機能がないため、大量の記事には向きません。 ...

Hypothes.is APIでWebアノテーションをエクスポートしてTEI/XMLに変換する

Hypothes.is APIでWebアノテーションをエクスポートしてTEI/XMLに変換する

はじめに Hypothes.isは、Webページ上にハイライトやコメントを付けられるオープンソースのアノテーションツールです。ブラウザ拡張やJavaScriptの埋め込みで手軽に使えますが、蓄積したアノテーションをバックアップしたい、あるいはTEI/XMLなど別の形式で活用したいケースもあります。 本記事では、Hypothes.is APIを使ってアノテーションをエクスポートし、TEI/XMLに変換する方法を紹介します。 APIキーの取得 Hypothes.isにログイン Developer settings にアクセス 「Generate your API token」でAPIキーを生成 取得したキーを.envファイルに保存します。 cp .env.example .env # .env を編集してAPIキーを設定 HYPOTHESIS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx アノテーションのエクスポート APIの基本 Hypothes.is APIのベースURLは https://api.hypothes.is/api です。認証はAuthorization: Bearer <API_KEY>ヘッダーで行います。 主要なエンドポイント: エンドポイント 用途 GET /api/profile 認証ユーザーのプロフィール取得 GET /api/search アノテーション検索 GET /api/annotations/{id} 個別アノテーション取得 スクリプト エクスポートからTEI/XML変換までを1つのスクリプト hypothes_export.py にまとめています。 https://github.com/nakamura196/hypothes-export/blob/main/hypothes_export.py 以下、主要な処理を抜粋して説明します。 .envの読み込みとAPI呼び出し def load_env(): env_path = Path(__file__).parent / ".env" with open(env_path) as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: k, v = line.split("=", 1) os.environ[k.strip()] = v.strip() def api_get(endpoint, params=None): api_key = os.environ["HYPOTHESIS_API_KEY"] url = f"https://api.hypothes.is/api/{endpoint}" if params: url += "?" + urllib.parse.urlencode(params) req = urllib.request.Request(url) req.add_header("Authorization", f"Bearer {api_key}") with urllib.request.urlopen(req) as resp: return json.loads(resp.read().decode()) 全アノテーションの取得(ページネーション対応) Search APIは1リクエストあたり最大200件なので、offsetをずらして全件取得します。 ...

OpenITI mARkdownからTEI XMLへの自動変換ツール「oitei」を試す

OpenITI mARkdownからTEI XMLへの自動変換ツール「oitei」を試す

はじめに イスラーム圏の歴史テキストを扱う OpenITI(Open Islamicate Texts Initiative) プロジェクトでは、TEI/XMLの代わりに mARkdown という軽量記法でテキストをタグ付けできます。 TEI/XMLは構造化の国際規格として強力ですが、特にアラビア語のような右から左に書く言語(RTL)では、XMLタグとの混在でエディタ上の表示が乱れるという問題があります。mARkdownはこの課題を解決する記法です。 本記事では、mARkdownで書かれたテキストを TEI XMLに自動変換 するPythonツール oitei を実際に動かしてみます。 oiteiとは OpenITI mARkdown → TEI XML の変換ライブラリ(Python) OpenITI TEI Schema に準拠したXMLを出力 PyPIで公開されており pip install で導入可能 依存ライブラリ: oimdp(mARkdownパーサー)、lxml https://github.com/OpenITI/oitei インストール pip install oitei Python 3.8以上が必要です。oimdp(OpenITI mARkdown Parser)と lxml が依存関係として自動インストールされます。 OpenITI mARkdownの記法 mARkdownファイルは以下の3部構成です。 マジックバリュー (1行目): ######OpenITI# メタデータ : #META# で始まる行 本文 : #META#Header#End# の後に記述 主なタグ 記法 意味 `### ` `### ### $ 伝記エントリ # 段落の開始 @P02 名前 人物名(後続2語を含む) @T11 地名 地名(後続1語を含む) @YB732 誕生年(ヒジュラ暦732年) @YD808 没年(ヒジュラ暦808年) %~% 詩行(hemistich)の区切り 固有表現タグ(@P, @T 等)の後ろの 2桁の数字 は、1桁目がエンティティ番号、2桁目が「後続する何単語を名前に含むか」を指定します。例えば @P02 Ibn Khaldun は「後続2語(Ibn Khaldun)を人名として含む」という意味です。 ...

GakuNin RDM APIをNode.jsで操作する — プロジェクト作成からGitHub+Vercel自動デプロイまで

GakuNin RDM APIをNode.jsで操作する — プロジェクト作成からGitHub+Vercel自動デプロイまで

はじめに GakuNin RDMは、国立情報学研究所(NII)が提供する研究データ管理プラットフォームです。Open Science Framework(OSF)をベースに構築されており、APIを通じてプロジェクトの操作を自動化できます。 本記事では、Node.jsからGakuNin RDM APIを使って以下の操作を行う方法を紹介します。 プロジェクトの作成・設定 Wikiの作成・更新 メンバーの追加 GitHub連携 + Vercelによる自動デプロイ 事前準備 パーソナルアクセストークンの取得 GakuNin RDMにログイン 設定 > パーソナルアクセストークンに移動 新しいトークンを作成(スコープ:osf.full_read、osf.full_write) プロジェクトの初期化 mkdir rdm && cd rdm npm init -y npm install dotenv .envファイルにトークンを保存します。 GRDM_TOKEN=your_personal_access_token_here .gitignoreも作成しておきます。 .env node_modules/ APIクライアントの作成 GakuNin RDMのAPIはJSON:API形式を採用しています。まず汎用的なクライアントをlib/client.jsとして作成します。 lib/client.js require("dotenv").config(); const BASE_URL = "https://api.rdm.nii.ac.jp/v2"; const TOKEN = process.env.GRDM_TOKEN; if (!TOKEN) { throw new Error("GRDM_TOKEN is not set in .env"); } const headers = { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/vnd.api+json", }; async function request(method, path, body) { const url = `${BASE_URL}${path}`; const options = { method, headers }; if (body) { options.body = JSON.stringify(body); } const res = await fetch(url, options); if (!res.ok) { const text = await res.text(); throw new Error(`${method} ${path} failed (${res.status}): ${text}`); } if (res.status === 204) return null; return res.json(); } module.exports = { request, headers, BASE_URL }; ポイント: ...

ethers.js v6 の日本語チュートリアルを作った

ethers.js v6 の日本語チュートリアルを作った

はじめに Ethereum の JavaScript ライブラリである ethers.js の日本語チュートリアルを作成しました。 https://github.com/nakamura196/ethers-ja-tutorial VitePress で静的サイトとしても公開しています。 https://nakamura196.github.io/ethers-ja-tutorial/ 作った背景 ethers.js は Ethereum 開発において最も広く使われているライブラリの一つです。v6 に関する日本語記事は v5 からのマイグレーション解説が中心で、初心者がゼロから学べる体系的なチュートリアルは見当たりませんでした。公式ドキュメントも英語のみのため、日本語で基礎から順を追って学べるチュートリアルを作成しました。 チュートリアルの内容 全 8 章構成で、基礎から実践的な内容までカバーしています。 章 テーマ 内容 1 環境構築 Node.js と ethers.js のインストール 2 プロバイダー Ethereum ネットワークへの接続 3 ウォレット ウォレットの作成と管理 4 ブロックチェーンの読み取り 残高確認・ブロック情報の取得 5 トランザクション送信 ETH の送金 6 スマートコントラクト コントラクトとのやり取り 7 イベント イベントのリスニング 8 ユーティリティ 単位変換・ハッシュなどの便利機能 特徴 すぐに動くサンプルコード付き examples/ ディレクトリに実行可能なサンプルコードを用意しています。 git clone https://github.com/nakamura196/ethers-ja-tutorial.git cd ethers-ja-tutorial npm install node examples/01-connect.mjs たとえば 01-connect.mjs を実行すると、Ethereum メインネットの最新ブロック番号やガス価格が取得できます。 ...