Data mining: ton identité virtuelle ne t’appartient plus

read all books meme

Comment ça ?

 

Depuis le temps qu’on te le dit.

 » Sur Internet, tout est public « 

Au début, tu t’en foutais. Après tout, qui en a vraiment quelque chose à faire de tes photos aux Maldives, de ton nouveau chapeau acheté en soldes ou du screenshot de la partie où tu as poutré-du-noob ? Tu as raison d’écouter la petite voix dans ta tête. Per-sonne.

 

Seulement voilà.

 

Calmes toi cinq minutes et regarde derrière toi en te posant la question toute bête : « Depuis combien de temps est-ce que je traîne sur Internet ?« . Allez, soyons honnête, ça fait sûrement plus de dix piges. Un dixième de siècle de bordel virtuel qui aurait bien besoin d’être un peu rangé. Ou au moins, reluqué de près.

 

Et qu’est ce qu’il vient faire la, le data mining ?

 

Ce sujet, ça te parle ? En -très- gros, il s’agit de l’art de trouver automatiquement des data. Sur n’importe quel sujet. En effet, depuis le début des années 2000, la quantité de data disponible sur Internet se multiplie par [insérer-ici-un-chiffre-badass] tous les ans.

Imagine une étagère remplie de dictionnaires (si tu sais encore à quoi un dictionnaire sert et/ou ressemble) et que le vocabulaire de la langue française se trouverai multiplié par 10 tous les ans. Logiquement, le nombre de dictionnaires sur ton étagère va augmenter, tu suis ?

 

Et le problème se complexifie de plus en plus. Tu vas vouloir utiliser de plus en plus de mots différents, tu vas devoir chercher leur définition dans de plus en plus de dictionnaires .. Chronophage tout ça.

Et bah sur Internet, c’est pareil. Tout le monde laisse traîner des données un peu partout. Et le data mining te permet de récupérer vite (très vite) un type d’info spécifique. Comme des news sur ton équipe de football préférée ou les lois qui sortent au niveau mondial sur le bien-être des marsupiaux. Donc logiquement, tu devrais pouvoir générer assez rapidement des infos sur pas mal de gens. Allez, regarde ce que tu vas faire en moins de 120 lignes de code.

 

Python, au secours !

 

''' IMPORTS '''
import re
import json
import urllib
import string
import requests
from bs4 import BeautifulSoup as bs

 

Déjà, importe les librairies dont tu vas avoir besoin. Parce que oui, les grandes personnes travaillent en Python, alors sèche tes larmes.

 

''' REGEX '''
soupGetName = re.compile('title="(.*)">')
soupGetAdress = re.compile(' (.*)</a></div></div>')
soupGetFamilyName = re.compile('.html">(.*)</a>')
adressZip = re.compile('([0-9]{5})')
copainAvantURL = re.compile('<a href="(.*)"><figure style=')
copainAvantBDay = re.compile('">(.*)</abbr>')
copainAvantScolarity = re.compile(' {32}(.*)\t\t\t\t\t\t\t</a>')
copainAvantProfession = re.compile('<p class="title">(.*)</p>')
copainAVantPicture = re.compile('<meta content="(.*)" property="og:image"/>]')

 

Et compile quelques regex au passage pour t’en servir plus rapidement ensuite. Si tu ne sais pas ce que c’est, je t’invite a te renseigner. Ou si tu es patient, attends un peu, on va parler du sujet ici bientôt, vu la puissance du truc.

 

class search:
    ''' Step 0: initiation '''
    def __init__(self):
        self.frBase = './frBase.json'

 

Allez, plonge dans le vif du sujet. Crée une classe d’objet pour plus de lisibilité. Et parce que l’orienté objet, franchement, ça a plus de gueule. Appelle cette classe search, parce que ça rapelle bien le data mining. Astucieux.

Pas besoin de lui initialiser tout un tas de paramètre, juste un nom de fichier JSON suffira. C’est joli, les outputs en JSON.

 

Mais je recherche quoi au juste ?

 

''' Step 1: create a list of french family names '''
def nameList(self):
    alphaList = list(string.ascii_lowercase)
    familyNamesList = []
    for letter in alphaList:
        toScrap = 'http://www.nom-famille.com/commencant-par-' + letter + '/noms-les-plus-portes.html'
        repFamilyName = urllib.request.urlopen(toScrap)
        rawFamilyNameData = repFamilyName.read()
        fnSoup = bs(rawFamilyNameData, 'html.parser')
        familyNamesToAdd = fnSoup.find_all('a', attrs = {'class': 'nom'})
        for rawFamily in familyNamesToAdd:
            familyName = re.findall(soupGetFamilyName, str(rawFamily))[0]
            familyNamesList.append(familyName)
    print('Family names list loaded with {} names'.format(len(familyNamesList)))
    return familyNamesList

 

Des personnes, mon pote. Donc, la première fonction que tu dois créer, c’est un truc pour nous trouver des noms de famille. Eh oui, en général, c’est la base de l’identité. Donc, suis la tendance, pas de trucs exhubérants et pars de cette base là, toi aussi.

