Pourquoi votre maire va probablement s'appeler Michel et aura la soixantaine (avec Python)

Source : Wikipédia

L'actualité s'y prêtant, nous allons aujourd'hui parler des élections municipales. En effet, les 15 et 22 mars de cette année 2020, les citoyens seront invités à élire leurs conseillers municipaux et conseillers communautaires (et non leur maire, celui-ci sera désigné plus tard par les dits conseillers municipaux, attention subtilité).

Dans ce contexte, le ministère de l'intérieur a mis en ligne un site internet indiquant l'ensemble des candidats et listes se présentant à ces élections. L'initiative est louable, elle permet à tout-un-chacun de se tenir informé des personnes candidates, que ce soit chez elle, dans les villes voisines, ou n'importe où ailleurs. Cependant, le format (un site internet) a le défaut de ne pas permettre une exploitation facile des données. Supposons que l'on souhaite savoir combien il y a de personnes candidates en tout ou le nombre de femmes en position de tête de liste : on ne peut pas le déterminer facilement.

Tout cela serait facilité si les données brutes étaient disponible sous un format facilement exploitable, comme CSV ou JSON. Ces formats sont facilement lisibles (même « à l'œil nu ») et il existe pléthore de bibliothèques logicielles pour les exploiter (et faire des stats).

Notre objectif sera donc à partir des informations disponibles sur le site du ministère de l'intérieur d'obtenir un gros fichier au format JSON contenant les données brutes. Pour cela, nous allons faire usage de ce qu'on appelle le web scraping, ou « raclage de toile » en bon français (je plaisante, personne ne dit ça). Cela consiste en l'extraction « sauvage » de données d'un site internet sans passer par une API dédiée. Par exemple, si vous souhaitez suivre l'évolution des prix des appartements sur SeLoger.com ou des masques FFP2 sur Amazon, vous pouvez faire usage du web scrapping.

Ici, nous allons travailler avec Python et ses modules requests et lxml. Ces modules permettent entre autre de récupérer le code HTML d'une page web et de manipuler ensuite ce format facilement.

Par exemple ci-dessous on récupère la page contenant les listes candidates à Brest.

    import requests
    from lxml import html
    
    url = 'https://elections.interieur.gouv.fr/municipales-2020/029/C1029019.html'
    page = requests.get(url)
    tree = html.fromstring(page.content)


L'objet tree peut ensuite être facilement manipulé, toujours avec la bibliothèque lxml, pour extraire les informations qui nous intéressent. Supposons que l'on souhaite récupérer à présent le nom de chacune des listes : on va chercher le « chemin » de l'information dans la page. Ce chemin s'appelle le XPath et indique un emplacement dans le code. Pour l'obtenir, on va user de fonctionnalités un peu avancées du navigateur. Notre travail sera d'autant facilité que les pages ont toutes des formats très simples avec très peu de mise en forme.



Positionnons-nous sur la première liste et voyons « Examiner l'élément » (ça peut aussi s'appeler « Inspecter l'élément » sur d'autres navigateurs). Va alors s'ouvrir dans un encart l'emplacement dans le code de la page où est référencé cette première liste. On va maintenant demander au navigateur le XPath de « VIVRE EN SÉCURITÉ À BREST » (c'est une liste d'extrême droite, je suis désolé).



On obtient le résultat suivant : /html/body/div/div[2]/div[1]/div[4]/div/table/tbody/tr[2]/td[1]/a. On comprend comment cela fonctionne instinctivement, on commence par rentrer dans la balise html, puis dans la balise body, puis div, puis la 2ème balise div, etc. Avec Python, on peut vérifier :

    print(tree.xpath('/html/body/div/div[2]/div[1]/div[4]/div/table/tbody/tr[2]/td[1]/a/text()'))

    # Sur la console :
    # ['VIVRE EN SECURITE A BREST']

Le text() à la fin indique que l'on prend en considération le texte à l'intérieur de la balise a.

