Liste des codes postaux et codes insee des communes

Map showing the communes of Metropolitan Franc...
Image via Wikipedia

Le terme commune est utilisé de manière assez générique en France. Pour ma part je préfère considérer qu’une commune est la plus petite division administrative c’est-à-dire qu’une commune correspond à une mairie. Les communes de France sont identifiées par l’INSEE, organisme public, par un code. La liste des codes INSEE et donc des communes est disponible sur le site de l’INSEE dans la rubrique téléchargement des Code Officiels Géographiques. Le fichier intitulé Liste des communes existantes au 1er janvier 2011 nous intéresse ici.

Par ailleurs si les codes INSEE constituent une nomenclature officielle, ils sont aussi réservés aux initiés (statisticiens, cartographes…). Le grand public ne connait des communes que leur code postal. Malheureusement la notion de code postal ne correspond pas à la notion de commune. En effet sur une commune il peut y avoir plusieurs codes postaux. Un code postal peut par exemple se justifier quand une localité est éloignée du centre ville ou dans le cas d’arrondissement de grande ville. Les cas sont nombreux. La liste des codes postaux est gérée par La Poste, service d’origine publique. Pourtant il est difficile d’obtenir la liste des codes postaux et un marché de niche existe dans le domaine. Le Service National des Adresse (SNA) propose néanmoins un service en ligne des codes postaux. Ce dernier donne le code postal en fonction du nom de la localité.
Nous nous plaçons dans la problématique d’obtenir un référentiel des communes avec leur code INSEE et leur code postal. L’idée est de partir de la liste de l’INSEE puis de requêter le service du SNA. Ce dernier ne proposant pas de liste complète il faut pour chacune des communes remplir un formulaire pour obtenir le code postal correspondant.

Le script python suivant permet heureusement de faciliter ce travail en prenant en argument le fichier des codes INSEE et le fichier à générer. Les 2 difficultés sont:

  • Le format des noms de localité: pas de tiret, d’apostrophe etc.
  • La réponse HTML qui n’est pas bien formée: attribut sans délimiteur
#!/usr/bin/python
import urllib
import sys
from HTMLParser import HTMLParser
import logging

def get_cp(txtCommune,cdep=None):
  logging.info("looking for %s" %  txtCommune)
  params = urllib.urlencode({'txtCommune':txtCommune, 'selCritere':'CP'})
  f = urllib.urlopen('//www.laposte.fr/sna/rubrique.php3?id_rubrique=59&recalcul=oui', params)
  reponse = f.read().decode('iso-8859-1')
  f.close()
  #-- remove malformatted HTML
  i = 0
  while i > -1:
    i = reponse.find('onclick=window.open')
    if i > -1 :
      logging.warn("malformed html found")
      j = reponse.find('false;',i)
      reponse = reponse[:i] + reponse[j+len('false;'):]
  p = MyParser(cdep)
  p.feed(reponse)
  p.close()
  return p.cp_found

class MyParser (HTMLParser):
  result_found=False
  tag = None
  cdep = None
  cp_found = []

  def __init__(self, cdep):
    HTMLParser.__init__(self)
    self.cdep = cdep
    self.tag = None
    self.result_found = False
    self.cp_found= []

  def handle_starttag(self, tag, attrs):
    for a,v in  attrs:
      if a == 'class':
        if v  =='resultat':
           self.result_found=True
           self.tag = tag

  def handle_endtag(self, tag):
    if self.result_found:
      if tag == self.tag:
        self.result_found=False

  def handle_data(self,data):
    if self.result_found :
      if self.cdep:
        if data.startswith(self.cdep) :
          self.cp_found.append(data)
      else:
        self.cp_found.append(data)

def normalize_commune(art, ncc):
  logging.info("normalizing %s %s" % (art, ncc))
  resu=None
  if art:
    resu = art.strip('(').strip(')').strip("'")
  if resu:
    resu = "%s %s" % (resu, ncc)
  else:
    resu = ncc
  resu = resu.replace('-',' ').replace('SAINT','ST').replace("'"," ")
  return resu

logging.basicConfig(level=logging.INFO)
with open(sys.argv[2],'w') as insee_cp:
  with  open(sys.argv[1]) as insee:
    first = True
    for line in insee:
      if first:
         first = False
         continue
      splitted = line.split('t')
      dep = splitted[3]
      com = splitted[4]
      artmaj = splitted[8]
      ncc = splitted[9]
      txtCommune = normalize_commune(artmaj, ncc)
      cp = get_cp(txtCommune, dep)
      cd_insee = dep + com
      if len(cp) > 1:
        logging.warn("Plusieurs  CP")
      for a_cp in cp:
        logging.info("%s CP %s INSEE %s" % (txtCommune, a_cp, cd_insee))
        insee_cp.write("%st%st%s" % (txtCommune, a_cp, cd_insee))
        insee_cp.write("n")

Le fichier généré contient le nom de la commune (formatée pour le site du SNA), le code postal et le code INSEE. Le lecteur pourra sans peine modifier le script pour qu’il sorte le nom de la commune dans sa forme originale (avec les apostrophes, tirets etc.)

Pour ceux que le code python rebute, le résultat du script est par ici : code postal et insee des communes 2011