Jean-David Daviet

Coordonnées GPS depuis Google Map sans API avec Puppeteer.

Pour un projet je devais récupérer une liste de coordonnées GPS à partir d’une liste d’adresses, et les exporter ensuite dans un fichier CSV pour une réutilisation facile.

Google Maps semblait me donner les meilleurs résultats et comme je connais bien cet outil, j’ai décidé de récupérer les coordonnées sur cette plateforme. Il donne aussi la possibilité avec le clic droit d’afficher directement les coordonnées GPS d’un point suite a une recherche. Mieux encore, une fois une recherche effectuée, plusieurs paramètres s’ajoutent a l’URL, dont la latitude et la longitude. Ces données sont reconnaissables grâce au symbole @ qui se trouve avant.

De plus, au lieu de faire une recherche manuelle, l’adresse peut aussi être directement mise dans l’URL dans le paramètre d’URL q après google.maps.fr/search/?q=. Cela revient à faire une recherche manuelle dans la barre de recherche.

Plutôt que de mettre en place un compte Google Maps API, de devoir rentrer un numéro de carte bleu avec adresse et tout, puis de chercher comment faire fonctionner l’API, je me suis dit que je pourrais très facilement tester d’utiliser puppeteer pour cette tâche.

Mise en place du plan

Avant de commencer il faut découper la tache en plusieurs étapes :

  1. Récupérer l’URL contenant les coordonnées en fonction d’une adresse.
  2. Extraire ces coordonnées
  3. Insérer les données dans un fichier csv.
  4. Assembler le tout ensemble

1. Récupérer l’URL contenant les coordonnées en fonction d’une adresse.

Puppeteer permet de très facilement ouvrir une page web et en récupérer les informations, de la même manière qu’un utilisateur pourrait utiliser le site. L’avantage c’est qu’au lieu de faire toute cette récupération de données a la main ( pouvant contenir des milliers d’adresses ), on automatise tout le processus et gagne des heures précieuses.

Tout d’abord on installe puppeteer : npm i puppeteer dans un dossier franchement créé pour ce travail, et crée un fichier index.js qui sera le point d’entrée de mon automatisation.

// récupérer l'url de la recherche Google

const puppeteer = require('puppeteer');

(async () => {
  const adressTest = 'Gare Part-Dieu 69000 Lyon';
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(`https://www.google.com/maps/search/${encodeURIComponent(adressTest)}`);

  // permet d'attendre que l'url change pour une url avec les positions coordoonées
  await page.waitForNavigation();

  // La partie qui nous interesse : l'URL contenant les coordoonées GPS
  const url = page.url();
  console.log(url);

  await browser.close();
})();

Le code se lit très bien et la documentation est complète pour l’usage que j’en ai. Le point où il faut prêter attention c’est le page.awaitNavigation(). C’est ce bout de code qui me permet d’attendre que l’URL ajoute les coordonnées. Une fois que la recherche a été effectuée, et l’URL changée je n’ai plus qu’à récupérer l’URL pour procéder à l’extraction des coordonnées. On peut maintenant procéder a l’étape deux.

2. Extraire les coordonnées de l’URL

Comme je sais que parser une URL peut parfois être long a faire a la main surtout quand celle ci utilise des paramètres spéciaux ( le @ ), je me suis dit que quelqu’un avait sûrement dû pu lire une librairie JS pour faire ce travail. Je tombe sur google-maps-parser qui semble faire exactement ce que je demande. Je l’installe : npm i google-maps-parser, puis je teste si le code d’exemple marche ( la librairie datant de 7 ans je n’étais pas très confiant ). Mais miracle elle marche parfaitement. Il faut juste que je l’utilise dans mon code.

PS : Je me rends compte après avoir écris ces lignes, qu’en fait une simple regex aurait suffit à faire le travail directement. D’ailleurs c’est que ce fait cette librairie pour récupérer les coordonnées GPS. Mais j’ai préféré documenter cette méthode car c’est ce que j’ai fait pour avancer au plus vite.

const puppeteer = require('puppeteer');
const CoordParser = require('google-maps-coordinate-parser');

(async () => {
  const adressTest = 'Gare Part-Dieu 69000 Lyon';
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(`https://www.google.com/maps/search/${encodeURIComponent(adressTest)}`);

  // permet d'attendre que l'url change pour une url avec les positions coordoonées
  await page.waitForNavigation();

  // La partie qui nous interesse : l'URL contenant les coordoonées GPS
  const url = page.url();
  
  /** ON PARSE L'URL  */
  const coords = CoordParser.parse(url);
  /** ET RECUPERE LAT ET LONG */
  const [ lat, long ] = coords.toString().split(', ');
  console.log(lat, long);

  await browser.close();
})();

A ce moment le plus gros du travail est fait puisque j’arrive a extraire des coordonnées GPS depuis une adresse sans le faire manuellement. Le reste du travail consiste maintenant surtout à ajouter ces coordonnées à un fichier pour un traitement ultérieur.

3. Insérer les données dans un fichier csv.

NodeJS permet d’inserer des données dans un fichier très facilement :

const fs = require('fs');
fs.appendFile("output.csv", `Donnée sous forme de string à insérer dans le fichier`, () => {});

Pour lire un ficher, c’est très facile. Je n’ai pas besoin de faire ça asynchrone c’est pourquoi j’utilise ici des fonctions NodeJS synchrones.

const fs = require('fs');
fs.readFile('input.csv', 'utf8' , (err, data) => {
  // On à accès à la variable data qui contient les données du fichier
});

4. Assembler le tout ensemble

Nous avons tout ce qu’il faut pour finaliser ce travail et arriver a récupérer ces coordonnées. Voici le code final.

const fs = require('fs');
const puppeteer = require('puppeteer');
const CoordParser = require('google-maps-coordinate-parser');

const getLocation = async ([id, address]) => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(`https://www.google.com/maps/search/${encodeURIComponent(address)}`);

  await page.waitForNavigation();

  const url = page.url();
  const coords = CoordParser.parse(url);
  const [ lat, long ] = coords.toString().split(', ');

  fs.appendFile("output.csv", `"${id}", "${address}", "${lat}", "${long}"\n`, () => {});

  await browser.close();
}

(async () => {
  fs.readFile('input.csv', 'utf8' , async (err, data) => {
    const lines = data.split('\n');
    // prevent reading the last empty new line
    lines.pop();

    lines.forEach((line, key) => {
      const newLine = line.split('","');
      newLine[0] = newLine[0].replace(/^\"/, '');
      newLine[1] = newLine[1].replace(/\"$/, '');
      lines[key] = newLine;
    });

    for(const line of lines){
      await getLocation(line);
    }
  });
})();

On boucle sur la liste d’adresses tirée du fichier input.csv et pour chaque ligne, on lance la fonction getLocation qui va nous récupérer les coordonnées et stoker dans le tableau outputs : l’identifiant unique de l’adresse, l’adresse, la latitude, la longitude. A chaque récupération de coordoonées, j’insère cette ligne directement dans le fichier output.csv
Ensuite, il suffira de traiter le fichier et de l’insérer dans une base de données ( comme c’était mon cas par exemple ).
Il n’y a plus qu’à lancer l’automatisation avec la commande node index.js et regarder avec admiration le fichier CSV se remplir automatiquement tout en sirotant un verre de votre boisson favorite. En quelques dizaines de minutes on a pu automatiser un travail qui aurait pris plusieurs heures à la main ( mon fichier input.csv comprenant 2000 adresses ).