Analysis6 min read

Japanese price parsing: yen, width and tax labels

Parse Japanese price fields without losing currency or tax basis. Run a tested Python fixture for fullwidth yen, mixed prices and deliberate review cases.

On this page

A Japanese price parser should return an amount together with currency evidence, original text and tax basis. Select one offer field first, guard Unicode normalization, and hold ambiguous inputs. A parsed field still needs checks before it becomes an accepted monitoring record.

The downloadable Python example makes that boundary explicit. Its 36 synthetic offline cases produced 36 expected classifications: 11 parsed fields, 25 review outcomes and zero fixture failures. No Japanese storefront or proxy was tested. The useful result is a reproducible set of decisions, including when the parser declines to choose a price.

A display can contain two legitimate amounts

Japan's National Tax Agency gives this example in its total-price display guidance:

code
10,000円(税込価格11,000円)

The explicitly tax-inclusive amount is 11,000 yen, although 10,000 appears first. The NTA's examples permit displays containing both inclusive and exclusive figures. Taking the first number would lose that distinction. NTA No. 6902, display examples.

Removing every nondigit is worse: applied to this string, Python's re.sub(r"\D", "", text) produces 1000011000. The supplied parser returns review with reason unsupported_or_multiple_price_text. It does not choose an amount from a multi-price container.

Resolve that upstream: identify the intended amount and its label within the selected offer, preserving the source context. If your extractor cannot distinguish the amounts, keep the record under review. A general display obligation does not prove the tax basis of an arbitrary extracted field.

Require currency context before parsing

The ¥ character does not establish Japanese yen. Unicode specifies U+00A5 for both Japanese yen and Chinese yuan, independent of glyph styling. Obtain explicit currency context for the same offer before supplying JPY. Unicode 17, “Yen and Yuan”.

The function accepts only three keys, with text limited to a string of 1–200 characters. Here is a synthetic fixture input, whose evidence reference is deliberately a fixture label:

json
{
  "text": "¥1,298(税込)",
  "currency": "JPY",
  "currency_evidence": "fixture.selected_offer.priceCurrency"
}

For that input, the recorded result is:

json
{
  "original": "¥1,298(税込)",
  "status": "parsed",
  "normalized": "¥1,298(税込)",
  "amount_yen": 1298,
  "currency": "JPY",
  "currency_evidence": "fixture.selected_offer.priceCurrency",
  "tax_basis": "included",
  "tax_label": "税込"
}

In an adapter, call parse_price(record) from the downloaded module with those three keys. Keep product identity, URL, timestamp and other monitoring context in an outer record; extra keys in this function's input return invalid_record.

The evidence reference is a caller assertion. The function requires a nonempty string but neither retrieves nor authenticates it. An author probe using a deliberately fabricated reference still returned parsed. Supplying JPY because the page has a yen-shaped glyph would therefore defeat the intended contract even if the parser passes. Check the underlying currency evidence separately and preserve conflicts.

Guard normalization before it changes the field

Fullwidth digits can represent ordinary decimal digits, but compatibility normalization also changes other characters. Unicode's NFKC form can erase distinctions beyond width; Python exposes it through unicodedata.normalize. Unicode compatibility digits, normalization guidance, Python unicodedata.

This parser checks each character before normalizing the whole field. It permits changes for fullwidth ASCII U+FF01–U+FF5E, ideographic space U+3000 and fullwidth yen U+FFE5. Other characters that change under NFKC go to review. The grammar is checked after normalization; an allowed width conversion does not itself make an input a price.

That order matters for ¥①298(税込) and ¥¹298(税込). Blind compatibility folding can turn the circled or superscript character into an ordinary digit. The fixture holds both as unsupported_compatibility_character. It also holds ASCII and fullwidth backslashes; a character's font appearance does not make it a yen sign.

