青空文庫のコンテンツをローカルにダウンロードして、少し編集して、縦でも読めるようにして自分のレンタルサーバーにアップロードします。
青空文庫にある xhtml ファイルを手動でダウンロードしていろいろ操作するのはできるのですが、コンピュータを使って一括で実行します。
青空文庫の xthml ファイルを一括ダウンロードするのは簡単ではありません。
例えば芥川竜之介のファイルは「https://www.aozora.gr.jp/cards/000879」にあることがすぐわかるのですが、 いわばファイルリストがわかりません。
chatGPT はとても賢いのですが、こういうことはできないようで、あれこれ提示してくれましたが、例によって全然駄目なので自分で考えることにしました。
芥川竜之介の作品一覧のページのソースを見ると、
<li><a href="../cards/000879/card4872.html">愛読書の印象</a> (新字旧仮名、作品ID:4872) </li><li><a href="../cards/000879/card16.html">秋</a> (新字旧仮名、作品ID:16) </li><li><a href="../cards/000879/card178.html">芥川竜之介歌集</a> (新字旧仮名、作品ID:178) </li><li><a href="../cards/000879/card15.html">アグニの神</a> (新字旧仮名、作品ID:15) </li><li><a href="../cards/000879/card43014.html">アグニの神</a> (新字新仮名、作品ID:43014) </li><li><a href="../cards/000879/card3804.html">悪魔</a> (新字旧仮名、作品ID:3804) </li>
となっているので、これがわかれば card*.html のリストを作ることができます。
そうして、それぞれのページにアクセスすると、
<td><a href="./files/14_14602.html">14_14602.html</a></td>
となっているので、それぞれをダウンロードすることができます。
そのままダウンロードすると、文字コードが「shift-jis」になってしまうので、それを「utf-8」に変更する必要があります。
普通にダウンロードすると、html 文書の始めの方は以下のようになっています。
<?xml version="1.0" encoding="Shift_JIS"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" ><head> <meta http-equiv="Content-Type" content="text/html;charset=Shift_JIS" /> <meta http-equiv="content-style-type" content="text/css" /> <link rel="stylesheet" type="text/css" href="../../default.css" />
これではまずいので、python で以下のように一括変換します。
<!DOCTYPE html><html lang="ja"><head><meta http-equiv="content-type" content="text/html; charset=UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta http-equiv="content-style-type" content="text/css" /><link rel="stylesheet" type="text/css" href="../../viewer/css/style.css" />
import osimport reimport requestsfrom bs4 import BeautifulSoup
class AozoraCardFetcher: def __init__(self): # 設定(ここで一括定義) self.base_url = "https://www.aozora.gr.jp" self.author_id = "1833" self.author_name = "鈴木大拙" self.author_page = f"{self.base_url}/index_pages/person{self.author_id}.html" self.card_urls = []
sanitized_name = re.sub(r'[\\/*?:"<>|]', '_', self.author_name) self.download_folder = f"./downloads/{sanitized_name}" if not os.path.isdir(self.download_folder): os.makedirs(self.download_folder)
# 起動時にすべて実行 self.fetch_cards() for card_url in self.card_urls: print(f"[INFO] {card_url} から .html ファイルを取得中...") html_files = self.fetch_html_links_from_card(card_url) self.download_and_convert_html_files(html_files)
def fetch_cards(self): res = requests.get(self.author_page) soup = BeautifulSoup(res.content, "html.parser") for li in soup.select("ol > li > a"): href = li.get("href") if href and "cards" in href and href.endswith(".html"): full_url = self.base_url + href.replace("..", "") self.card_urls.append(full_url)
def fetch_html_links_from_card(self, card_url): res = requests.get(card_url) soup = BeautifulSoup(res.content, "html.parser") table = soup.find("table", class_="download") if not table: return [] links = [] for a in table.find_all("a"): href = a.get("href") if href and href.endswith(".html"): links.append(href.replace("./files/", "")) return links
def clean_html_header(self, content): content = re.sub(r'<\?xml[^>]*?\?>', '', content) content = re.sub(r'<!DOCTYPE[^>]*?>', '<!DOCTYPE html>', content) content = re.sub(r'<html[^>]*?xml:lang="ja"[^>]*?>', '<html lang="ja">', content) content = content.replace( '<meta http-equiv="Content-Type" content="text/html;charset=Shift_JIS" />', '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />\n<meta name="viewport" content="width=device-width, initial-scale=1.0" />' ) content = content.replace( '<link rel="stylesheet" type="text/css" href="../../default.css" />', '<link rel="stylesheet" type="text/css" href="../../viewer/css/style.css" />' ) content = content.replace( '<link rel="stylesheet" type="text/css" href="../../aozora.css" />', '<link rel="stylesheet" type="text/css" href="../../viewer/css/style.css" />' ) content = re.sub(r'(<meta[^>]*?)(?<!/)>', r'\1 />', content) content = re.sub(r'</meta>', '', content) content = re.sub(r'(<link[^>]*?)(?<!/)>', r'\1 />', content) content = re.sub(r'</link>', '', content) content = re.sub(r'^\s*\n', '', content, flags=re.MULTILINE) return content
def extract_title(self, html_content): soup = BeautifulSoup(html_content, "html.parser") title_tag = soup.find("title") if title_tag: full_title = title_tag.text.strip() cleaned_title = re.sub(f'^{re.escape(self.author_name)}\\s*', '', full_title) return cleaned_title return "unknown_title"
def sanitize_filename(self, title): return re.sub(r'[\\/*?:"<>|]', '_', title)
def download_and_convert_html_files(self, html_files): for file_name in html_files: download_url = f"{self.base_url}/cards/{int(self.author_id):06}/files/{file_name}"
try: response = requests.get(download_url) response.encoding = 'shift_jis' original_html = response.text converted_html = self.clean_html_header(original_html) title = self.extract_title(original_html) safe_title = self.sanitize_filename(title) output_path = os.path.join(self.download_folder, f"{safe_title}.html")
with open(output_path, "w", encoding="utf-8") as f: f.write(converted_html) print(f"✅ {safe_title}.html を保存しました。")
except requests.exceptions.RequestException as e: print(f"⚠️ ダウンロード失敗: {file_name} - {e}")
if __name__ == "__main__": AozoraCardFetcher()
最初の方の init で作者名とその id を設定すれば、すべての html ファイルがダウンロードされます。
漢字混じりのタイトルをソートしても思い通りに並んではくれません。
芥川竜之介の作品は 360 ほどあるので、本棚から見たい作品を探すのに時間がかかってしまいます。
python は漢字の読みがなを取得できるようです。
それによって json を作成します。
import MeCabimport jsonimport os
# MeCabの設定def get_yomi(text): tagger = MeCab.Tagger("-Ochasen -r /etc/mecabrc") node = tagger.parseToNode(text) yomi = []
while node: features = node.feature.split(",") if features[0] == "名詞" and len(features) > 7: yomi.append(features[7]) # 音読みや訓読みを取得 node = node.next
return ''.join(yomi) # 全部の読みを結合して返す
# ディレクトリ内のファイルタイトルを取得して読みを生成def generate_yomi_for_directory(directory): works_with_yomi = []
# ディレクトリ内のファイルを取得 for filename in os.listdir(directory): if filename.endswith(".html"): title = os.path.splitext(filename)[0] # 拡張子を取り除く yomi = get_yomi(title) works_with_yomi.append({'title': title, 'yomi': yomi})
return works_with_yomi
# 結果をJSONに保存def save_to_json(data, filename='names.json'): with open(filename, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=4)
# 使用例directory = '/home/mituo/aozora/shelf/芥川竜之介' # 本棚のディレクトリパスworks_with_yomi = generate_yomi_for_directory(directory)save_to_json(works_with_yomi)
でもこれは完全ではないようで、
[ { "title": "早春", "yomi": "ソウシュン" }, { "title": "母", "yomi": "ハハ" }, { "title": "素戔嗚尊", "yomi": "モトミコト" }, { "title": "杜子春", "yomi": "モリコハル" },
杜子春をモリコハルと読んでます。
でも9割型はきちんと読んでいるので、それを編集することにしました。
以下のような php プログラムで編集します。
<?php// JSONファイルを読み込むfunction load_json($filename) { return json_decode(file_get_contents($filename), true);}
function save_json($data, $filename) { $result = file_put_contents($filename, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); if ($result === false) { echo "<p style='color:red;'>⚠️ 保存に失敗しました!ファイルパスまたはパーミッションを確認してください。</p>"; } else { echo "<p>✅ <strong>" . $filename . "</strong> に保存しました。</p>"; }}
// 読みを変更する関数function edit_yomi(&$data, $title, $new_yomi) { foreach ($data as &$entry) { if ($entry['title'] === $title) { $entry['yomi'] = $new_yomi; } }}
// 使用例
$data = load_json('names.json');
// 一括編集処理if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['yomi'])) { foreach ($_POST['yomi'] as $title => $new_yomi) { edit_yomi($data, $title, $new_yomi); // 各タイトルごとに読みを更新 } save_json($data, 'names_edited.json'); // 更新されたデータをJSONファイルに保存 echo "<p>すべての読みが更新されました。</p>";}
// 一覧表示echo '<h1>タイトル一覧</h1>';echo '<form method="POST" action="">';echo '<ul>';foreach ($data as $item) { echo "<li><span style='display:inline-block; width:300px;'>{$item['title']}</span> - <input type='text' name='yomi[{$item['title']}]' value='{$item['yomi']}' placeholder='新しい読み'> </li>";}echo '</ul>';
echo '<input type="submit" value="一括編集">';echo '</form>';?>
このようにして、芥川竜之介と夏目漱石の作品合わせて 470 ほどをサーバー上にアップしました。
https://kunokatura-library.site/xthml ファイルを普通にダウンロードすると、作品の前に作家名が付いてきます。 それではおかしいので上のプログラムでは作者名を削除するようになっているのですが、それとは別に古い名前の方が付いてくることがあります。
例えば、「柳田国男」の作品の一部には「柳田國男」という作家名が付いてくることがあります。それもやはり削除したいので、シェルスクリプトを作成しました。
#!/bin/bash
TARGET_DIR="柳田国男"
for file in "$TARGET_DIR"/*; do new_name=$(echo "$file" | sed 's/柳田國男//g') if [[ "$file" != "$new_name" ]]; then mv "$file" "$new_name" echo "ファイル名を変更しました: $file → $new_name" fidone