On génère une liste contenant les lettres de l’alphabet avec la fonction string.ascii_lowercase (qui vient du package string que tu as importé au début). Ces lettres vont nous permettre de récupérer sur le site nom-famille les 280 noms de famille les plus courant pour chaque lettre de l’alphabet. Un peu moins de 8000 noms de familles, ça paraît pas mal pour commencer, non ?

Pour ce faire, tu contruis l’URL, tu télécharge la page avec urllib et request, tu la parse avec BeautifulSoup (très pratique pour parser du HTML, au passage) pour récupérer les classes ‘nom‘. Tu enlève les trucs moches qui restent autour des noms avec la regex soupGetFamilyName et HOP. Tu te retrouve avec une liste familyNamesList qui contient les noms de familles français les plus utilisés.

 

La base du sanctuaire des données personelles. L’adresse.

 

    ''' Step 2: get name and actual adress from an official website '''        
    def adress(self, familyNamesList):
        frBaseList = []
        for keyName in familyNamesList:
            # Let's scrap french anuary
            toScrap = 'https://www.pagesjaunes.fr/pagesblanches/recherche?proximite=0&quoiqui=' + keyName
            PjRep = urllib.request.urlopen(toScrap)
            rawPjData = PjRep.read()
            PjSoup = bs(rawPjData, 'html.parser')
            rawPersons = PjSoup.find_all('header', attrs = {'class': 'v-card'})
            # Used to increment our contact ID
            ID = len(frBaseList)
            for contact in rawPersons:
                # People parsing
                contactSoup = bs(str(contact), 'html.parser')
                contactRawName = contactSoup.find_all('a', attrs = {'class': 'denomination-links pj-lb pj-link'})
                try:
                    # Dictionary fill with contact informations
                    personDict = {}
                    personDict['id'] = ID
                    personDict['name'] = re.findall(soupGetName, str(contactRawName))[0]
                    personDict['fullAdress'] = re.findall(soupGetAdress, str(contact))[0]
                    personDict['zip'] = re.findall(adressZip, str(contactAdress))[0]
                except:
                    continue
                # Add person contact to our baselist
                frBaseList.append(personDict)
                ID += 1
        print('PagesJaunes scraped for {} different profiles'.format(len(frBaseList)))
        return frBaseList

 

Deuxièmement, essaye d’avoir un peu de crédit et sers toi d’un site un minimum officiel pour essayer d’avoir quelque chose de fiable. Cocorico, les PagesJaunes.

Pour chaque nom de famille de la liste extraite par la fonction précédente, on construit une URL pour rechercher ce nom sur le site. Tu vas devoir ensuite scraper et parser la page de résultats, comme au dessus.

Tu récupère donc d’ici pour chaque nom de famille différent une liste de personnes (les gens qui partagent ce patronyme en France et ne sont pas sur liste rouge) qui sont contenues sur le site dans un bloc header {class : v-card}.

Maintenant, parcours ce truc et grâce aux regex soupGetName, soupGetAdress et adressZip, tu vas choper respectivement le nom, l’adresse et le ZIP de chaque personne.

Tout ça va finir dans un dictionnaire personDict. Tu dois aussi agrémenter tout ça d’un ID pour chaque personne, c’est l’usage et c’est tout.

Pour le moment, tu as donc une liste frBaseList qui contient pour chaque personne un truc qui ressemble à ça :

 

{
    'id' : 12
    'name' : 'Jean Luc Sirotant'
    'fullAdress' : '12 rue des Assoiffés, 58211, Poil'
    'zip : '58211'
}

 

Bon, je suis d’accord, tu vas pas débarquer sur la première de couverture de Médiapart avec ça. Mais c’est un début.

 

Plus profond dans ton intimité. Les réseaux sociaux.

 

Ah, les social networks, sacro-sainte terre des likes et autres auto-congratulations en tous genres, dois-tu te dire. Tu as raison, chef, mais quand tu poste quelque chose sur un site, je suis sûr que tu te dis :

 » Bon, allez, je leur file des données à eux, mais ça reste sur leur serveurs « 

Alors, premier fist-fucking dans ton oreille gauche : le marché des data. Un graphique parle de lui-même.

data mining _ market_data_france

Eh oui ma puce. Miliards. Donc dis toi bien que le post de la photo du dernier concert où tu as été, il paye la place de concert de celui qui va récupéré les data engendrées par ladite photo.

Et deuxième fist-fucking dans ton oreille droite, la protection des données. Sur la majorité des sites, elle est comme une partouze au Vatican. Il n’y en a pas. Tout simplement.

 

