すでに次のようにカテゴリー分けされているし、内部リンクも十分あるんですけどね?
ちなみにXML形式のサイトマップもあるけど、これはタップしてもおもしろくないです。
私のチェック用みたいなものですから、コードの羅列が出てくるだけで、おまけって感じ?
このページでは、まず利便性向上のためにリストを表示しています。
そこから先は、このページを作っているソースコードの紹介へ移りますね😊
私がページを増やすたびに、自動的にここへ追加されていく仕組みになっていますよ?
ブログとかでよく見る、自動生成される記事リストみたいなもんです。
これも典型的なサーバーサイドスクリプトかと。
アナデン お勧め装備とバッジ一覧 ~編成の幅を広げる攻略ガイド~
App Engineのapp.yamlでWebサイトを設定する
Webサイトの共同制作とGitHubの導入:トラブル防止と基本操作ガイド
Google Driveを活用してサーバーの容量不足を解消する
ナビゲーションメニューをCSS・JavaScriptで用意する
無理しなくてもいいんですけどね。
でも、普通のhtml・css・JavaScriptだけでは、難しい状況もあるんです。
ホームページにサーバーサイドスクリプトがある意義を、なるべく分かりやすく、例え話をしつつ考えてみましょう。
サーバーサイドスクリプトは、ウェブサイトの「頭脳」と考えるとわかりやすいです。
例えば、レストランに行ったとき、お客さん(あなた)は注文をして料理を受け取りますよね。
でも、注文を受けたり料理を作ったりする「キッチン」の仕事は見えません。
この「キッチン」の役割がサーバーサイドスクリプトと考えてよろしいかと。
次は身近でよく見るタイプのWebサイトで、考えてみますね。
このページの後ろで動作している「PythonのFlaskフレームワーク」というのは、サーバーサイドスクリプトの一種です。 それを使って、Webページのリストを表示しているんですよね。 ページが増えるたびに手作業なんかしてたら、もう大変ですよ…
もしサーバーサイドスクリプトがなかったら、ウェブサイトは単に「紙に印刷されたポスター」のようになります。
見た目は綺麗でも、自分の情報を入力したり、自分専用のサービスを受けたりすることができません。
これが、いわゆる「機能」の部分です。
当サイトも、今後はこのサイトマップだけではなく、いろいろやっていくつもりです。
サーバーサイドがあるサイトだと、次のような感じになります。
こうした「ちょっと便利」を実現するのが、サーバーサイドスクリプトの意義なんです。
PythonのFlaskフレームワークでサイトマップを作ろうと考えた時、おおむね次のような手順となります。
Pythonコードの例を挙げるなら、次のような感じでしょうかね。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/sitemap')
def sitemap():
pages = [
{'url': '/', 'title': 'Home'},
{'url': '/about', 'title': 'About Us'},
{'url': '/contact', 'title': 'Contact'},
]
return render_template('sitemap.html', pages=pages)
次はHTMLです。
URLの書き方がちょっと特殊ですよね。
<ul>
{% for page in pages %}
<li><a href="{{ page.url }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
上述のコードはFlaskのテンプレートエンジンである「Jinja2」の構文を利用して動的にHTMLを生成するものです。
一見するとおかしなhtmlに見えるかもしれませんが、ちゃんと意味がありますよ。
これがまた便利なんですよ。
Flaskに限らず、フレームワークを使う意味といいますか。
例えば、数百ページにリンクを貼る場合、それぞれのURLとタイトルを手書きするのは非効率です。
Flaskで render_template() を使うことで、Pythonコードから一括してデータを注入し、テンプレート内で動的にリンクを生成できます。
データの管理も簡単なんですよね。
URLやタイトルをPython側でリストや辞書で一元管理できますし、修正が必要ならテンプレートをいじらずにPythonコードだけで対応可能です。
ここで言う「テンプレート」とは、動的なコードを差し込まれる側のhtmlファイルのことです。
テンプレートとなっているhtmlにはちょっとした特徴があります。
レンダリング後には、普通のhtmlとしてブラウザに送信されるため、最終的にはシンプルなリンクになるのです。
なんでも、Jinja2の名前は日本の「神社(jinja)」に由来しているとか。
これを開発したArmin
Ronacher氏が、日本文化やデザインの美しさからインスピレーションを受けて命名したと言われています。
数字の「2」は、初期バージョンのJinjaから進化を遂げた第二世代であることを示しています。
日本文化がこうした技術的なツールの名前に反映されているのは、なんだかとっても興味深いです。
日本人の個人サイトに、ぴったりな技術だと思いませんか?
参考サイト:Jinja2の公式ドキュメント
PythonのFlaskについては、今後もいろいろなページを作っていくときに説明していきます。
前置きはこのくらいにして、このページを作っているソースコードを見ていきましょう。
XMLについては、また後日に別のページで解説します。
まずは閲覧者に直接影響するHTMLのほうですね。
このコードの主な目的は、指定されたカテゴリーごとのページリストを作成し、/pagelistというURLでそのリストを表示することです。
リンクには、ページのURL(リンク先)とページのタイトル(画面に表示される名前)が含まれます。
htmlサイトマップを作るpagelist.py
import os
from flask import Blueprint, render_template
from bs4 import BeautifulSoup
pagelist_bp = Blueprint('pagelist', __name__)
def get_category_pages_with_titles(category_name):
static_dir = 'www'
category_pages = []
excluded_paths = [
'/index.html',
'/externalization/commongdl.html',
'/menu/menu.html',
'/another-eden/anaden_sitemap.html',
'/contents/site_create.html',
'/fish/fish_sitemap.html',
'/contents/rss.html',
'/sitemap.xml',
'/contents/x_exoduslink.html',
'/menu/symbol.html',
'/pages/pagelist',
'/'
]
for root, dirs, files in os.walk(static_dir):
if category_name in root:
for file in files:
if file.endswith('.html'):
path = os.path.join(root, file).replace('\\', '/')
url = path.replace(static_dir, '').lstrip('/')
if f'/{url}' not in excluded_paths:
title = get_page_title(path)
category_pages.append({'url': f'/{url}', 'title': title})
return category_pages
def get_page_title(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'html.parser')
return soup.title.string if soup.title else 'No Title'
except Exception as e:
return 'Error Reading Title'
def get_another_eden_pages():
return get_category_pages_with_titles('another-eden')
def get_contents_pages():
return get_category_pages_with_titles('contents')
def get_fish_pages():
return get_category_pages_with_titles('fish')
def get_rss_pages():
return get_category_pages_with_titles('rss')
def get_dynamic_pages(app):
dynamic_pages = []
excluded_paths = [
'/index.html',
'/externalization/commongdl.html',
'/menu/menu.html',
'/another-eden/anaden_sitemap.html',
'/contents/site_create.html',
'/fish/fish_sitemap.html',
'/contents/rss.html',
'/sitemap.xml',
'/contents/x_exoduslink.html',
'/menu/symbol.html',
'/pages/pagelist',
'/'
]
url_to_title = {
'/': None,
'/sitemap.xml': 'サイトマップのXML表示',
'/pages/pagelist': None,
'/redirect': '独自ドメイン設定後にリダイレクト機能を追加する',
'/top': '個人サイトへPythonのFlaskを導入する'
}
for rule in app.url_map.iter_rules():
if rule.endpoint != 'static' and '<' not in rule.rule and rule.rule not in excluded_paths:
title = url_to_title.get(rule.rule, 'Dynamic Page')
if title:
dynamic_pages.append({'url': rule.rule, 'title': title})
return dynamic_pages
@pagelist_bp.route('/pagelist')
def pagelist():
from main import app
pages_by_category = {
'Another Eden': get_another_eden_pages(),
'Contents': get_contents_pages(),
'Fish': get_fish_pages(),
'RSS': get_rss_pages(),
'Dynamic': get_dynamic_pages(app)
}
return render_template('pagelist.html', pages_by_category=pages_by_category)
このコードは、PythonのFlaskフレームワークを使って、サイトのページリスト(カテゴリーごとのリンクリスト)を動的に生成するプログラムです。
次のような仕組みで動作しているんですよね。
では、具体的にプログラムの構成を見ていきます。
pagelist_bp = Blueprint('pagelist', __name__)
Blueprint は、Flaskでルートや機能をモジュール化する方法です。
ここでは、 pagelist という名前の独立したルートを作成しています。
def get_category_pages_with_titles(category_name):
この関数は、www フォルダ内に保存されている静的なHTMLファイルを検索します。
指定したカテゴリー名(例えば、another-eden など)に属するページのURLとタイトルを取得してリストにまとめます。
それぞれの部分が何をしているか、ちょっと詳しく見てみましょうか。
例えば、カテゴリー another-eden に属するHTMLファイルを探し、そのファイルからURLとタイトルを取り出します。
for root, dirs, files in os.walk(static_dir):
if category_name in root:
for file in files:
if file.endswith('.html'):
path = os.path.join(root, file).replace('\\', '/')
url = path.replace(static_dir, '').lstrip('/')
title = get_page_title(path)
category_pages.append({'url': f'/{url}', 'title': title})
ファイルパスを探して url を生成。
各HTMLファイルのタイトルを取り出すため、get_page_title を呼び出します。
def get_page_title(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'html.parser')
return soup.title.string if soup.title else 'No Title'
BeautifulSoup(HTML解析ライブラリ)を使って、HTMLファイルの<title>タグからページタイトルを取得します。
def get_dynamic_pages(app):
Flaskアプリケーション(app)のルート一覧(url_map)を調べ、動的なURLを取得します。
一部除外対象(例えば、/index.html など)をフィルタリングした後、URLとタイトルをまとめます。
最後に、取得したデータを元に、/pagelist というURLでカテゴリーごとのリンクを表示します。
@pagelist_bp.route('/pagelist')
def pagelist():
from main import app
pages_by_category = {
'Another Eden': get_another_eden_pages(),
'Contents': get_contents_pages(),
'Fish': get_fish_pages(),
'RSS': get_rss_pages(),
'Dynamic': get_dynamic_pages(app)
}
return render_template('pagelist.html', pages_by_category=pages_by_category)
まずは get_another_eden_pages() などを呼び出してカテゴリーごとのデータを収集しますよね。
それからテンプレートに渡します。
Flaskの render_template() を使い、データを pagelist.html テンプレートに渡していく。
そして pagelist.html のテンプレートでリストが表示されるってこと。
このページのような動的サイトマップを作るとき、自分が作っているWebサイトのディレクトリ構造を把握しておいたほうが良いですね。
例えば私のサイトの場合、次のようになっています。
FEIN-SITES-DEV1(App EngineにデプロイされているFlaskアプリ)
│ app.yaml
│ dispatch.yaml
│ main.py
│ README.md
│ requirements.txt
│ sitemap.py
│ pagelist.py
│
├─redirect-service
│ app.yaml
│ redirect.py
│ requirements.txt
│
├─static
│ ├─css
│ ├─icons
│ ├─images
│ ├─js
│ ├─menu
│
├─templates
│ │ sitemap.xml
│ │ top.html
│ │ pagelist.html(これが今ご覧になっているページです)
│ │
│ └─server
│ redirect.html
│
├─www(これは静的ウェブサイト)
│ │ .hintrc
│ │ favicon.ico
│ │ index.html
│ │ manifest.json
│ │ robots.txt
│ │ service-worker.js
│ │
│ ├─another-eden
│ │ anaden_beginner.html
│ │
│ ├─contents
│ │ color.html
│ │
│ ├─css
│ ├─externalization
│ ├─fish
│ │ morayeel.html
│ │
│ ├─icons
│ ├─image
│ ├─menu
│ │ symbol.html
│ │
│ ├─rss
│ │ rss202502.html
│ │
│ ├─script
└─キャッシュなど
動的サイトと静的サイトが同じところに住んでいる、ちょっと変わった構造になっています。
こうすることで、柔軟に自分のコンテンツを拡大させつつも、いろんなリスクに即時対応できるわけです。
このディレクトリ構造を踏まえた上で、ソースコードの続きを見ていきます。
このコードは、Google App EngineにデプロイされているFlaskアプリケーションの中核部分、つまりメインの処理を定義しているファイルです。
これがちゃんとしていないと、サイトマップも正常動作しないのですよ。
サイトマップ生成を踏まえたmain.py
import os
from flask import Flask, render_template, send_from_directory, abort
from sitemap import sitemap_bp
from pagelist import pagelist_bp
app = Flask(__name__)
app.register_blueprint(sitemap_bp)
app.register_blueprint(pagelist_bp, url_prefix='/pages')
@app.route('/')
def serve_static_index():
return send_from_directory('www', 'index.html')
@app.route('/redirect')
def serve_redirect():
return render_template('server/redirect.html')
@app.route('/top')
def serve_flask_content():
return render_template('top.html')
@app.route('/<page_name>')
def serve_dynamic_content(page_name):
try:
return render_template(f'server/{page_name}.html')
except:
abort(404)
@app.route('/<path:filename>')
def serve_static_files(filename):
return send_from_directory('www', filename)
if __name__ == '__main__':
debug_mode = os.getenv('FLASK_DEBUG', 'False') == 'True'
app.run(debug=debug_mode)
このコードは、トップページ(静的ファイル)やテンプレートを利用した動的ページなど、いろいろなページにアクセスできるルートを定義しています。
また、 Blueprint を使って、サイトマップやページリストなどの機能をモジュールごとに分けて管理してもいる。
そして、静的ファイル(HTMLやCSS、画像)や動的に生成されるコンテンツをユーザーに返しているわけですよ。
import os
from flask import Flask, render_template, send_from_directory, abort
from sitemap import sitemap_bp
from pagelist import pagelist_bp
FlaskというPythonのフレームワークを使って、ウェブアプリを作成すると書いてありますね。
また、他のファイル( sitemap.py や pagelist.py )から機能をインポートして利用しています。
この場合、サイトマップ用とページリスト用の機能を取り込んでいます。
app = Flask(__name__)
Flaskアプリケーションのインスタンス(アプリ本体)を作成しています。
この app がアプリの中心となります。
app.register_blueprint(sitemap_bp)
app.register_blueprint(pagelist_bp, url_prefix='/pages')
BlueprintはFlaskアプリケーションをモジュール化して管理する仕組みです。
ここでは、sitemap_bp と pagelist_bp という2つのBlueprintを登録しています。
@app.route('/')
def serve_static_index():
return send_from_directory('www', 'index.html')
サイトのトップページ(`/`)にアクセスが来たとき、www というフォルダにある index.html を返します。
静的なファイルをそのまま表示します。
@app.route('/redirect')
def serve_redirect():
return render_template('server/redirect.html')
/redirect というURLにアクセスすると、templates/server/redirect.html を返します。
@app.route('/top')
def serve_flask_content():
return render_template('top.html')
/top というURLにアクセスすると、templates/top.html を返します。
@app.route('/<page_name>')
def serve_dynamic_content(page_name):
try:
return render_template(f'server/{page_name}.html')
except:
abort(404)
例えば、/about にアクセスすると、templates/server/about.html というテンプレートを表示しようとします。
でも指定されたHTMLファイルが存在しない場合は、404エラー(ページが見つかりません)を返します。
@app.route('/<path:filename>')
def serve_static_files(filename):
return send_from_directory('www', filename)
サイト内の画像やCSSファイルなどの静的なリソースを提供します。
例えば、/css/style.css にアクセスすると、www/css/style.css を返しますよね。
if __name__ == '__main__':
debug_mode = os.getenv('FLASK_DEBUG', 'False') == 'True'
app.run(debug=debug_mode)
__name__ == '__main__' という部分は、このファイルが直接実行された場合のみ以下のコードが動作する仕組みです。
アプリケーションを起動し、デバッグモード( debug=True )かどうかを環境変数 FLASK_DEBUG で制御しています。
app.yamlは別の機会でも良いかな。
ここではFlaskの/pages用ルートを解決するコードを入れましたが、それで紙面を割くほどじゃないでしょ。
上述してきたようにpythonが書けたら、テンプレートとなるhtmlを用意します。
このテンプレートに、pythonによって生成されたWebページリストが差し込まれていくわけですよ。
だから私の手作業が大幅に減るという仕組みなんです。
<h2>FlankによるWebページ</h2>
{% for page in pages_by_category['Dynamic'] %}
<p><a href="{{ page.url }}">{{ page.title }}</a></p>
{% endfor %}
<!-- Another Eden -->
<h2>アナザーエデン</h2>
{% for page in pages_by_category['Another Eden'] %}
<p><a href="{{ page.url }}">{{ page.title }}</a></p>
{% endfor %}
<!-- Contents -->
<h2>個人サイト制作</h2>
{% for page in pages_by_category['Contents'] %}
<p><a href="{{ page.url }}">{{ page.title }}</a></p>
{% endfor %}
<!-- Fish -->
<h2>アウトドア</h2>
{% for page in pages_by_category['Fish'] %}
<p><a href="{{ page.url }}">{{ page.title }}</a></p>
{% endfor %}
<!-- RSS -->
<h2>RSSフィード</h2>
{% for page in pages_by_category['RSS'] %}
<p><a href="{{ page.url }}">{{ page.title }}</a></p>
{% endfor %}
このように、サーバーサイドスクリプトの実装には意味があります。
静的ウェブサイトは安定稼働という意味で最高峰だと感じるのですが、それを統一管理する仕組みはサーバーサイドスクリプトが欲しいですよね。
私の場合、PythonのFlaskフレームワークということなんです。
全ページをリスト化したサイトマップも用意していますが、けっこうなページ数があります。
下記の「カテゴリー分けサイトマップ」のほうが使いやすいでしょう。
アナザーエデンの強敵戦やストーリーコンテンツのリスト、お勧めバッジなどを掲載したコーナーです。
期間限定のない普通のRPGですので、初心者でも安心して続けていけるゲームとなっています。
もっとも重要なグラスタについては、場所別に網羅した表があります。
個人でウェブサイトを作るにはどうすればいいか。
HTML・CSS・JavaScriptの書き方はもちろん、無料かつ広告なしでホームページを作る方法を掲載したコーナーです。
Webデザインやレイアウトについても書いてあります。
ゲームとパソコンだけじゃなく、アウトドアも趣味なんです。
このコーナーでは魚釣りの記録とか、魚料理のレシピ、はたまたサイクリングなどなど。
アウトドアに関連するコンテンツが詰め込まれています。