Automatisation complète de mon extracteur de commentaires YouTube

Je me suis rendu compte, à force de laisser rouler mon extracteur de commentaires pendant plusieurs heures, voire plusieurs jours, qu’il finit toujours par y avoir une erreur qui le fait crasher, malgré tous les niveaux de robustesse que j’ai tenté de lui inculquer. Je pense que c’est dû en partie à la mémoire vive qui a besoin d’un petit sommeil de quelques instants, le temps de repartir à zéro. En tout cas, je me suis dit qu’un reboot de temps en temps ne doit pas faire de mal à ce pauvre ordi qui subit tous mes scripts plus ou moins legit.

Afin de me débarrasser de la nécessité de brancher un écran pour que firefox fonctionne, j’ai commencé par installer un serveur VNC, en suivant les instructions suivantes. C’est un guide très complet, qui explique également comment faire pour que le serveur VNC se mette en marche tout seul au démarrage. Il est fait pour debian, mais j’ai pu l’adapter sans problème pour Fedora.

Par la suite, j’ai essayé de démarrer mon programme classique d’extracteur de commentaires depuis le bureau virtuel sur lequel je me suis connecté. Ça n’a pas marché, je me suis rendu compte qu’il y avait encore un problème de PATH avec mon geckodriver (je l’avais placé dans ~/bin), il ne le trouvait pas puisque j’étais à l’intérieur du bureau virtuel, pour une raison ou une autre. J’ai donc placé le geckodriver dans /usr/bin, et ça a réglé le problème.

Par la suite, j’ai modifié mon programme pour qu’il se connecte d’abord sur une immense playlist de vidéos YouTube, qu’il enregistre où il est rendu dedans et qu’il commence avec la première vidéo qu’il n’a pas déjà en mémoire, c’est comme un immense générateur de « seed » pour mon crawlbot, ça lui fait des milliers de points de départ différents, tous rassemblés sur une seule page web.

Une fois tout cela testé, j’ai voulu le rendre automatique au démarrage, lorsque l’ordi boote, qu’il lance le crawler une fois que le serveur VNC est parti. C’est là que les problèmes ont commencé. J’ai essayé toutes sortes d’approches, comme d’inclure un appel à mon script python à même le service du VNC… sans grand succès. Ce qui a fini par fonctionner, toutefois, c’est de créer une crontab utilisateur avec ceci à l’intérieur :

@reboot python /home/fred/Documents/python/youtube_com_autoextract.py >> /home/fred/Documents/python/out.log 2>&1

Puisque le serveur VNC se logue directement à l’utilisateur fred de manière automatique, sans qu’il n’y ait besoin d’entrer le mot de passe associé, cela fonctionne, mon script python est lancé. Toutefois, j’obtenais toujours l’erreur récurrente de Selenium qui dit grosso modo que firefox n’a pas d’interface graphique. Je me suis buté à cette erreur un bon bout de temps, puisque lorsque je lançais moi-même exactement le même script depuis le bureau virtuel, tout fonctionnait bien. À force de me promener sur internet, j’ai fini par trouver la réponse ici : il faut spécifier dans le script python quel display on utilise, sinon ça doit sûrement être la valeur par défaut qui embarque, qui est sans doute nulle lorsque le script est lancé automatiquement depuis le crontab. J’ai donc simplement rajouté la ligne :

os.environ[‘DISPLAY’] = ‘:1’

après avoir mis un import os.

Cela fonctionne, puisque mon serveur VNC crée le display virtuel 1, donc la sortie de mon programme python, le browser firefox automatisé, est redirigé sur ce display.

Dernière chose mais non la moindre, je voulais également que mon programme envoie une commande de reboot lorsqu’il crashe. J’ai essayé le os.system(‘sudo shutdown -r now’), tel que trouvé ici, sans succès, l’erreur dit qu’il n’y a ni console ni mot de passe de fourni par l’utilisateur, ce qui est assez vrai.

En fouillant un peu, j’ai trouvé qu’il était possible de désactiver la demande de mot de passe pour n’importe quelle commande avec visudo (j’ai trouvé l’information ici). J’ai essayé avec %user ALL=(ALL) NOPASSWD:/usr/bin/shutdown, ça n’a pas marché. Finalement, je me suis rendu compte que c’était mieux d’utiliser systemd avec la commande systemctl pour lancer la procédure de reboot. J’ai trouvé l’information ici :

user hostname =NOPASSWD: /usr/bin/systemctl reboot

En remplaçant user et hostname par mes valeurs, dans le visudo. En mettant simplement la ligne

os.system(‘sudo systemctl reboot’)

