SEO対策:画像のalt属性をハンドで書き込むスクリプト、Markdown版
<!-- markdown-mode-on -->
## **概要**
[SEO対策:画像のalt属性をハンドで書き込むスクリプト](https://newprivatelibrary.blogspot.com/2025/05/seoalt.html)を作ったのですが、これはHTML版です。そこで、同じ考え方で、自分がよく使うMarkdownで書いた記事にも適用できるものを作りました。
---
このスクリプト `find_md__no_alt.py` は、Markdown形式の画像挿入記述 `` のうち、**altテキスト(代替テキスト)が空またはないもの**に対して、**見出し(h2〜h5)の内容をもとにaltを提案・挿入し、修正可能にしつつ、最終的に `output.html` を書き出す**ものです。
<a name="more"></a>
## 🔧 スクリプトの主な目的
1. **input.html を読み込む**
2. `` 形式の画像挿入記述から alt が空のもの(``)を抽出
3. その直前にある最も近い見出し(`##`〜`#####`)を altテキスト の候補とする
4. 同じ alt テキストが複数回出た場合には `(2)`, `(3)` などを自動付与
5. altテキスト をユーザーに提示し、修正も可能(編集プロンプト付き)
6. 画像URLをクリップボードにコピー
7. `output.html` に更新された内容を保存
---
## 🧠 スクリプト内部の動作詳細
### 1. `extract_headings_and_images()`
* 入力ファイルの各行を走査し、
* 見出し(`##`〜`#####`)を検出してリストに記録
* `` の画像を検出してリストに記録
```python
heading_match = re.match(r'^(#{2,5})\s*(.+)', line.strip())
image_match = re.search(r'!\[\s*\]\(([^)]+)\)', line)
```
---
### 2. `find_nearest_heading()`
* 各画像の「直前にある最至近の見出し」を探して返します。
* 最も近い上方向の見出しだけを見る(hレベルは考慮しない)
---
### 3. `process_file()`
1. `input.html` を読み込んで行単位で処理。
2. 画像ごとに altテキスト の候補を決定。
3. **同じ altテキストが再利用される場合は `(2)`, `(3)`…と自動で付け足す。**
4. `input_with_prefill()` を使って、ユーザーに alt 候補を編集可能に提示。
5. `pyperclip.copy()` で URL をクリップボードにコピー。
6. 該当行の `` を `` に置換。
7. 変更後の全行を `output.html` に保存。
---
### 4. `input_with_prefill()`
* ターミナルでユーザーに altテキストを入力させるとき、
* 初期値として候補を入力済みの状態で表示。
* ユーザーはそのまま Enter で確定か、編集して決定可能。
---
## 📤 出力の例
```text
[Image 1]
src: https://example.com/image.png
alt: 回路図 (2)
```
---
## 💡 注意点・特徴
| 機能 | 説明 |
| ---------- | -------------------------------------- |
| Markdown対応 | `` のみ対象。HTMLの `<img/>` タグは無視。 |
| 見出し階層 | `##`〜`#####`(h2〜h5)に対応。 |
| 編集可能 | altテキスト 候補は編集プロンプト付きで確認・修正できる。 |
| URLコピー | 各画像URLは自動でクリップボードにコピーされる。 |
| 重複防止 | 同じaltテキストが複数回使われると自動で `(2)`, `(3)`…の番号がつく。 |
---
## pythonスクリプト
<pre style="max-height: 30em;"><code class="language-python">
import re
import pyperclip
try:
import readline # macOS / Linux
except ImportError:
import pyreadline as readline # Windows
INPUT_FILE = "input.html"
OUTPUT_FILE = "output.html"
YELLOW = "\033[93m"
CYAN = "\033[96m"
GREEN = "\033[92m"
RESET = "\033[0m"
def input_with_prefill(prompt, text):
def hook():
readline.insert_text(text)
readline.redisplay()
readline.set_pre_input_hook(hook)
try:
return input(prompt)
finally:
readline.set_pre_input_hook()
def extract_headings_and_images(lines):
headings = []
images = []
for idx, line in enumerate(lines):
heading_match = re.match(r'^(#{2,5})\s*(.+)', line.strip())
image_match = re.search(r'!\[\s*\]\(([^)]+)\)', line)
if heading_match:
level = len(heading_match.group(1))
text = heading_match.group(2).strip()
headings.append({'line': idx, 'level': level, 'text': text})
if image_match:
url = image_match.group(1).strip()
images.append({'line': idx, 'url': url})
return headings, images
def find_nearest_heading(headings, image_line):
valid = [h for h in headings if h['line'] < image_line]
if not valid:
return ""
return valid[-1]['text']
def process_file(input_path, output_path):
try:
with open(input_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
except FileNotFoundError:
print(f"エラー:ファイル '{input_path}' が見つかりません。")
return
headings, images = extract_headings_and_images(lines)
updated_lines = lines[:]
count = 0
alt_counter = {}
for idx, img in enumerate(images):
nearest_heading = find_nearest_heading(headings, img['line'])
base_alt = nearest_heading.strip()
# サフィックス処理
suffix = ""
key = base_alt
alt_counter[key] = alt_counter.get(key, 0) + 1
if alt_counter[key] > 1:
suffix = f" ({alt_counter[key]})"
suggested_alt = base_alt + suffix
print(f"\n{YELLOW}[Image {idx + 1}]{RESET}")
print(f" src: {CYAN}{img['url']}{RESET}")
print(f"{GREEN} alt:{RESET}", end="")
user_input = input_with_prefill(" ", suggested_alt)
final_alt = user_input.strip() or suggested_alt
pyperclip.copy(img['url'])
original_line = lines[img['line']]
new_line = re.sub(r'!\[\s*\]\(([^)]+)\)', f'', original_line)
updated_lines[img['line']] = new_line
count += 1
with open(output_path, 'w', encoding='utf-8') as f:
f.writelines(updated_lines)
print(f"\n{count} 件の画像に alt を設定しました。出力先: '{output_path}'")
if __name__ == "__main__":
process_file(INPUT_FILE, OUTPUT_FILE)
</code></pre>
以下は、`find_md_no_alt.py` に合わせて書き換えた **使い方説明** です:
---
## **このスクリプトの使い方:**
このスクリプトは、`input.html` を読み込み、Markdown形式の画像挿入記述 `` のうち、`alt` テキストが空または省略されているものを一つずつ検出します。その `URL` を表示してクリップボードにコピーし、画像の直前にある見出し(`##`〜`#####`)をもとに `alt` テキストを自動生成します。ユーザーは提案された `alt` テキストをそのまま使うか、自由に編集できます。すべての画像が処理されると、結果が `output.html` に書き出されます。
---
### 📄 操作手順:
1. 上記のPythonコードを `find_md_no_alt.py` という名前で保存します。
2. 処理対象のHTMLファイルを `input.html` という名前で、`find_md_no_alt.py` と同じディレクトリに置きます。
3. ターミナル(またはコマンドプロンプト)を開き、`find_md_no_alt.py` があるディレクトリに移動します。
4. 以下のコマンドを実行します。
```bash
python find_md_no_alt.py
```
5. スクリプトは `input.html` を読み込み、`` 形式のうち `alt` テキストが空またはないものを検出します。
6. 各画像について、直前にある最も近い見出しを使って `alt` の候補が提示されます(例:`[Image 1]` の形式で表示)。
7. 同じ `alt` テキストが複数回出る場合には、自動的に `(2)`, `(3)` などのサフィックスが付与されます。
8. 提示された `alt` テキストは、初期値として編集プロンプトに入力された状態で表示されます。Enterで確定、または内容を編集可能です。
9. 各画像の `URL` は自動でクリップボードにコピーされます。
10. すべての画像の `` は `` に変換され、結果が `output.html` に保存されます。
---
### ⚠️ 注意点:
* 対象は `` 形式のみです。HTMLの `<img/>` タグは対象外です。
* 見出しは `##`(h2)から `#####`(h5)までが対象で、それぞれの画像に最も近い見出しが使われます。
* 重複する `alt` テキストには自動で番号が追加されます(例:`回路図`, `回路図 (2)`, `回路図 (3)`)。
* `pyperclip` ライブラリがインストールされていないと、クリップボードへのコピーは動作しません。
* 非常に大きなHTMLファイルでは処理に時間がかかる場合があります。
---
このスクリプトが、Markdown画像の `alt` テキストを効率的に追加する作業の助けとなることを願っています。
## 関連リンク