Le Google Assistant du pauvre …

En ces temps de confinement (et de chômage partiel …), rien de tel que de continuer sur le projet de robotique.
Depuis le départ, je souhaite que le robot puisse converser et soit doté de connaissances (genre culture générale). Pour l’agent conversationnel, plusieurs outils ou webservices sont disponibles sur le net : DialogFlow, IBM Watson Assistant, Amazon Lex, … Pour ma part, j’ai testé DialogFlow qui se révèle être très prometteur et que je détaillerai peut-être dans un prochain post. Mais ces systèmes sont limités aux intentions que vous leur apprenez, c’est à dire qu’ils ne sauront répondre qu’aux questions / phrases (et leurs variantes) préalablement définies dans votre agent conversationnel. Ex :
– Utilisateur : Bonjour
– Robot : Salut. Comment tu t’appelles ?
– Utilisateur : Nicolas et toi ?
– Robot : Enchanté Nicolas, je m’appelle Wall-E

Mais, si l’on souhaite poser des questions de culture générale, cela devient beaucoup plus complexe. Le nombre de connaissances étant infini (du moins, extrêmement grand), le nombre d’intentions à prévoir pour l’agent conversationnel devient lui aussi énorme. Des solutions existent : Google Knowledge Graph Search, WolframAlpha, … Mais avant de m’orienter vers ces systèmes nécessitant des développements ardus (traitement automatique de la langue, extraction de l’entité principale, extraction de l’intention, …), je me suis demandé s’il n’y avait pas une solution plus simple et moins coûteuse.
Et pourquoi la solution ne viendrait pas d’une simple recherche Google ??? En effet, lorsque vous tapez la question « Quel était le président français en 1900 ?« , « Qui est Elon Musk ? » ou « Combien font 22 * 68 ?« , vous obtenez ces résultats :


Les réponses pertinentes apparaissent en haut ou à droite des résultats et correspondent au Knowledge Graph de Google.
En analysant un peu le contenu de la page de résultats, avec une simple recherche Google et un bon parser HTML (JSoup), j’ai réussi en peu de lignes de code à extraire les réponses pertinentes et à créer un pseudo Google Assistant. J’ai même pris en compte les questions pour demander la météo.

Voici le code JAVA permettant de saisir une question et d’afficher la réponse (le fameux Google Assistant du pauvre !) :

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.safety.Whitelist;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class GoogleSearchParser {

    public static void main(String[] args) {

        GoogleSearchParser gsp = new GoogleSearchParser();
        Scanner scanner = new Scanner(System.in);
        System.out.println("Ask a question : ");
        String query = scanner.nextLine();
        while (!"q".equalsIgnoreCase(query)) {
            String response = gsp.getResultsFromGoogle(query);
            System.out.println("Response = " + response);
            System.out.println("Ask a question : ");
            query = scanner.nextLine();
        }
        System.out.println("Exit");
    }

    private String getResultsFromGoogle(String query) {

        try {
            String encodedQuery = URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
            Document doc = Jsoup.connect("https://www.google.com/search?q=" + encodedQuery).get();
            // Extract relevant information in "knowledge" tags
            // Weather infos
            Element element = doc.select("[id=\"wob_wc\"]").first();
            if (element != null) {
                return processWeatherInfos(element);
            }

            element = doc.select("[data-attrid], [data-tts=\"answers\"], [id=\"cwos\"]").not("[data-attrid=\"image\"]").first();
            if (element != null) {
                // Clean all HTML tag of the selected node
                Document cleanDoc = Jsoup.parse(Jsoup.clean(element.html(), Whitelist.none()));
                return cleanDoc.text();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String processWeatherInfos(Element weatherElement) {
        // Location
        String location = weatherElement.select("[id=\"wob_loc\"]").text();
        // Time
        String timestamp = weatherElement.select("[id=\"wob_dts\"]").text();
        // Weather
        String weather = weatherElement.select("[id=\"wob_dc\"]").text();
        // Temperature
        String temperature = weatherElement.select("[id=\"wob_tm\"]").text();

        return String.format("A %s, %s, le temps est %s, avec une température de %s °", location, timestamp, weather, temperature);
    }

La dépendance Maven pour JSoup :

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.13.1</version>
</dependency>

Et un exemple d’exécution :

Ask a question : 
Qui est le président français en 1900 ?
Response = Émile Loubet
Ask a question : 
Qui est Elon Musk ?
Response = En juin 2002, désabusé par la NASA, qu'il estime manquer d'ambition, Elon Musk fonde sa troisième société, Space Exploration Technologies (SpaceX), dont il est l'actuel CEO et CTO.
Ask a question : 
Combien font 22 * 68 ?
Response = 1496
Ask a question : 
Combien mesure Emmanuel Macron ?
Response = 1,77 m

Bien sûr, cette solution peut être grandement améliorée : certaines réponses ne correspondent pas tout à fait à la question initialement posée car le programme ne récupère pas forcément la bonne information. De plus, elle est soumise au bon vouloir de Google qui peut changer à tout moment la structure des balises. Et enfin, cette solution n’est peut-être pas forcément très légale, ça reste de l’aspiration de données et on contourne les API et par la même occasion, les limites et autres quotas.

Source Github

2 commentaires :

  1. Sympa.. il te reste plus qu a recuperer le fichier audio de la reponse sur google text-to-speech et ton wallE te repondra 😉

    • Salut Juan,
      La mise en place de la synthèse est déjà faite 😉 Ca marche bien. J’utilise effectivement le text-to-speech de Google lorsque j’ai besoin de synthétiser du texte (dans ce cas notamment). Lorsque j’utilise DialogFlow, je peux directement récupérer un flux audio en réponse. Ensuite, j’effectue un traitement audio de mon côté pour « robotiser » un peu la voix et essayer de m’approcher de celle de Wall-E.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *