Générer du contenu grâce à Youtube et à la transcription interactive

Référenceur que tu es, tu as toujours besoin de plus de contenu pour positionner tes sites. Seulement voilà, le contenu, ça coûte cher à la longue. J’avais déjà donné une astuce pour ajouter du contenu sur les pages de vos produits Amazon mais sous forme d’image. C’était plus pour se faire bien voir par le visiteur et pour améliorer son CTR. Cela n’apportait pas vraiment de plus-value pour Google.

L’astuce du jour va utiliser la fonctionnalité ‘transcription interactive’ de Youtube. C’est une fonctionnalité qui n’est pas si récente que ça. Lorsqu’un utilisateur upload une vidéo, il a la possibilité d’uploader également un ‘transcript’ ou ‘timed text’. C’est tout bêtement les sous-titres de la vidéo pour les personnes malentendantes. Aux Etats-Unis, Google peut générer automatiquement ce script suivant les cas. Or cette fonctionnalité s’est vue améliorée : la transcription interactive est automatique pour 6 nouvelles langues, dont, vous l’aurez deviné, le français. Je me suis dit que c’était le moment d’exploiter cette fonctionnalité.

youtube transcript

Seulement voilà, comme le dit le proverbe ‘La chance aide parfois, le travail toujours’ et sur ce coup, pas de chance. Les sous-titres sont chargés en Ajax et Youtube ne propose pas de fonction toute prête pour interroger son API pour récupérer les transcripts de n’importe quelle vidéo. Un membre de l’équipe Youtube a même pris la peine de nous éclairer sur ce point :

‘What you’re describing is not a supported method of accessing data from YouTube, please don’t do that. You should be using the YouTube Data API if you want to access caption data for a specific video, and the caption data is only available if you are authenticated as the owner of that video.’

Algorithmiquement parlant, ce que je devais faire se résume en quelques étapes :

  1. Faire une recherche Youtube sur le mot clé de la thématique de mon site
  2. Prendre une page au hasard et récupérer les id des vidéos
  3. Regarder les vidéos les unes après les autres pour vérifier si le transcript est disponible
  4. Récupérer le contenu du transcript jusqu’à atteindre la taille de contenu désirée
  5. Si je n’ai pas atteint la taille désirée, je prends une autre page au hasard, et ainsi de suite

Points techniques :

  • On peut ne récupérer que 1000 vidéos par recherche au maximum. Il y a 20 vidéos par page, soit 50 pages de résultats possibles (même si Youtube vous affiche 450 000 vidéos qui répondent à votre recherche). De ce fait, je fais scraper une page parmi les 50 au hasard.
  • J’ai utilisé la puissance du multithreading de cURL pour scraper les 20 vidéos de chaque page dans la foulée.
  • Je me base sur l’url de la page pour mettre en cache le contenu du scrap. Si vous avez un site avec plusieurs pages de la même thématique, le script va générer un texte pour chaque page et le mettre en cache.

Points d’amélioration :

  • La recherche est actuellement en dur dans le script. A terme, il serait intéressant d’appeler le script avec une recherche dynamique en fonction de la page sur laquelle vous vous situez (piste : utilisez les tags ou le titre de la page par exemple). Même chose avec la taille du contenu désirée.
  • Le scrap peut contenir dans de rare cas un contenu dupliqué. Le contenu du transcript représente ce que les personnes disent dans la vidéo. Or de temps en temps, ce texte est préparé et donc mis dans la description de la vidéo.

