Webフォントのサブセットアプリの作り方

当サイトでは、ページ上で使用するWebフォントを抜き出し、サブセット化して使用していますが、フォントのサイズ容量が抑制されるため、サイトの表示がはやくなるメリットがあります。

このPythonを使用してパソコン上でサブセット化する方法については、こちらのページの下部をご参照ください。

上記では手動でサブセット化していましたが、Pythonを使用して半自動化することができます。

当サイトで作成したサブセットアプリは以下のようなものですが、ボタンを押すだけで、サイト内の.htmlページから必要なテキストのみを抽出し、自動でサブセット化してWebフォントを作成することができます。

サブセットアプリ

ボタンを押すだけで、アウトプット用のフォルダにWebフォントを自動で生成することができます。

サブセット化されたWebフォント

woff2の方は圧縮されており、当サイトのテキスト容量ですと、概ね100KB前後で収まっており、実写の写真1枚分程度の容量のため、この辺りですと許容範囲に収まるはずです。

必要なものは、元となるWebフォントのファイルですが、GoogleのNotoフォントがおすすめです。また、Pythonやライブラリも必要ですが、無料で利用できるため、費用をかけずに自サイト専用のサブセットアプリを作ることができます。

PythonでWebフォントのサブセットを自動化する方法

サブセットを自動化する際、サイト内の個別ファイル名を利用して、それをそのまま.woff2形式のフォントファイルに対応させて変換するのが簡単です。

つまり、サイト内の「ファイル名」.htmlから、Pythonで自動でテキストを抽出して 「ファイル名」.txtに保存し、さらにPythonのfonttoolsでサブセット化して「ファイル名」.woff2のWebフォントを生成します。

例えば、このページでは「subset-app.html」のファイル名のため、コンテンツ内のテキストのみを抽出して「subset-app.txt」の形式で保存します。そして、その.txtファイルを元に「subset-app.woff2」の形でWebフォントを生成しています。

サブセット化する際には、Noto_Sans_JPなどの元となるフォントのほか、抽出するテキストのリスト、完成したwoffファイルの出力先フォルダなどが必要になります。加えて、太字の部分はWeightの大きいフォントを使用するため、別途にサブセット化します。

単純化しますと、以下のようなフォルダを用意すると便利です。(※フォルダ名は任意)

用意するフォルダ

woff、woff2の出力先
output → 完成フォントの出力先フォルダ

必要な材料
example.com → 「.html」形式のホームページファイル
Noto_Sans_JP → サブセット元のフォント

標準フォント用
reg-txt → 標準フォントのテキスト抽出用
reg-subset → 標準フォントのサブセット用

太字用
bold-txt → 太字のテキスト抽出用
bold-subset → 太字のサブセット用

スタイル設定用
style → ページ内<style>の設定用

上記のうち、Pythonは無料で利用できるため、自分で用意する必要があるのはサブセット元のフォントのみです。

このページでは、Google FontsのNotoフォントを例にサブセット化する手順をご紹介していますが、元となるフォントを持っていない場合は、こちらの「Download family」の箇所から入手するとよいでしょう。

Google Fonts公式ページ

フォルダごと解凍すると、「Noto_Sans_JP」フォルダの「static」フォルダ内に以下のフォントがありますが、このページのサンプルではBold(Weight 700)とRegular(Weight 400)を使用してサブセット化しています。

Notoフォント

加えて、Pythonも必要になりますが、インストールしておきます。

Python公式ページ

PythonのPATH設定

インストールする際は、「Add python.exe to PATH」のオプションを選択するとコマンドブロンプトからPythonを直接実行できるようになります。

このページでのコードは簡略化した形で記載していますが、テキストの抽出を<main>タグ内のみに限定したい場合など、より複雑なコードについては、ChatGPTなどで修正してもらうとよいでしょう。

サブセット化するテキストの抽出方法

ここでは標準的なウェイトのフォントのみを説明しますが、まずは上記で作成した「reg-text」フォルダ内にて、メモ帳などで.pyファイルを作成します。

ファイル名は任意ですが、reg-text.pyなどで作成します。

reg-text.py

この.pyファイルを実行すると、フォルダ内に.txtファイルが生成されるようにしますが、必要な情報はexample.comのホームページファイルのパスと出力先フォルダのパスです。

例えば、デスクトップの場合は以下のようになります。

