技術ブログにおけるタグの自動生成は、一貫性を保つための強力な武器です。しかし、その核心部となる「キーワードの抽出」において、正規表現の \b(単語境界)に頼り切ると、思わぬ「誤爆」や「検出漏れ」に直面します。
特に日本語と英単語が分かち書きされずに隣接するケースでの、判定ロジックの改善記録を残します。
1. 従来の課題:\b は万能ではない
当初、キーワード判定には以下のようなシンプルな正規表現を使用していました。
# 従来のコード
pattern = rf'\b{re.escape(kw)}\b'
このコードは、英語のみの文章であれば完璧に動作します。
This is SEO.-> マッチUse SEOPY library.-> マッチしない(誤判定を防げる)
しかし、日本語が混在する文章では、以下のケースで期待通りにマッチしません。
SEO対策を徹底する(漢字との隣接) -> マッチしないこれはSEOブログだ(カタカナとの隣接) -> マッチしない
これは、Python 3の正規表現において \w がデフォルトでUnicode文字(漢字・ひらがな・カタカナを含む)を単語構成文字として扱うためです。SEO(英字)と対策(漢字)の間に「単語と非単語の境界」が存在しないとみなされ、\b が成立しなくなってしまうのです。
2. 解決策:否定の先読み・後読み
解決策は、\b という曖昧な境界フラグを捨て、「前後に英数字が存在しないこと」を明示的に指定することです。
具体的には、以下の正規表現パターンを採用しました。
# 改善されたコード:否定の戻り読みと先読み(Negative Lookbehind/Lookahead)
pattern = rf'(?<![a-zA-Z0-9]){re.escape(kw)}(?![a-zA-Z0-9])'
このパターンの利点
-
英単語としての独立性を維持:
(?![a-zA-Z0-9])という否定の先読みを使うことで、SEOを探しているときにSEOPYという別の単語の一部としてマッチしてしまうのを確実に防ぎます。 -
日本語との隣接を許容: 日本語の文字(例:「対」や「設」)は
[a-zA-Z0-9]には含まれません。そのため、SEO対策のように英単語と日本語が密着していても、「隣にあるのは英数字ではない」と判断され、正しくキーワードとして抽出されます。
3. 実装のポイント
tagger.py では、キーワードがASCII文字(技術用語)の場合のみこの厳密な判定を適用し、日本語のキーワードの場合は単純なエスケープのみを行うように分岐させています。
if kw.isalnum() and kw.isascii():
# ASCII英数字のキーワードのみ、単語の独立性をチェック
pattern = rf'(?<![a-zA-Z0-9]){re.escape(kw)}(?![a-zA-Z0-9])'
else:
# 日本語キーワードなどはそのままマッチさせる
pattern = re.escape(kw)
結論
正規表現の \b は非常に便利ですが、日本語環境においてはその「単語」の定義が仇となることがあります。
特に技術ブログのように英単語と日本語が頻繁に混ざり合うドキュメントを扱う場合、「何が境界なのか」を文字クラスで定義し、先読み・後読みで制御するアプローチが、最も堅牢で期待に近い判定ロジックとなります。