Code source :

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <?php

        $search = "immobilier"; // recherche youtube : immobilier, immobilier paris,...
        $content_length = 500; // longueur minimume de contenu désirée en nombre de caractères

        $useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11";
        $referer = "http://www.youtube.com/";

        if (file_exists("cache/" . md5(currentUrl()) . ".txt") && filesize("cache/" . md5(currentUrl()) . ".txt") > $content_length) {
            $fh = fopen("cache/" . md5(currentUrl()) . ".txt", 'r');
            $content = fread($fh, filesize("cache/" . md5(currentUrl()) . ".txt"));
            fclose($fh);
            echo $content;
        } else {
            $content = "";
            while (strlen($content) < $content_length) {
                // array des vidéos
                $search_result = getVideos($search, $useragent, $referer);
                $content .= getTranscripts($search_result, $useragent, $referer);
            }
            $fh = fopen("cache/" . md5(currentUrl()) . ".txt", 'w') or die("Pb avec le fichier");
            fwrite($fh, $content);
            fclose($fh);
            echo $content;
        }

        /*
            FONCTIONS
        */
        function getVideos($search, $useragent, $referer)
        {
            // max 1000 resultats par requete soit 50 pages de 20 résultats
            $res = scrap("http://www.youtube.com/results?search_type=videos&search_query=" . urlencode($search) . "&page=" . rand(1, 50), $useragent, $referer);
            $html = new DOMDocument();
            @$html->loadHTML($res);
            $xpath = new DOMXPath($html);

            $aTag = $xpath->query("//*[@id='search-results']/li/div/h3/a");
            $finaloutput = array();
            foreach ($aTag as $url) {
                $finaloutput[str_replace('/watch?v=', '', $url->getAttribute('href'))] = utf8_decode($url->nodeValue);
            }
            return $finaloutput;
        }

        // fonction de scrap simple
        function scrap($url, $useragent, $referer)
        {
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
            curl_setopt($ch, CURLOPT_REFERER, $referer);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
            $result = curl_exec($ch);
            curl_close($ch);
            return $result;
        }

        // nettoyage du fichier de sous-titre
        function cleanTranscript($script)
        {
            return str_replace("\n", ' ', rtrim(strip_tags($script)));
        }

        // récup&ègrave;re l'url courante
        function currentUrl()
        {
            return $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        }

        // récupération des sous-titres
        function getTranscripts($nodes, $useragent, $referer)
        {
            set_time_limit(90);

            $curl_arr = array();
            $master = curl_multi_init();
            $i = 0;

            foreach ($nodes as $key => $value) {
                $curl_arr[$i] = curl_init("http://www.youtube.com/api/timedtext?lang=fr&format=1&kind=&hl=fr&type=track&v=" . $key);
                curl_setopt($curl_arr[$i], CURLOPT_FOLLOWLOCATION, true);
                curl_setopt($curl_arr[$i], CURLOPT_USERAGENT, $useragent);
                curl_setopt($curl_arr[$i], CURLOPT_FRESH_CONNECT, true);
                curl_setopt($curl_arr[$i], CURLOPT_CONNECTTIMEOUT, 10);
                curl_setopt($curl_arr[$i], CURLOPT_RETURNTRANSFER, true);
                curl_setopt($curl_arr[$i], CURLOPT_REFERER, $referer);
                curl_setopt($curl_arr[$i], CURLOPT_TIMEOUT, 30);

                curl_multi_add_handle($master, $curl_arr[$i]);
                $i++;
            }

            $previousActive = -1;
            $finalresult = array();
            $returnedOrder = array();
            do {
                curl_multi_exec($master, $running);
                if ($running !== $previousActive) {
                    $info = curl_multi_info_read($master);
                    if ($info['handle']) {
                        $finalresult[] = cleanTranscript(html_entity_decode(curl_multi_getcontent($info['handle'])));
                        $returnedOrder[] = array_search($info['handle'], $curl_arr, true);
                        curl_multi_remove_handle($master, $info['handle']);
                        curl_close($curl_arr[end($returnedOrder)]);
                        ob_flush();
                        flush();
                    }
                }
                $previousActive = $running;
            } while ($running > 0);

            curl_multi_close($master);

            $res = "";
            foreach ($finalresult as $fr) {
                $res .= $fr . " ";
            }

            set_time_limit(30);
            return $res;
        }

        /*
            FIN FONCTIONS
        */

    ?>
</body>
</html>

Voici un exemple de contenu scrapé via Youtube :

scrap youtube

Voilà à quoi ressemble mon dossier :

Télécharger le projet : cliquez ici

Merci de liker et plusser l’article si vous compter utiliser cette astuce ;) et attention, je ne suis pas responsable de ce que vous en ferez. Ceci est un article à but didactique.

Affiliation Amazon et contenu dupliqué : la parade
Mémo des formulaires Google : disavow, spam report, pishing...

20 Comments on “Générer du contenu grâce à Youtube et à la transcription interactive”

  1. Et ba putain, ça c’est de l’astuce, je savais même pas qu’on pouvait y afficher, pourtant je suis en ricain sur youtube. J’avais pourtant chercher un ocr vocal histoire d’avoir du contenu. En tout cas merci pour le script, j’ai un petite idée pour l’utiliser d’ailleurs =)

  2. Super astuce !!!! merci beaucoup. Ca relance les dès pour la création de contenu.
    Par contre j’ai un problème « Warning: domdocument() expects at least 1 parameter, 0 given in » ligne 38

    Merci en tout cas

    1. Passe en PHP5, ça devrait le faire. Et il y a cette erreur sous PHP5 (chose rare), c’est à cause d’une vieille extension de domxml. Je présume que tu l’as testé en local sur un vieux wamp ou easyPHP ?

  3. Excellent, ca laisse sans voix, bravo pour l’astuce, du speech to text, une tendance à venir, demain ce seront les appels téléphoniques qui pourront devenir du contenu texte également.

  4. Et bien putain ça c’est du partage :D

    Tu as effectivement bien jauger notre talent pour la feignantise et l’attrait pour le contenu. Voilà un truc auquel je n’aurais pas penser et qui fait bien envie, je vais du coup tester ta technique pour voir si ça peut allez sur des blogs satellite sans être trop crade.

    En tout cas c’est en effet de l’astuce, merci bien comme le dit Soul ça peut servir pour pas mal de chose ce petit code !

  5. C’est très bon ça ! Merci pour l’astuce je vais essayer de m’en servir prochainement… Je garde ton article sous la main ! En plus ton code source va me permettre d’améliorer mes connaissances en CURL :) Pas mal aussi cette fonction de 25 mots pour un backlink ! C’est un plugin ou c’est toi qui a développé cette fonctionnalité ? Ça m’intéresse beaucoup aussi !

    1. Effectivement, au même titre que de prendre du contenu chez un concurrent ou d’utiliser une image sans en demander l’autorisation. J’ai ajouté une petite ligne en fin d’article suite à ton commentaire.

      1. Tu as ajouté une ligne visant à te protéger juridiquement, et je crains qu’en cas de poursuites, elle ne serve pas. En effet, ton article offre des moyens industriels de pirater du contenu, et ton article incite très clairement à ce faire. Je t’invite à réfléchir aux conséquences face à l’industrie du disque, notamment. Celle-ci a depuis plusieurs années déjà ciblé les sites de paroles de chansons, y compris sur le plan légal.

        Sous prétexte de « je peux le faire », un peu trop de référenceurs exploitent des « astuces » techniques dans l’espoir d’une impunité ressentie. Et il est vrai que les faits leur donnent souvent raison, à savoir que les poursuites sont rarissimes. Toutefois, lorsque poursuites il y a, les dégâts peuvent q’avérer importants. Et il est dommage qu’un professionnel de la visibilité sur Internet, qui plus est un cadre, incite à ce point les lecteurs naïfs à enfreindre la loi.

        Il y a des lignes blanches à ne pas franchir, pour gratter quelques places sur les moteurs de recherche. La loi en est une.

    1. Avant d’utiliser cette technique à grande échelle, il faudrait faire un test SEO pour voir les effets de l’utilisation des contenus générés par Youtube (Google) pour voir si ça pose problème de se positionner dessus.

  6. Merci pour l’article et pour cette astuce. Je pense que l’on va voir fleurir de nouveaux sites avec ce type de contenu certes de faible qualité mais unique et facilement automatisable.

    Il fallait y penser ;)

    Au plaisir de vous lire.

  7. Même si cette technique est d’après moi vraiment géniale, je suis assez d’accord avec Martin, c’est assez risqué quand même (ou pas?) mais je ne vais pas tenter le diable ;-). Quoi qu’il en soit je dis chapeau bas, il fallait le savoir, y penser, surtout le mettre en pratique et puis avoir une assez bonne âme pour partager cette astuce :-)

  8. Salut,
    hormis peut-être effectivement le côté border-line comme expliqué par @Martin1975 d’un point de vue légal, je trouve cette astuce d’un point de vue idée et technique carrément géniale : bravo.

    Je suis une bille en dev, mais je me posais une question : ne serait-il pas judicieux (si c’est techniquement possible), ne stocker quelque part les ID des vidéos sur lesquelles le script est déjà passé afin d’éviter de repasser dessus lors d’un prochain scrap et ainsi éviter de générer du contenu dupliqué ?

    A te lire

    1. Salut,
      Oui effectivement. Le script peut être optimisé sur beaucoup de points.
      Mais attention car selon la requête, un nombre variable de vidéos peut avoir les sous-titres. Il faut passer parfois sur 30 vidéos pour avoir un textes de 500 caractères minimum. Donc plus vous générez de textes, plus la réserve de vidéos diminue, or tu es limité à 1000 vidéos par recherche. Tu peux arriver relativement vite au bout.

  9. Bonjour,

    Je viens de tomber sur ton article.
    Merci pour le partage.

    Si votre but est de bien indexer ces pages crées avec du contenu de vidéos youtube qui ne vous appartiennent pas il y a quand même de grosses interrogations

    S’il existent de transcriptions pour ces vidéos ça veut dire que google va pouvoir vérifier si le texte a été réutilisé dans un article crée par la suite. Ça fait du duplicate content et ton article ne sera pas bien positionné.

    J’ai cru lire quelque part que désormais toutes les vidéos avaient une transcription automatique….Autant dire que ton script ne sert plus à grand chose à part si vous souhaitez réécrire le texte..

    Que pensez-vous de ma remarque?

    1. Hello Seb,

      En fait, pas tout à fait. Le but premier de cette article était de permettre la génération d’une quantité donnée de page sur une thématique spécifique.
      La transcription n’est indexée nul part. Elle est juste présente en base de donnée sur les serveurs de Google.
      Je ne suis pas sûr que Google fasse déjà le rapprochement entre une page dont le contenu est la transcription générée automatiquement par ses soins et la transcription en elle-même.

      Le script ne sert pas à générer une transcription mais à récupérer une transcription existante. Le fait que les transcriptions se démocratisent permet justement d’utiliser cette technique. Un américain l’a d’ailleurs commercialisée.

      Par contre, je ne suis pas certain que mon script fonctionne toujours comme l’interface a pas mal changé depuis…

Laisser un commentaire

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