base_dir = 'C:/Users/ユーザー名/Desktop/example.com/'
output_dir = 'C:/Users/ユーザー名/Desktop/reg-text/'

次に、以下のようなPythonのコードを書き、この.pyファイルにコピペで貼り付け、reg-text.pyなどのファイル名で保存します。ユーザー名やサイト名は適宜書き換えてください。

reg-text.pyのサンプル

import os
from bs4 import BeautifulSoup

# 基本となるパスを定義します
base_dir = 'C:/Users/ユーザー名/Desktop/example.com/'
output_dir = 'C:/Users/ユーザー名/Desktop/reg-text/'

for root, dirs, files in os.walk(base_dir):
    for file in files:
        if file.endswith('.html'):

            # HTMLファイルを開き、ページ全体のテキストを取り出します(utf-8が前提)
            with open(os.path.join(root, file), 'r', encoding='utf-8') as html_file:
                soup = BeautifulSoup(html_file, 'html.parser')
                all_text = soup.get_text()

                # テキストファイルのファイル名を定義します
                if file == "index.html":
                    if root == base_dir:  # 最上位の開始ディレクトリの場合
                        txt_file_name = 'index.txt'
                    else:  # 下層ディレクトリの場合
                        dir_name = os.path.basename(root)
                        txt_file_name = f'{dir_name}-index.txt'
                else:
                    txt_file_name = os.path.splitext(file)[0] + '.txt'

                txt_file_path = os.path.join(output_dir, txt_file_name)

                # 取り出したテキストを個別のテキストファイルに書き込みます(utf-8が前提)
                with open(txt_file_path, 'w', encoding='utf-8') as txt_file:
                    txt_file.write(all_text)コピーボタンコピーチェックボタン

サイトの文字コードはUTF-8を想定しています。もしShift JISだった場合、元のコンテンツをUTF-8へ変換する必要が出てきますが、変換に失敗するリスクもあるため、必ずバックアップを取った上でUTF-8への変換を検討してください。

また、Pythonはインデントも関係ありますので、列を乱さないようにお願いします。

このスクリプトを実行するには、PythonのBeautifulSoupというライブラリが必要になります。

まだインストールしていない場合は以下のコマンドでインストールできます。

pip install beautifulsoup4

作成したreg-text.pyは、コマンドブロンプトにて、cdコマンドで.pyファイルのあるフォルダまで移動し、「python ファイル名.py」で実行できます。あるいは、環境によってはダブルクリックでも実行できるかもしれません。

pythonの実行

例えば、「cd Desktop」などと入力すると、デスクトップに移動できます。逆に「cd ..」などと入力すると、1つ上のフォルダに移動できます。

「python ファイル名.py」で実行すると、以下のようにexample.com内の.htmlファイルからテキストのみを抽出し、.txt形式のファイルが作成されます。下層ディレクトリまで含めて作成されますが、同一のファイル名がないことを前提としています。

ただし、index.htmlページについては、開始フォルダのルートディレクトリのみ「index.txt」とし、下層フォルダについては「ディレクトリ名-index.txt」となるようにしています。

ほぼ一瞬でファイルが作成されるはずですので、適切にテキストのみが抽出されているか、ファイルの中身を確認しておきます。

テキストのみを抽出した.txtファイル

同じくbold用のフォントも必要にはなりますが、太字については特殊なため、まずは標準フォントの生成を完了させるとよいでしょう。

Python fonttoolsのインストールとPathの通し方

上記の「reg-text」のフォルダ内に、「ファイル名」.txtが入っているはずですが、このテキストファイルに記載されている文字リストを元に、Pythonで元のフォントから抜き出してサブセット化します。

このサブセット化をする際、事前準備としてfonttoolsとbrotliが必要になりますが、Windowsのコマンドブロンプトなどからインストールしておきます。

pip install fonttools brotli

このfonttoolsパッケージにはpyftsubset.exeが含まれており、このpyftsubset.exeがサブセット化する際のメインツールになります。Brotliについてはデータを圧縮するアルゴリズムですが、woff2を出力するのに必要なので、こちらも入れておきます。

サブセット化をする際には、元となるフォント、抜き出すテキストのリスト、出力する場所を指定した上で、このpyftsubset.exeを実行します。

当サイト運営者の場合、このpyftsubset.exeがインストールされている場所のフルパスは以下のようになっていました。

pyftsubsetのフルパス

"C:/Users/あなたのユーザー名/AppData/Local/Packages/PythonSoftwareFoundation.Python.バージョン名_識別子/LocalCache/local-packages/Python39/Scripts/pyftsubset.exe"

上記はWindowsからダウンロードしたもので、下は公式サイトからダウンロードした場所になります。

"C:\Users\あなたのユーザー名\AppData\Local\Programs\Python\Python311\Scripts\pyftsubset.exe"

Windowsからダウンロードする場合と公式サイトから直接ダウンロードする場合とでは、インストールされる場所に違いがあります。

一般的には、以下の「Script」のフォルダ内にpyftsubset.exeが入っていると思います。

PythonのPATH

同じ場所とは限りませんが、見当たらない場合はこの辺りを辿ってみることをおすすめします。

サブセット化をする際、コマンドブロンプトでこのフルパスを直接指定しても実行できますが、このフルパスは長すぎるので使いづらいです。そのため、この「Script」のフォルダでPATHを通しておき、「pyftsubset ~」の形でショートカット的に実行できるようにしておきます。

まず、PATHが通っているかどうかを確認するには、コマンドブロンプトから以下のように、適当なコマンドを実行します。

pyftsubset --help

オプションなどが適切に表示されるようでしたら、既にPATHが通っていることになります。この場合は特に何も設定する必要はありません。

一方で、PATHが通ってなく、エラーが出る場合、以下のようにPATHを通す必要があります。

PATHの通し方

まず、上記の画像のように右クリックして、フルパスのアドレスをコピーしておきます。

次に、Windowsのスタートメニューで「環境変数」を検索し、「システム環境変数の編集」を選択します。システムのプロパティが表示されるので、「詳細設定」→「環境変数」をクリックします。

表示された環境変数の箇所にて、「Path」を選択し、編集をクリックします。表示された編集画面にて「新規」をクリックしたのち、コピペしたフルパスを貼り付け、OKを押して閉じます。

パスの設定

既にコマンドブロンプトを開いている場合は、一旦終了したのち、再度、コマンドブロンプトを起動して上記のpyftsubset --helpで確認します。

公式サイトからダウンロードする際、PATHを通す項目にチェックしていたら、この環境変数は自動で設定されているはずです。

以下のサンプルコードですが、上記のPATHが通っていることが前提で記載しています。もし何等かの事情でPATHが通っていない場合、pyftsubset_path = "pyftsubset"の箇所に、pyftsubset_path = "フルパス"の形で入力しても実行することができます。

抽出したテキストを元にpyftsubsetでサブセット化

手動でサブセット化する際、コマンドブロンプトから以下のように実行すれば、woff2ファイルを作成することができます。

pyftsubset "元のフォントファイル" --text-file="抽出する.txtファイル" --flavor=woff2 --output-file="出力ファイル名"

けれども、このように手動で出力するのは面倒なため、以下のコードにて、元となるフォント、抜き出すテキスト、出力する場所を指定した上で実行できます。

例えば、以下のように記載することで、上記のコマンドを自動化することができます。

command_woff2 = f'{pyftsubset_path} "{font_path}" --text-file="{text_path}" --flavor=woff2 --output-file="{output_path_woff2}"'

以下のサンプルについては、アウトプット用の「reg-subset」フォルダ内にreg-subset.pyとして作成してください。ユーザー名となっているパスなども適宜修正してください。

reg-subset.py

import os
import subprocess

# 元のフォントのパス
font_path = "C:/Users/ユーザー名/Desktop/Noto_Sans_JP/static/NotoSansJP-Regular.ttf"

# pyftsubsetのパス(PATHが通っていることが前提)
pyftsubset_path = "pyftsubset"

# .txtファイルがあるディレクトリ
dir_path = "C:/Users/ユーザー名/Desktop/reg-text"