dans un except qui ramasse toutes les erreurs non identifiées de mon script en python, le redémarrage se fait automatiquement lui aussi, sans crasher à cause d’une demande de mot de passe. Tout fonctionne? Je viens de le partir, pour l’infini ou presque on l’espère, ça reste à voir! Mais cette fois-ci, c’est une araignée aux pattes d’acier, je vois mal ce qui va pouvoir la tuer, mis à part une panne de courant.

Bug dans mon extracteur de commentaires

Il y a des fois où on ne comprend vraiment pas ce qu’il se passe dans l’exécution d’un programme. Dans ma boucle principale, j’ai rajouté une section qui regarde s’il y a un problème avec le navigateur :

while 1:
    next_url = PageScraper(current_url)
    current_url = next_url
    if not current_url:
        driver.close()
        driver = webdriver.Firefox()
        current_url = [‘https://www.youtube.com/watch?v=…’]
        print « Le crawler est reparti! »

Or, ce qu’il s’est passé, c’est que cette partie du code s’est exécutée avant qu’il y ait une exception dans ma fonction PageScraper (autrement dit, alors que tout devait encore fonctionner). Qu’est-ce qui a bien pu se produire pour que curent_url soit nul? C’est vraiment complètement mystérieux.

Pour l’instant, je pense que je vais abandonner cette magie noire et simplement faire un bash script pour redémarrer l’ordi ainsi que mon programme automatiquement à un certain intervalle donné de temps, afin de permettre à la mémoire vive de se reposer un peu.

Pour rajouter de la robustesse, il faudrait aussi que j’essaie de faire en sorte que mon programme puisse détecter la version de YouTube (ancienne ou nouvelle) et qu’au lieu d’essayer de faire quoi que ce soit lorsque le navigateur bogue, qu’il redémarre tout au complet, en itérant le fichier de sortie.

J’ai rajouté un serveur VNC aussi (en suivant les instruction ici), pour pouvoir voir ce qui se passe à distance sans avoir à brancher un écran! Ça peut toujours être pratique, et probablement que je vais en avoir besoin de toute manière pour pouvoir faire marcher firefox sans écran physique connecté à l’ordi.

Ajout de robustesse pour mon extracteur de commentaires sur YouTube

Je vais bientôt faire une publication complète de ce projet dans une page à part, lorsque je serai prêt à le présenter au monde. En attendant, je vais simplement parler de l’ajout que j’ai effectué aujourd’hui. L’erreur que j’ai réglée était un problème de chargement de la page lorsque Firefox ne reconnaît pas le format vidéo. Il s’agit d’un problème avec HTML5 qui se produit avec quelques rares vidéos, mais statistiquement mon bot finit toujours par tomber dessus. YouTube affiche alors simplement une page blanche avec une boîte vidéo qui griche, impossible d’avoir accès aux autres suggestions de vidéos ou aux commentaires. Mon programme, qui fonctionne en extrayant les url des vidéos suggérées pour naviguer sur YouTube indéfiniment, cesse donc de fonctionner. Pour corriger cela, j’ai rajouté l’exception suivante :

last_urls = urls[1:len(urls)]
try:
    elements = driver.find_elements_by_css_selector(« a.yt-simple-endpoint.style-scope.ytd-compact-video-renderer »)
    urls = []
    for element in elements:
        urls.append(element.get_attribute(« href »))
except:
    urls = last_urls

Ainsi, à chaque vidéo, mon programme n’extrait non plus simplement la première vidéo suggérée mais toutes les vidéos suggérées (si c’est possible). La prochaine vidéo est donc la première de la liste, à moins qu’il y ait un bug avec la page et qu’aucune vidéo recommandée n’est visible. Le programme essaie alors la deuxième vidéo de la liste précédente, en espérant qu’elle ne provoque pas elle aussi une erreur de lecture. Puisqu’il n’y a environ une dizaine d’options dans la liste, c’est statistiquement improbable qu’aucune de ces vidéos ne fonctionne. Cela rajoute donc une bonne robustesse à mon programme, qui était parfois déjà capable de survivre plus de 24 heures sans s’arrêter. Voyons voir maintenant quel record il pourra faire!

Éventuellement j’aimerais utiliser phantomJS, ça marchait l’année passée, mais maintenant YouTube bloque ce genre de navigateur, il faut que je trouve une manière détournée de l’utiliser. Pour l’instant, je privilégie la robustesse à la rapidité. Il va falloir que je pense au remplissage des mémoires tampon et vives à long terme, puisque justement, après 24 heures d’utilisation continue, l’ordi commence à rusher un peu. Peut-être au moyen de redémarrage planifiés cela pourrait bien marcher. Je n’en suis pas encore là.