De là, il est facile de récupérer les noms des autres listes en faisant varier l'indice après la balise td. td[2] va renvoyer 'MARCHONS POUR BREST !', td[3] indiquera 'BREST A VENIR !', etc.

    len_table = len(tree.xpath(/html/body/div/div[2]/div[1]/div[4]/div/table/tbody/tr)
    for i in range(1, len_table+1):
        path_i = f'/html/body/div/div[2]/div[1]/div[4]/div/table/tbody/tr[2]/td[{i}]/a/text()'
        print(tree.xpath(path_i)[0])

    # Sur la console :
    # VIVRE EN SECURITE A BREST
    # MARCHONS POUR BREST!
    # BREST A VENIR !
    # LUTTE OUVRIERE-FAIRE ENTENDRE LE CAMP DES TRAVAILLEURS
    # BREST LA LISTE CITOYENNE
    # BREST, IMAGINONS DEMAIN
    # BREST ECOLOGIE SOLIDARITES
    # BERNADETTE MALGORN, BREST, C'EST VOUS !
    # DEMOCRATIE COMMUNALE ET LAICITE
    # BREST AU COEUR!



Avec ces quelques principes, on peut construire un code qui va parcourir l'ensemble des départements, puis l'ensemble des villes. De là, si la ville a plus de 1000 habitants, on parcourt les listes candidates et on récupère leurs membres. Dans le cas contraire, on récupère directement les candidats au scrutin majoritaire.

À noter qu'en plus les adresses des pages sont toutes formatées de la même façon, avec le numéro du département, le code INSEE de la ville et le numéro de la liste, ce qui facilite le parcours du site.

Après un peu de gymnastique, on peut aboutir au code suivant qui va construire patiemment notre fichier. Attention cependant si vous souhaitez essayer vous-même, l'exécution est longue. Comptez plusieurs heures. En effet, il faut compter a minima une requête par ville et une requête par liste, ce qui monte déjà à plus de 55 000 appels. Personnellement je m'y suis repris à plusieurs fois pour en venir à bout.

Quant au résultat final, il est disponible ici. Il fait 25,5 Mo, ce qui paraît raisonnable mais est conséquent pour un fichier texte. Comme annoncé, c'est du JSON, avec le format suivant :

{
    "001": {
        "01004": {
            "nom": "Ambérieu-En-Bugey", "mode": "Listes",
            "listes": {
                "L001": {
                    "nom": "UNIS POUR AMBERIEU", "nuance": "LDVC",
                    "candidats": ["M. Daniel FABRE", "Mme Sylvie SONNERY"]
                },
                "L002": {
                    "nom": "AMBERIEU CITOYENNE", "nuance": "LDVG",
                    "candidats": ["M. Antoine MARINO-MORABITO", "Mme Gaëlle FABBRI"]
                }
            }
        },
        "01006": {
            "nom": "Ambléon", "mode": "Candidats (Scrutin majoritaire)",
            "candidats": ["M. Philippe BERJOAN", "M. Jacques BERNE"]
        }
    },
    "002": {
        ...
    }
}



Les villes sont rassemblées par département. Pour chaque commune, on indique le nom et le mode de scrutin (« Listes » pour celles de plus de 1000 habitants ou « Candidats (Scrutin majoritaire) » pour celles de moins de 1000 habitants). Pour les villes soumises au scrutin de listes, on indique pour chaque liste le nom, la nuance politique (uniquement pour les communes de plus de 3500 habitants, c'est el famoso « circulaire Castaner », pour les autres j'ai mis « XXX »). Pour les villes soumises au scrutin majoritaire, pas de liste. On a donc directement les candidats.

À noter que pour les villes de Marseille, Lyon et Paris, divisés en arrondissements et donc tributaires d'un mode de scrutin particulier, j'ai compté chaque secteur comme une commune à part entière, soumise au scrutin de liste. Ainsi, il est indiqué par exemple que le département 75 compte dix-sept « communes », comme autant de secteurs (les arrondissements 1 à 4 sont rassemblés sous l'appellation « Paris centre »).

À présent qu'on a un fichier bien propre sous la main, on peut librement l'exploiter pour essayer d'en tirer quelques enseignements factuels. Pour nous faciliter la tâche, on va utiliser le module json de Python, qui va convertir le contenu du fichier en un objet « dictionnaire » au sens de Python.

Quelques chiffres, donc. Comme j'étais d'humeur joueuse j'ai en plus fait des beaux graphiques avec matplotlib.

35028 villes sont concernées par le scrutin. Parmi celles-ci, 9986 ont plus de 1000 habitants et 25042 on moins de 1000 habitants (c'est énorme, non ?).


900135 personnes sont candidates à cette élection municipales. Parmi elles, on compte 497868 hommes et 402267 femmes.


Dans les villes de plus de 1000 habitants, où la parité des listes est la règle, on trouve 15943 hommes en première place contre 4766 femmes. Mieux encore, dans 6227 communes, il n'y a que des hommes têtes de liste. Ce n'est que dans 867 communes qu'on trouve uniquement des femmes en première position.



20709 listes sont en course. Les listes dans les villes de plus de 3500 habitants sont réparties à travers 23 nuances politiques. Parmi celles-ci, les plus communes sont « Divers gauche » et « Divers droite », quasiment à égalité avec respectivement 2219 et 2218 occurrences, ainsi que « Divers » (1498) et « Divers centre » (1218).


Si on se penche nuance par nuance, on note que la couleur politique la plus « paritaire » est les radicaux de gauche avec 40 % de listes menées par une femme, ce qu'il faut relativiser puisqu'il n'y a que cinq listes avec cette nuance. Derrière, on trouve les listes Europe Écologie-Les Verts, la France Insoumise et d'extrême gauche, toutes au-dessus de 35 %. Globalement, les listes de gauche sont plutôt en avance sur ce point (ce qui est très relatif). En dernier, on trouve le MoDem qui ne présente qu'une seule liste conduite par une femme, sur les 18 qu'elle présente.


110 villes ne présentent aucun candidat. Notable également, dans 3814 villes il n'y a qu'une seule liste candidate, soit 38 % des villes de plus de 1000 habitants.

On trouve le plus de listes à Saint-Paul, à la Réunion, avec 16 listes. En métropole c'est Montpellier qui l'emporte avec 14 listes. Parmi les villes de moins de 1000 habitants, c'est Pontevès dans le Var qui gagne avec 62 candidats, ce qui représente 10 % des inscrits sur les listes locales (si on se fie au résultat des dernières élections européennes).

Si on se penche sur le nom des listes et qu'on regarde les mots les plus courants, on trouve, outre les mots de liaison communs, « ensemble » (5266), « avenir » (2034), « vivre » (1439), « agir » (1241), « demain » (1137), « 2020 » (797), « unis » (606), « continuons » (580) et « autrement » (544) (ces deux derniers dénotant des objectifs bien distincts).


Mieux encore, 9984 listes ont dans leur intitulé le nom de la ville dans laquelle elles se présentent, soit 48 % des listes. À noter également, 380 listes mettent en avant le nom de leur tête de liste.

Les prénoms les plus portés par les candidats masculins sont Philippe, Alain, Michel et Patrick. Pour les femmes, il s'agit d'Isabelle, Nathalie, Catherine et Sylvie. Si on regarde uniquement les têtes de liste, Michel passe devant Philippe et Isabelle garde la première place.


Cela est assez peu étonnant, étant donné que ce sont des prénoms très courants. On peut néanmoins estimer l'âge moyen des candidats, tous ces prénoms ayant été très populaires entre les décennies 1940 à 1960 avant de s'effondrer dans les années suivantes (source Insee). On peut même estimer au vue des graphiques que les femmes candidates doivent avoir en moyenne dix années de moins que les hommes candidats.


Les esprits facétieux noteront le très léger pic de « Philippe » au début des années 1940.









Il faut croire que le renouvellement de la vie politique n'est pas encore pour cette fois-ci.

 * * *

Félicitations d'être arrivés jusque là !

Voilà tout pour cet article, j'ai essayé de vous donner un aperçu de ce qu'il était possible de faire sans trop de difficulté et sans avoir besoin d'un gros niveau en Python. À noter qu'on peut retrouver sur data.gouv.fr, le portail gouvernemental dédié aux données ouvertes, des fichiers similaires à celui que nous avons réalisé, notamment au format CSV. Je trouve personnellement le JSON plus facile à exploiter.

Nous n'avons pas mis au jour de gros secrets dans nos rapides analyses à la fin, on peut néanmoins noter que les femmes continuent d'être marginalisées dans les postes à responsabilité quand la loi ne vient pas imposer leur présence, et qu'au vue des prénoms les plus fréquemment portés par les candidats il faut s'attendre à des maires d'en moyenne une soixantaine d'années.

Vous êtes bien entendu libres de réutiliser les données à votre guise.

Commentaires

Articles les plus consultés