# ディレクトリ内の各.txtファイルに対して処理
for filename in os.listdir(dir_path):
    if filename.endswith(".txt"):
        # テキストファイルのフルパス
        text_path = os.path.join(dir_path, filename)
        
        # 出力フォントファイルの名前(.txtを.woffと.woff2に置換)
        output_filename_woff = filename.replace(".txt", ".woff")
        output_filename_woff2 = filename.replace(".txt", ".woff2")
        
        # 出力フォントのフルパス
        output_path_woff = "C:/Users/ユーザー名/Desktop/output/" + output_filename_woff
        output_path_woff2 = "C:/Users/ユーザー名/Desktop/output/" + output_filename_woff2

        # pyftsubsetのコマンド
        command_woff = f'{pyftsubset_path} "{font_path}" --text-file="{text_path}" --flavor=woff --output-file="{output_path_woff}"'
        command_woff2 = f'{pyftsubset_path} "{font_path}" --text-file="{text_path}" --flavor=woff2 --output-file="{output_path_woff2}"'

        # サブプロセスを実行して結果を取得(エラーがあれば例外を送出)
        subprocess.run(command_woff, shell=True, check=True)
        subprocess.run(command_woff2, shell=True, check=True)コピーボタンコピーチェックボタン

このファイルを実行すると、以下のように、出力先の「output」フォルダにサブセット化されたWebフォントが生成されます。

生成されたWebフォント

そちらの完成品を確認したのち、実際のホームページファイルのあるフォルダにコピペすることをおすすめしますが、直接example.com内のfontフォルダに出力しても良いと思います。

このwoff2フォントの生成には時間がかかるかもしれませんが、この時間がかかる作業をパソコン上で事前にやっておくことで訪問者の時間を節約することができます。時間がかかればかかるほど高速化のメリットは大きいため、気長に待つとよいでしょう。

このpyftsubsetのオプションについては、コマンドブロンプトで「pyftsubset --help」を入力すれば、オプションの一覧が表示されます。

pyftsubsetのオプション一覧

点々の箇所が該当しますが、--text-file=については、抽出した.txtテキストのファイルを指定します。他にも--flavor=woff2と--flavor=woffの2種類を出力することをおすすめします。それ以外のオプションについては、--layout-features='*'などもありますが、特に見かけ上の違いはないため、使用しなくてもよいと思います。

太字フォントのテキスト抽出とサブセット方法

ページ内でh1やh2、<b>などで囲っている太字については、Weightの大きいフォントを指定する必要があります。

この太字フォントのサブセット化についても、上記の標準フォントと同じように対応できますが、既存ページ内から太字の部分のみをピックアップしてテキストを抽出する必要があります。

そのため、まずはCSSで太字となる箇所のクラス属性などを全てピックアップしておくとよいでしょう。

以下のコードでクラス属性などを指定することで、太字部分のみを抽出できます。

例えば、h4も太字にしている場合は、tags = ['h1', 'h2', 'h3', 'h4']などとし、赤字.redのclass属性を太字にしている場合にはclasses_and_ids = ['red']などとします。下の例の場合、id属性とclass属性がhogehogeとexampleの箇所が太字としてピックアップされます。

任意ですが、「bold-text」フォルダ内にbold-text.pyなど、適当なファイル名をつけておきます。

bold-text.py

import os
from bs4 import BeautifulSoup

# 基本となるパスを定義します
base_dir = 'C:/Users/ユーザー名/Desktop/example.com/'
output_dir = 'C:/Users/ユーザー名/Desktop/bold-text/'

# 太字に関連するタグとクラス、IDをリストに格納します
tags = ['h1', 'h2', 'h3', 'a', 'th', 'strong', 'b', 'yourbold']
classes_and_ids = ['hogehoge', 'example']

# 基本パス内のすべてのHTMLファイルを探索します
for root, dirs, files in os.walk(base_dir):
    for file in files:
        if file.endswith('.html'):

            # HTMLファイルを開き、指定されたタグとクラス、IDのテキストを取り出します
            with open(os.path.join(root, file), 'r', encoding='utf-8') as html_file:
                soup = BeautifulSoup(html_file, 'html.parser')

                bold_text = ''

                # 指定したタグのテキストを抽出します
                for tag in tags:
                    elements = soup.find_all(tag)
                    for element in elements:
                        bold_text += element.get_text() + '\n'

                # 指定したクラスとIDのテキストを抽出します
                for class_or_id in classes_and_ids:
                    # classとして検索
                    elements = soup.find_all(class_=class_or_id)
                    for element in elements:
                        bold_text += element.get_text() + '\n'

                    # IDとして検索
                    element = soup.find(id=class_or_id)
                    if element:
                        bold_text += element.get_text() + '\n'

                # 取り出したテキストを個別のテキストファイルに書き込みます
                if bold_text:
                    if file == "index.html":
                        if root == base_dir:  # 最上位の開始ディレクトリの場合
                            txt_file_name = 'index.txt'
                        else:  # 下層ディレクトリの場合
                            dir_name = os.path.basename(root)
                            txt_file_name = f'{dir_name}-index.txt'
                    else:
                        txt_file_name = os.path.splitext(file)[0] + '.txt'
                    
                    txt_file_path = os.path.join(output_dir, txt_file_name)

                    with open(txt_file_path, 'w', encoding='utf-8') as txt_file:
                        txt_file.write(bold_text)コピーボタンコピーチェックボタン

