Turn local-business data into structured, SEO-ready HTML with a zero-dependency template engine and Schema.org JSON-LD builder.
Project description
localbiz-page
Turn local-business data into structured, SEO-ready HTML — with zero runtime dependencies.
localbiz-page pairs a tiny HTML template engine with deterministic parsers for
Japanese local-business data (postal codes, addresses, opening hours, languages)
and a Schema.org JSON-LD builder. Feed it a
plain dict and an HTML template; get back a rendered page plus valid structured
data for rich search results.
- No dependencies. Pure Python standard library.
pip installpulls in nothing else. - Deterministic. Same input → same output. No AI, no network calls.
- Composable. Use the template engine, the parsers, or the JSON-LD builder independently.
日本語の説明は下のほうにあります。
Install
pip install localbiz-page
Or just copy src/localbiz_page/ into your project — it has no dependencies.
Quick start
from localbiz_page import (
TemplateEngine, build_local_business, to_script_tag,
format_postal_code, parse_address, parse_opening_hours, map_languages,
)
# 1. Build Schema.org JSON-LD from raw fields.
jsonld = build_local_business(
"青葉珈琲店",
business_type="CafeOrCoffeeShop",
url="https://example.com",
telephone="03-1234-5678",
postal_code=format_postal_code("1600021"), # -> "160-0021"
address=parse_address("東京都渋谷区神南1-2-3"), # -> region / locality / street
opening_hours=parse_opening_hours("09:00-18:00", "水"),
languages=map_languages([{"対応可能な外国語名": "英語"}]), # -> ["ja", "en"]
)
# 2. Render an HTML template against your data.
html = TemplateEngine().render(
"<h1>{{ name }}</h1>{{ json_ld }}",
{"name": "青葉珈琲店", "json_ld": to_script_tag(jsonld)},
)
See examples/generate.py for a full
cafe.json + cafe_template.html → HTML pipeline.
The template engine
Double-brace syntax (so it never clashes with the single braces in inline CSS/JS or serialized JSON), three constructs:
| Syntax | Meaning |
|---|---|
{{ name }} / {{ shop.city }} |
Variable substitution, with dotted paths |
{{#if path}}...{{/if}} |
Conditional block (truthiness of path); nests freely |
{{#for item in items}}...{{/for}} |
Loop over a list; item is exposed in the body |
from localbiz_page import render
render("{{#if open}}Welcome, {{ name }}!{{/if}}", {"open": True, "name": "Aoba"})
# "Welcome, Aoba!"
# {{#if}} works inside {{#for}}, and outer variables stay in scope:
render("{{#for m in menu}}<li>{{ m.item }}{{#if m.hot}} 🔥{{/if}}</li>{{/for}}",
{"menu": [{"item": "Coffee", "hot": True}]})
# "<li>Coffee 🔥</li>"
Rendering is recursive-descent, so conditionals and loops nest correctly and a
loop body can reference both the loop variable and outer variables. Empty/missing
values render as an empty string; None, "", [], {} and False are falsy
for {{#if}}. An unclosed block raises ValueError.
No auto-escaping. Values are inserted verbatim — intended for trusted, structured input (you often want to embed HTML). Escape untrusted values yourself (e.g.
html.escape) before putting them in the data dict. Single braces in the template pass through untouched, so inline CSS/JS is safe.
Local-business parsers
All functions are pure and side-effect free:
| Function | Input → Output |
|---|---|
format_postal_code("1600021") |
"160-0021" |
parse_address("東京都渋谷区…") |
{"addressRegion": "東京都", "addressLocality": "渋谷区", "streetAddress": "…"} |
parse_opening_hours("09:00-18:00", "水") |
Schema.org OpeningHoursSpecification[] |
parse_days_off("木 日 祝日") |
["Thursday", "Sunday"] |
map_languages([...]) |
ISO 639-1 codes, always including "ja" |
should_display(value) |
filters Japanese "no data" markers (-, 無し, …) |
safe_get(data, "a.b.c") |
nested dict access with default |
JSON-LD builder
build_local_business(name, ...) returns a Schema.org JSON-LD dict. It accepts
any LocalBusiness subtype via business_type
("Restaurant", "Store", "BeautySalon", …), omits empty fields, and takes an
extra= dict for any additional Schema.org property. to_script_tag(jsonld) wraps
it in a ready-to-embed <script type="application/ld+json"> tag.
Develop & test
PYTHONPATH=src python -m unittest discover -s tests -v
python examples/generate.py
No test dependencies — everything runs on the standard library.
Why "local business"?
The parsers encode conventions specific to Japanese local businesses (prefecture splitting, weekday-based opening hours, language-name mapping), so they shine for restaurants, cafes, salons, shops, studios and similar listing/directory pages. The template engine and JSON-LD builder, however, are entirely domain-agnostic — use them for any structured-content page.
License
MIT — see LICENSE.
日本語
ローカルビジネスの情報を、外部依存ゼロで構造化された SEO 向け HTML に変換するツールキットです。
- 軽量 HTML テンプレートエンジン(
{{#if}}/{{#for}}/{{ 変数 }}の二重ブレース構文) - 日本のローカルビジネス向けパーサ(郵便番号・住所・営業時間・対応言語)
- Schema.org
LocalBusinessの JSON-LD ビルダ
いずれも Python 標準ライブラリのみで動作し、確定的(同じ入力なら同じ出力、AI 推論なし)です。 飲食店・サロン・クリニック・店舗など、住所と営業時間を持つ施設ページの生成に向いています。 テンプレートエンジンと JSON-LD ビルダ自体は業種非依存で、あらゆる用途に使えます。
使い方は examples/generate.py(cafe.json から HTML を生成する一連の流れ)を参照してください。
ライセンスは MIT です。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file localbiz_page-0.1.0.tar.gz.
File metadata
- Download URL: localbiz_page-0.1.0.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
804f6ac94aad3bcb87614c04cee72cb1de46b767fb4af4a5f600ba6834dd665c
|
|
| MD5 |
09b369fbc34b431ddc7922433894bbae
|
|
| BLAKE2b-256 |
0d77a49e1fc4005f5cbbb826a4cf0f367c3b1f4f343a0d11930426874794fdbe
|
Provenance
The following attestation bundles were made for localbiz_page-0.1.0.tar.gz:
Publisher:
publish.yml on asapura/localbiz-page
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
localbiz_page-0.1.0.tar.gz -
Subject digest:
804f6ac94aad3bcb87614c04cee72cb1de46b767fb4af4a5f600ba6834dd665c - Sigstore transparency entry: 1728281665
- Sigstore integration time:
-
Permalink:
asapura/localbiz-page@05cd4796ec340233dcc26e14b40cae9b24add8d1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/asapura
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05cd4796ec340233dcc26e14b40cae9b24add8d1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file localbiz_page-0.1.0-py3-none-any.whl.
File metadata
- Download URL: localbiz_page-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1bc01e1c03ea15e833ca5ccac97632158c68d522561afec5fca697d46ee20c29
|
|
| MD5 |
0fde68bdd796a1fd3559d4fe3d72a156
|
|
| BLAKE2b-256 |
b15e3c3c99ac3d3629e5c51fc49fe3db44b2f6e7730626358fd0c2eb64a01a82
|
Provenance
The following attestation bundles were made for localbiz_page-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on asapura/localbiz-page
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
localbiz_page-0.1.0-py3-none-any.whl -
Subject digest:
1bc01e1c03ea15e833ca5ccac97632158c68d522561afec5fca697d46ee20c29 - Sigstore transparency entry: 1728281802
- Sigstore integration time:
-
Permalink:
asapura/localbiz-page@05cd4796ec340233dcc26e14b40cae9b24add8d1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/asapura
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@05cd4796ec340233dcc26e14b40cae9b24add8d1 -
Trigger Event:
release
-
Statement type: