Base de connaissances

Normaliser les prix, nombres et dates entre les locales

1.234,56 € et $1,234.56 sont tous deux des prix, et 03/04/2026 correspond à deux dates différentes. Comment analyser correctement les valeurs extraites selon la locale, avec du code testé.

James Meadow

James Meadow

30 septembre 2026 · 9 min de lecture

Collectez des prix dans plusieurs pays et vous rencontrerez le même nombre écrit d’une douzaine de façons différentes. Un site allemand affiche 1.234,56 €. Un site français affiche 1 234,56 €, avec une espace que vous ne pouvez peut-être pas voir. Un site suisse affiche CHF 1’234.50. Un site indien groupe les chiffres ainsi : 12,34,567. Et une date comme 03/04/2026 signifie le 4 mars aux États-Unis et le 3 avril presque partout ailleurs.

Si vous vous trompez sur l’un de ces points, l’erreur est rarement évidente : un prix faux d’un facteur mille, une date décalée d’un mois, une devise confondue avec une autre qui partage son symbole. Ce guide couvre les formats que vous rencontrerez, les règles qui les déterminent, et un code d’analyse qui refuse de deviner quand une valeur ne correspond pas.

Points clés

  • Analysez chaque valeur avec la locale de la page dont elle provient. Les mêmes caractères signifient des nombres différents selon les locales.
  • Ne retirez pas les séparateurs en espérant que cela fonctionne. Déterminez quel symbole est le séparateur décimal à partir de la locale, et rejetez les valeurs qui ne correspondent pas.
  • Traitez les symboles monétaires comme ambigus. « $10.00 » représente des dollars canadiens sur une page canadienne, des pesos sur une page mexicaine et des dollars australiens sur une page australienne. Stockez les codes ISO 4217.
  • Stockez les montants en unités mineures avec la bonne précision. Le yen n’a pas de décimales ; le dinar bahreïni et le dinar koweïtien en ont trois.
  • Les dates numériques nécessitent une locale, ou mieux, une source lisible par machine. Privilégiez le format ISO 8601 issu des données structurées lorsque la page le fournit.

D’où viennent ces règles

Les conventions de locale ne relèvent pas du folklore. Elles sont publiées dans le Unicode Common Locale Data Repository, CLDR, qu’utilisent les systèmes d’exploitation, les navigateurs et la plupart des bibliothèques d’internationalisation. Les exemples ci-dessous proviennent directement des données CLDR, via la bibliothèque Python Babel, en formatant le nombre 1234567.891 et le montant de 1 234,50 euros :

LocaleDécimaleGroupement1234567.891€1,234.50
Anglais, États-Unis.,1,234,567.891€1,234.50
Allemand, Allemagne,.1.234.567,8911.234,50 €
Français, France,espace fine insécable1 234 567,8911 234,50 €
Allemand, Suisse.’1’234’567.891EUR 1’234.50
Anglais, Inde.,12,34,567.891€1,234.50
Portugais, Brésil,.1.234.567,891€ 1.234,50

Trois détails piègent les gens. Le français groupe les milliers avec une espace fine insécable, un caractère qui ressemble à une espace mais n’en est pas une. L’allemand suisse utilise un caractère proche de l’apostrophe. Et l’anglais indien groupe les trois premiers chiffres puis par paires, si bien que 1 234 567 s’écrit 12,34,567.

Analysez avec la locale, et refusez ce qui ne correspond pas

L’approche tentante consiste à retirer tout sauf les chiffres et un séparateur, puis à deviner lequel est le séparateur décimal. Cela fonctionne jusqu’à ce que l’on rencontre « 1.234 », qui vaut mille deux cent trente-quatre en Allemagne et un virgule deux cent trente-quatre aux États-Unis. Aucune astuce ne permet de retrouver la bonne réponse à partir de la seule chaîne de caractères. La locale, si.

import re

from babel.dates import parse_date
from babel.numbers import NumberFormatError, get_currency_precision, get_group_symbol, parse_decimal

# Keep digits, separators and the space variants sites use between thousands.
NUMBER_CHARS = re.compile("[^0-9.,'\u2019 \u00a0\u2009\u202f-]")
SPACES = re.compile("[ \u00a0\u2009\u202f]")


def parse_amount(text, locale):
    """Parse a displayed amount using the page's locale. Refuses input that does not fit it."""
    number = NUMBER_CHARS.sub("", text).strip()
    group = get_group_symbol(locale)
    if SPACES.fullmatch(group):
        # Sites mix ordinary, no-break and narrow no-break spaces; use the locale's own.
        number = SPACES.sub(group, number)
    try:
        return parse_decimal(number, locale=locale, strict=True)
    except NumberFormatError as e:
        raise ValueError(f"{text!r} does not parse as a {locale} number: {e}") from None


def to_minor_units(amount, currency):
    """Store money as an integer in the currency's smallest unit (cents, yen, fils)."""
    return int((amount * 10 ** get_currency_precision(currency)).to_integral_value())


def parse_display_date(text, locale):
    """Numeric dates are ambiguous without a locale: 03/04/2026 is March or April."""
    return parse_date(text.strip(), locale=locale)

Testé sur des formats réels :

AffichéLocaleRésultat analysé
$1,234.56Anglais, États-Unis1234.56
1.234,56 €Allemand, Allemagne1234.56
1 234,56 € (l’un des trois caractères d’espace)Français, France1234.56
CHF 1’234.50Allemand, Suisse1234.50
₹12,34,567.00Anglais, Inde1234567.00
R$ 1.234,56Portugais, Brésil1234.56
1.234,56 €Anglais, États-UnisRefusé
9,99Anglais, États-UnisRefusé

Les deux dernières lignes sont l’essentiel. Un prix au format allemand sur une page que vous pensiez américaine est le signe que quelque chose ne va pas : la mauvaise variante de locale a été servie, la page a changé, ou votre configuration est erronée. Une erreur visible vaut mieux qu’un prix silencieusement faux d’un facteur mille.

La gestion des espaces a mérité sa place lors des tests. Notre première version rejetait un prix français écrit avec une espace ordinaire, parce que le séparateur français de CLDR est l’espace fine insécable. Les sites réels mélangent les trois types d’espace, si bien que le code convertit désormais chacun d’eux vers celui de la locale avant l’analyse.

Devise : ne jamais faire confiance au seul symbole

De nombreuses devises partagent un symbole. CLDR formate dix unités de la devise locale en « $10.00 » aussi bien au Canada, au Mexique qu’en Australie, et affiche le dollar américain sous la forme « US$10.00 » sur une page canadienne. Un scraper qui associe « $ » à USD se trompera sur une large part des sites du monde.

Déterminez la devise à partir de quelque chose de non ambigu : un code ISO 4217 dans les données structurées de la page, la valeur d’un sélecteur de devise, ou la devise par défaut connue du site et du marché depuis lequel vous avez collecté. Stockez le code à trois lettres avec chaque prix, et traitez un prix avec seulement un symbole et aucune autre preuve comme incomplet plutôt que de deviner.

Unités mineures et précision

Une fois analysés, stockez les montants sous forme d’entiers dans la plus petite unité de la devise, car l’arithmétique en virgule flottante sur des montants d’argent accumule des erreurs d’arrondi. Le nombre de décimales n’est pas toujours deux :

DeviseDécimales1 234 affiché devient
Euro, dollar américain2123400
Yen japonais, won coréen, peso chilien01234
Dinar bahreïni, dinar koweïtien31234000

Enregistrez l’unité en même temps que la valeur. Un pipeline qui mélange unités majeures et mineures, comme le prix arrivé sous la forme 10000 pour un produit à 100 $ décrit dans la dérive de schéma dans les données scrapées, produit des erreurs exactement d’un facteur cent qui paraissent tout à fait plausibles.

Dates : privilégiez les sources lisibles par machine

La même date numérique s’analyse différemment selon la locale :

AffichéLocaleRésultat analysé
03/04/2026Anglais, États-Unis4 mars 2026
03/04/2026Anglais, Royaume-Uni3 avril 2026
03/04/2026Allemand, Allemagne3 avril 2026

Quand c’est possible, évitez tout simplement d’analyser les dates affichées. De nombreuses pages publient des dates lisibles par machine dans des données structurées ou dans l’attribut datetime d’un élément <time>, généralement au format ISO 8601, qui n’est pas ambigu ; leur extraction est traitée dans arrêtez d’analyser le HTML. Lorsque vous devez analyser une date affichée, utilisez explicitement la locale de la page, stockez le résultat en ISO 8601 avec un fuseau horaire, et signalez les valeurs où le jour et le mois sont tous deux inférieurs ou égaux à douze pour les sites dont vous n’avez pas confirmé la locale.

Établir la bonne locale dès le départ

Chaque règle ci-dessus dépend du fait de savoir quelle locale une page a utilisée, ce qui relève en partie d’une décision de collecte. La même page produit peut s’afficher dans une langue, une devise et un format numérique différents selon le pays du visiteur, c’est pourquoi un prix collecté via une sortie en Allemagne et un autre collecté via une sortie aux États-Unis ne sont pas directement comparables tant que les deux n’ont pas été normalisés. Enregistrez le marché depuis lequel chaque valeur a été collectée, et maintenez des sessions cohérentes en langue et en région, comme expliqué dans faire correspondre la géolocalisation, le fuseau horaire et la locale d’un proxy résidentiel.

En résumé

Les nombres, les prix et les dates ont l’air universels, mais ne le sont pas. Les mêmes caractères signifient des valeurs différentes selon les locales, les symboles correspondent à plusieurs devises, la précision varie selon la devise, et les dates numériques sont ambiguës par nature.

Analysez chaque valeur avec la locale dans laquelle elle a été écrite, refusez ce qui ne correspond pas plutôt que de deviner, stockez la devise sous forme de code ISO et les montants en unités mineures avec la bonne précision, et privilégiez les dates lisibles par machine partout où la page les fournit. Chacune de ces règles transforme une erreur silencieuse et plausible en une erreur visible.

Sources et références

Prêt à commencer ?

Essayez les proxies résidentiels de Shifter, 205M+ IPs, 195+ pays, à partir de 0,75 $/GB.

Commencer