''' Step 3: scrap social media to complete '''
def social(self, frBaseList):
    frSOcialList = []
    # How many people are in CA
    count = 0
    for person in frBaseList:
        # Clean unsupported characters for URL construction
        nameCleaned = re.sub('[éèê]', 'e', person['name'])
        nameCleaned = re.sub('[ïî]', 'i', nameCleaned)
        toScrap = 'http://copainsdavant.linternaute.com/s?full&q=' + re.sub(' ', '+', nameCleaned) + '&ty=1'
        caRep = urllib.request.urlopen(toScrap)
        caRawData = caRep.read()
        caSoup = bs(caRawData, 'html.parser')
        rawPersonList = caSoup.find_all('div', attrs = {'class': 'grid_line gutter grid--norwd'})
        for copain in rawPersonList:
            copainURL = 'http://copainsdavant.linternaute.com' + re.findall(copainAvantURL, str(copain))[0]
            copainPageRep = urllib.request.urlopen(copainURL)
            copainPageRaw = copainPageRep.read()
            copainSoup = bs(copainPageRaw, 'html.parser')
            # Birthday
            rawBirthDay = copainSoup.find_all('abbr', attrs = {'class': 'bday'})
            try:
                person['birthDay'] = re.findall(copainAvantBDay, str(rawBirthDay))[0]
                count += 1
            except:
                person['birthDay'] = 'N/A'
            # Actual profession
            rawProfession = copainSoup.find_all('p', attrs = {'class': 'title'})
            try:
                person['profession'] = re.findall(copainAvantProfession, str(rawProfession))[0]
            except:
                person['profession'] = 'N/A'
            # Scolarity places
            rawScolarity = copainSoup.find_all('a', attrs = {'class': 'jTinyProfileEtab notip jCareerLabel'})
            try:
                person['scolarity'] = re.findall(copainAvantScolarity, str(rawScolarity))
            except:
                person['scolarity'] = 'N/A'
            # A picture
            rawPictureURL = copainSoup.find_all('meta', attrs = {'property': 'og:image'})
            try:
                person['picture'] = re.findall(copainAVantPicture, str(rawPictureURL))[0]
            except:
                person['picture'] = 'N/A'
        frSOcialList.append(person)
    print('CopainAvant extracted more information for {} of the {} persons in total'.format(count, len(frSOcialList)))
    return frSOcialList

 

Donc profite de ça pour ton projet.

Ainsi, pour chaque personne de ta précédente liste (celle que tu as récupéré sur PagesJaunes), il faut construire une URL pour interroger le site copainsdavant en GET.

Puis scraper la page de résultats pour voir si on a des résultats (tout le monde n’est pas sur ce site).

Quand il y en a, tu peux suivre les liens de ces résultats (copainURL). Et dans cette page, qui se trouve être le profil de la personne, utilise les regex compilées au début pour trouver des trucs intéressants après avoir, une fois n’est pas coutume dans le data mining, scrapé tout le contenu.

FInalement, sur une unique page, tu as ici accès à la date de naissance, la profession actuelle, la liste des établissements scolaires fréquentés et une URL contenant une photo. Ça commence a devenir interessant là.

 

{
    "name": "Jean Luc Sirotant",
    "zip": "58211",
    "fullAdress": "12, rue des Assoiffés, 58211, Poil",
    "birthDay": "17 janv. 1979",
    "scolarity": [
        "Ecole Marceline Machin",
        "Ecole Léon Truc",
        "Lycée Européen Monteboule",
        "Collège Madame De Ruerouge",
        "Université Franck Provost"
    ],
    "id": 27,
    "profession": "Dézingueur de toitures",
    "picture": "http://un_lien_copain_davant.jpg"
}

 

Et tout ça avec seulement trois sites. Un pour récupérer des noms de famille, les PageJaunes pour avoir des adresses et des noms réels et copainDavant. Rien ne t’empêche d’allonger la liste pour récupérer plus d’infos.

 

Sauvegarder et éxécuter dans l’histoire ?

 

''' Last step : print result in a json file '''	
def outPrint(self, toPrint):	
    jsonEncoded = json.dumps(toPrint, indent=4)
    frBaseFile = open(self.frBase, 'w')
    frBaseFile.write(jsonEncoded)
    frBaseFile.close()

 

Une dernière petite fonction qui va utiliser la librairie json pour encoder notre liste en JSON et te l’enregistrer dans le fichier spécifié dans l’initiation de la classe search, au début.

 

''' EXECUTION '''
search = search()
# Search for famili names
familyNamesList = search.nameList()
# Search for adress
frBaseList = search.adress(familyNamesList)
# Search for social infos
frSOcialList = search.social(frBaseList)
# Print out in a file
search.outPrint(frSOcialList)

 

Et finalement, l’éxécution de tout ça.

 


 

Bien. Tout ça pour dire quoi ?

En 120 lignes, on arrive a récupérer les infos personelles de beaucoup de personnes (145 000 environ). Mais pour en faire quoi me diras-tu ?

Et bien, si tu as envie de, par exemple, télécharger en masse toutes les photos, appliquer dessus un algorithme de machine learning pour reconnaître les femmes blondes, brunes et rousses et regarder leur distribution sur le territoire français grâce au code postal, c’est possible.

Tu peux ensuite générer une carte que tu peux finalement vendre à un fabriquant de teinture pour cheveux afin qu’il cible sa campagne publicitaire.

 

 

Allez, sois inventif, mon petit.

 

 

PS : Le code est sur Github. Moi non plus, je n’aime pas faire cinquante copier-coller.

 

Laisser un commentaire