このファイルを実行すると、上記の標準フォントと同様に「ファイル名」.txtがbold-textフォルダ内に生成されているはずです。けれども、中身は太字のテキストの箇所のみのため、テキストの文字数自体は少なくなっているはずです。

次に、上記で抽出した.txtファイルから、太字用のWebフォントをサブセット化します。

以下のようなコードにて、サブセットすることができますが、生成するフォントは「ファイル名」-bold.woff2の形式にして、標準フォントと区別します。

output_filename_woff2 = filename.replace(".txt", "-bold.woff2")

つまり、「ファイル名.txt」を「ファイル名-blod.woff2」に置換している形になります。

また、元となるフォントから、weightの大きい太字用のフォントファイルを指定します。

以下がサンプルコードになりますが、「bold-subset」フォルダ内にbold-subset.pyなどとして保存します。

bold-subset.py

import os
import subprocess

# 元のフォントのパス
font_path = "C:/Users/ユーザー名/Desktop/Noto_Sans_JP/static/NotoSansJP-Bold.ttf"

# pyftsubsetのパス
pyftsubset_path = "pyftsubset"

# .txtファイルがあるディレクトリ
dir_path = "C:/Users/ユーザー名/Desktop/bold-text/"

# ディレクトリ内の各.txtファイルに対して処理
for filename in os.listdir(dir_path):
    if filename.endswith(".txt"):
        # テキストファイルのフルパス
        text_path = os.path.join(dir_path, filename)

        # 出力フォントファイルの名前(.txtを取り除き、"-bold.woff"と"-bold.woff2"を追加)
        output_filename_woff = filename.replace(".txt", "-bold.woff")
        output_filename_woff2 = filename.replace(".txt", "-bold.woff2")
        
        # 出力フォントのフルパス
        output_path_woff = "C:/Users/ユーザー名/Desktop/output/" + output_filename_woff
        output_path_woff2 = "C:/Users/ユーザー名/Desktop/output/" + output_filename_woff2

        # pyftsubsetのコマンド
        command_woff = f'{pyftsubset_path} "{font_path}" --text-file="{text_path}" --flavor=woff --output-file="{output_path_woff}"'
        command_woff2 = f'{pyftsubset_path} "{font_path}" --text-file="{text_path}" --flavor=woff2 --output-file="{output_path_woff2}"'

        # サブプロセスを実行
        subprocess.run(command_woff, shell=True)
        subprocess.run(command_woff2, shell=True)コピーボタンコピーチェックボタン

フォントの出力先は、「output」フォルダで全て1つに統一しています。

上記にて、標準フォントと太字フォント、ページ内のCSS設定をすれば、サイト内のサブセットは全て完了するはずです。

新しく作成する新規ページについては、「ファイル名.txt」のページ出力は一瞬で終了するので、新規ページの.txtのみを残し、それ以外を削除した上でサブセット化すれば簡単かと思います。

サブセット化したWebフォントをページに設定する方法

サブセット化したWebフォントをサーバーにアップロードする際、まずはパソコン上のホームページファイル内に任意の名前で「fonts」などの専用フォルダを用意しておきます。

上記の場合ですとexample.comフォルダ内になりますが、そちらに「fonts」などのフォルダを作成し、「output」フォルダで生成されたWebフォントをコピペで貼り付け、フォルダごとサーバー上にアップロードします。

直接、example.com内の「fonts」フォルダ内に出力してしまいますと、間違って出力した際に煩雑になってしまうため、サブセット化するためのフォルダとホームページファイルは分離しておくことをおすすめします。

次に、ホームページファイルの内部CSSにて、<style></style>にてWebフォントを設定します。