Preserve original alongside normalized, as the successful record does above. This guarded policy is specific to the downloaded parser. It is not a prescription to apply NFKC indiscriminately to product names, identifiers or arbitrary page text.

Preserve tax basis without calculating tax

The parser accepts one whole-yen expression: ¥1298, 1298円 or JPY 1298, with optional correctly grouped commas. A supported trailing tax label can appear in parentheses or after whitespace. It maps 税込 and 税込価格 to included; 税抜, 税抜き and 税別 to excluded. No label produces unspecified.

Selected outcomes from the executed fixture are below. Except where stated, the input has currency: "JPY" and the synthetic evidence reference shown above.

Input fieldResultConsequence for a later comparison
¥1,298(税込)parsed: 1298, includedKeep the inclusive basis with the amount.
1,180円(税抜)parsed: 1180, excludedDo not silently compare it as the same basis as an inclusive amount.
1,298円parsed: 1298, unspecifiedIf the job requires known tax basis, that requirement remains unresolved.
10,000円(税込価格11,000円)reviewSelect and evidence the intended field before parsing.
12,98円(税込)reviewThe grouping does not meet this grammar.
¥①298(税込)reviewAn unsupported compatibility change is held before normalization.
¥1298(税込), currency missing or CNYreviewA familiar glyph cannot supply the required JPY context.

There is no tax-rate calculation, currency conversion or cents multiplier. amount_yen is an integer under this parser's whole-yen input contract; it does not define the units used by every API. Do not derive a missing gross or net value from the nearby number.

The grammar also holds ranges, instalments, points, multiple prices, negatives, decimals, Kanji-number units, prefixed tax labels and leading zeros. Some may be valid store presentations outside this scope. A review outcome means the input is unresolved under this grammar, not that the merchant's display is invalid.

Run the same offline fixture

Download parse_price.py and fixtures.json into one directory. The script uses Python's standard library; the README gives the Python 3.10-or-newer input contract and limits. Run:

sh
python3 parse_price.py fixtures.json > replay.json

The command makes no network requests. Compare replay.json with the published results.json. A matching run reports cases: 36, passed: 36 and failed: 0. The 25 review outcomes are expected decisions, so they do not count as fixture failures.

The fixture checks each declared expected field and retains complete actual outputs. Exit status 0 means the fixture expectations matched; status 1 means at least one did not; status 2 reports an invalid or unreadable fixture. None is a storefront acceptance signal.

The original environment record documents Python 3.14.7 with Unicode database 16.0.0. The author replay on Python 3.14.8, also using Unicode database 16.0.0, produced the same complete result JSON. Citing Unicode 17 documentation does not mean the executable used a Unicode 17 database.

Decide whether a parsed field is usable

Before accepting a monitoring record, bind the field to the exact product and variant, retain the source and observation time, validate currency evidence and resolve the price basis your comparison requires. Check quantity, membership, sale conditions and delivery context when the job depends on them. Those are decisions outside this parser.

For example, the fixture's 1298-yen inclusive field and 1180-yen exclusive field both parse. That does not make their numeric difference a measured price change. An unlabelled field can also parse while remaining unsuitable for a comparison that requires inclusive prices.

The separate price and currency validation case addresses conflicting offer and storefront signals. If your collection also requires a Japanese exit or named mobile access, use the Japan proxy evaluation guide for that evidence. A local parsing result supplies no evidence about an exit's country, network or availability.

Method: agent-assisted source research and synthesis, with executed synthetic fixtures and the separately described limit probe on 6 October 2026. These cases demonstrate the stated parser behavior; they are not a sample of Japanese retailers or an accuracy benchmark. No storefront, proxy or tax calculation was tested.

Notify me when access opens. One email when access opens. Nothing else.

Sources

  1. No.6902 「総額表示」の義務付け
  2. Unicode 17.0.0, Chapter 22: Symbols
  3. FAQ: Normalization
  4. Python unicodedata

Tagged:ProxiesTroubleshooting