例えば、このページでは以下のように指定していますが、font-family: "Noto Sans CJK JP"のフォントファイルを、サーバーにアップロードしたsubset-app.woff2で指定しています。

<style>
@font-face {font-family: "Noto Sans JP";font-style: normal;font-weight: 400;
src: url(/fonts/subset-app.woff2) format("woff2"),url(/fonts/subset-app.woff) format("woff");font-display: swap;}

@font-face {font-family: "Noto Sans JP";font-style: normal;font-weight: 700;
src: url(/fonts/subset-app-bold.woff2) format("woff2"),url(/fonts/subset-app-bold.woff) format("woff");font-display: swap;}

main {font-family:"Noto Sans JP",sans-serif;font-weight: 400;}
h1,h2,b,.aka {font-family:"Noto Sans JP",sans-serif;font-weight: 700;font-synthesis: none;}
</style>

指定したのち、mainなど、実際で使用する箇所のfont-familyでこの"Noto Sans CJK JP"を指定しています。

@font-faceでWEBフォントの指定方法

抽出する元となるWebフォントによって、この名称は全く違うため、できるだけ、正確なフォント名で指定することをおすすめします。

加えて、woff2については、事前読み込みのプリロードで設定しておくことをおすすめします。

<link rel="preload" as="font" href="標準.woff2" crossorigin="anonymous">
<link rel="preload" as="font" href="太字.woff2" crossorigin="anonymous">

ただし、woffについては、woff2が使用できない時の代替フォントのため、事前読み込みをする必要はありません。

むしろ、最近のブラウザはほぼwoff2に対応しているため、woff2が使用できない環境の場合は、何かしら特殊な事情があると想定できます。

そのような環境では、あえてWebフォントを使用する必要性を感じないため、デフォルトのシステムフォントを使用してもらえばよい気もしています。そのため、サーバーのアクセスログを確認し、woffで転送された痕跡が全くないようでしたら、woff2だけでも問題ないかと思います。

ちなみに、当サイトでの割合はwoffが0.5%に対し、woff2が22.3%です。

woffとwoff2の割合

これまでは惰性でwoffも使用してきましたが、割合としてはわずかのため、woffの記載は削除してwoff2だけにする予定です。

ページ数が大量にある場合の対処方法

上記の<style></style>の記載箇所については、個別ページごとに違うため、ページ数が大量にある場合は手動で1つづつ記載するのが面倒です。その場合、以下のようなコードを作成して、ページ内のCSSの記載箇所を一括して取得しておくとよいかもしれません。

ホームページ作成ソフトを使用している場合、全てを自動で入力するのは難しいとは思いますが、コピペで修正していけば、簡単かと思います。

例えば、style.pyなどとして以下のコードを保存します。

style.py

import os

# 基本となるパスを定義します
base_dir = 'C:/Users/ユーザー名/Desktop/example.com/'
output_file = 'C:/Users/ユーザー名/Desktop/style/sytle.txt'

# 出力ファイルを開きます
with open(output_file, 'w', encoding='utf-8') as f:
    for root, dirs, files in os.walk(base_dir):
        for file in files:
            if file.endswith('.html'):
                # ファイル名(拡張子なし)を取得します
                if file == 'index.html':
                    if root == base_dir:  # 最上位ディレクトリの場合
                        file_name = 'index'
                    else:  # 下層ディレクトリの場合
                        dir_name = os.path.basename(root)
                        file_name = f'{dir_name}-index'
                else:
                    file_name = os.path.splitext(file)[0]
                
                # フォント情報のテキストを生成します
                font_info = """
<link rel="preload" as="font" href="/{0}.woff2" crossorigin="anonymous">
<link rel="preload" as="font" href="/{0}-bold.woff2" crossorigin="anonymous">

<style>
@font-face {{font-family: "Noto Sans JP";font-style: normal;font-weight: 400;
src: url(/{0}.woff2) format("woff2"),url(/{0}.woff) format("woff");font-display: swap;}}
@font-face {{font-family: "Noto Sans JP";font-style: normal;font-weight: 700;
src: url(/{0}-bold.woff2) format("woff2"),url(/{0}-bold.woff) format("woff");font-display: swap;}}
</style>
    """.format(file_name)

                # フォント情報を出力ファイルに書き込みます
                f.write(font_info + '\n')コピーボタンコピーチェックボタン

上記の<style></style>内にて、mainなどの実際に使用する箇所でfont-family: 'Noto Sans JP', sans-serif;を指定する必要があります。適当なテキストファイルに上記の箇所だけを一覧で取得しておき、コピペすれば、簡単かと思います。

ちなみに、さらに高速化を追求する場合、ファーストビューのみをサブセット化したWebフォントを先に読み込ませて表示し、残りのWebフォントは後からダウンロードする方法もあります。

ただし、この場合には、JavaScriptでの対応が必要になり、そのファイル自体のリクエスト回数が増えることを考えますとメリットは少ないと感じています。

最新記事のテキスト抽出とサブセット方法

上記の場合、サイト内の全てのファイルが対象となるため、最新記事のみをサブセット化する際には、更新日時で一番最近のものだけをピックアップします。

例えば、上記と同様、テキスト抽出用に「latest-reg-text」のフォルダを用意し、フォルダ内に「latest-reg-text.py」を作成します。そちらに、以下のようなコードを記載し、更新日時の最新のものだけをピックアップするようにします。

latest-reg-text.py

import os
from bs4 import BeautifulSoup

# 基本となるパスを定義します
base_dir = 'C:/Users/ユーザー名/Desktop/example.com/'
output_dir = 'C:/Users/ユーザー名/Desktop/latest-reg-text/'

# 最新の更新日時とそのファイルのパスを保持する変数を初期化します
latest_mtime = 0
latest_file = ""

# 基本パス内のすべてのHTMLファイルを探索します
for root, dirs, files in os.walk(base_dir):
    for file in files:
        if file.endswith('.html'):
            full_path = os.path.join(root, file)
            mtime = os.path.getmtime(full_path)
            # もしファイルが最新の更新日時を持っていれば、それを記録します
            if mtime > latest_mtime:
                latest_mtime = mtime
                latest_file = full_path

# 最新のファイルを開き、そのテキストを取得します
with open(latest_file, 'r', encoding='utf-8') as html_file:
    soup = BeautifulSoup(html_file, 'html.parser')
    all_text = soup.get_text()

# テキストファイルのファイル名を定義します
file_name = os.path.basename(latest_file)
if file_name == "index.html":
    if os.path.dirname(latest_file) == base_dir:  # 最上位の開始ディレクトリの場合
        txt_file_name = 'index.txt'
    else:  # 下層ディレクトリの場合
        dir_name = os.path.basename(os.path.dirname(latest_file))
        txt_file_name = f'{dir_name}-index.txt'
else:
    txt_file_name = os.path.splitext(file_name)[0] + '.txt'

txt_file_path = os.path.join(output_dir, txt_file_name)

# 取り出したテキストをテキストファイルに書き込みます(utf-8が前提)
with open(txt_file_path, 'w', encoding='utf-8') as txt_file:
    txt_file.write(all_text)コピーボタンコピーチェックボタン

このファイルを実行すると、最新記事の.txtファイルだけが作成されます。

最新記事のみをサブセット化

latest-reg-subset.py

同様に、サブセット用のフォルダ「latest-reg-subset」フォルダも作成し、latest-reg-subset.pyを作成します。ただし、このファイルについては、上記のreg-subset.pyとほぼ同じコードです。

「# .txtファイルがあるディレクトリ」の箇所を、上記の「latest-reg-text」に指定すればよいだけです。

# .txtファイルがあるディレクトリ
dir_path = "C:/Users/ユーザー名/Desktop/latest-reg-text/"

出力先フォルダのユーザー名なども忘れずに書き換えるようにしてください。

詳細なコードについては記載しませんが、reg-subset.pyを上記のように修正した上で、ファイル名をlatest-reg-subset.pyとして保存します。このlatest-reg-subset.pyを実行すると、最新記事の標準フォントが「output」フォルダ内に生成されます。

latest-bold-text.py

同様に、太字用の「latest-bold-text」のフォルダも作成し、最新記事用の.pyファイルを作成します。

import os
from bs4 import BeautifulSoup

# 基本となるパスを定義します
base_dir = 'C:/Users/ユーザー名/Desktop/example.com/'
output_dir = 'C:/Users/ユーザー名/Desktop/latest-bold-text/'

# 太字に関連するタグとクラス、IDをリストに格納します
tags = ['h1', 'h2', 'h3', 'a', 'th', 'strong', 'b', 'yourbold']
classes_and_ids = ['hogehoge', 'example']

# 最新の更新日時とそのファイルのパスを保持する変数を初期化します
latest_mtime = 0
latest_file = ""

# 基本パス内のすべてのHTMLファイルを探索します
for root, dirs, files in os.walk(base_dir):
    for file in files:
        if file.endswith('.html'):
            full_path = os.path.join(root, file)
            mtime = os.path.getmtime(full_path)
            # もしファイルが最新の更新日時を持っていれば、それを記録します
            if mtime > latest_mtime:
                latest_mtime = mtime
                latest_file = full_path

# 最新のファイルを開き、指定されたタグとクラス、IDのテキストを取り出します
with open(latest_file, 'r', encoding='utf-8') as html_file:
    soup = BeautifulSoup(html_file, 'html.parser')

    bold_text = ''

    # 指定したタグのテキストを抽出します
    for tag in tags:
        elements = soup.find_all(tag)
        for element in elements:
            bold_text += element.get_text() + '\n'

    # 指定したクラスとIDのテキストを抽出します
    for class_or_id in classes_and_ids:
        # classとして検索
        elements = soup.find_all(class_=class_or_id)
        for element in elements:
            bold_text += element.get_text() + '\n'

        # IDとして検索
        element = soup.find(id=class_or_id)
        if element:
            bold_text += element.get_text() + '\n'

    # 取り出したテキストをテキストファイルに書き込みます
    if bold_text:
        # テキストファイルのファイル名を定義します
        file_name = os.path.basename(latest_file)
        if file_name == "index.html":
            if os.path.dirname(latest_file) == base_dir:  # 最上位の開始ディレクトリの場合
                txt_file_name = 'index.txt'
            else:  # 下層ディレクトリの場合
                dir_name = os.path.basename(os.path.dirname(latest_file))
                txt_file_name = f'{dir_name}-index.txt'
        else:
            txt_file_name = os.path.splitext(file_name)[0] + '.txt'
        
        txt_file_path = os.path.join(output_dir, txt_file_name)

        with open(txt_file_path, 'w', encoding='utf-8') as txt_file:
            txt_file.write(bold_text)コピーボタンコピーチェックボタン

latest-bold-subset.py

同様に、「latest-bold-subset」フォルダも作成し、上記の「bold-subset.py」の内容を以下のように書き換えて保存します。

# .txtファイルがあるディレクトリ
dir_path = "C:/Users/ユーザー名/Desktop/latest-bold-text/"

このファイルを実行すれば、太字についてもwoff、woff2ファイルが「output」フォルダに生成されます。

説明を簡略化にするために、上記のフォルダは個別に作成していますが、実際に使用する際にはパスを変更して1つのフォルダにまとめることをおすすめします。

アプリ化して.pyファイルを一括で操作する方法

逐次、各フォルダ内の.pyファイルにアクセスして実行するのは面倒なため、管理画面のボタンを押すと対応するフォルダ内の.pyファイルが実行されるようにアプリ化することをおすすめします。

例えば、以下のようなコードを書いてどのボタンがどの.pyファイルに対応するのかを定義します。

Pythonの標準ライブラリでtkinterがあるので、そちらを利用するとよいでしょう。

例えば、上記の太字のスクリプトをbold-subset.pyと作成していた場合、以下のように記載するとボタンが表示されます。

import tkinter as tk
import os

def run_script():
    os.system("C:/Users/ユーザー名/Desktop/bold-subset/bold-subset.py")

root = tk.Tk()
button = tk.Button(root, text="太字フォントをサブセットする", command=run_script)
button.pack()

root.mainloop()コピーボタンコピーチェックボタン

すると、このように表示されます。

サブセットのGUIアプリ

defはJavaScriptのfunctionみたいなものですが、関数を定義するものになります。

同様に、他の.pyファイルも続けて記載するとこのようになり、アプリのGUIから操作できるようになります。

サブセットアプリ

この場合、Pythonはパソコンにインストールされているものを使用しますが、スタンドアロンの.exeファイルなどでPythonまでを含めてアプリ化すると単独でも機能するようにもなります。

どのみち、自分しか使用しないものになるため、最低限、動作する形で作成してサブセット化するとよいでしょう。