Utilisateur:Pierreetheve

De {}
Aller à la navigation Aller à la recherche

Page de Pierre ETHEVE

Sources

Mon but est de collecter une grande quantité d'animations schématiques afin de pouvoir s'en servir pour de l'apprentissage automatique. Plusieurs sources sont possibles, sous forme de sites d'hébergements soit de vidéos (Vimeo, Youtube), soit de GIFs (Pinterest, Tumblr). L'avantage des sites de GIFS est que les répertoires sont plus fournis, et plus propres à l'herbergement de fragments video courts. La petite définition n'est pour le moment pas un problème, mais elle pourrait le devenir par la suite.

Pinterest : - https://www.pinterest.fr/chars7793/rough-animation/

Tumblr : - https://2dtraditionalanimation.tumblr.com/archive


Structure

Structure petheve.png





Extracteur d'URL

Le but du projet est simple : pouvoir télécharger en bloc tout le contenu d'un board pinterest pour constituer des bases de données d'images homogènes (les boards pinterest sont très efficace pour laisser aux utilisateurs le soin de trier les informations, et donc effectivement de les labeliser).

Méthode traditionnelle

La méthode classique consistant à charger la page via Mechanize, puis parse les données récupérées via CSSSelect ne marche que pour des sites relativement statiques. Or Pinterest demande qu'on se connecte pour pouvoir fournir des choix personnalisés.

Deux options sont donc possibles : essayer de continuer dans une voie 100% automatique avec un navigateur plus avancé comme Selenium, ou bien chercher une méthode plus spécifique. En l'occurence, on peut tirer partie du fait que la collecte de donnée se fait une fois, sur une seule page, et n'a pas besoin d'être répétée automatiquement. On peut donc ruser en altérant manuellement la page déjà chargée dans le navigateur pour qu'elle renvoie d'elle-même les données qui nous intéresse.


Méthode locale

J'ai donc fini par me rabattre sur une méthode que j'avais déjà expérimentée auparavant, consistant à aller modifier dans la page chargée dans le navigateur certaines fonctions de bases pour ménager un flux d'informations pertinentes vers le log, qui sera ensuite copié à la main.

Ici le but va être de modifier la fonction setAttribute de chacune des éléments de la page. Cette fonction est appelée à la création de nouveaux éléments graphiques pour remplir les différents attributs de la balise, dont un champ 'src' qui contient l'adresse exacte de l'image stockée sur Pinterest en haute définition. Ainsi, une fois modifiée, cette fonction remplira ses usages normaux, mais en prime renverra toute chaine de caractère qui contient le pattern "pinimg", présent dans toutes les adresses des images.

Pour cela, on va imbriquer des wrapping de fonction, qui permettent d'adjoindre de nouvelles fonctionnalités à une fonction existante.

Basiquement, on va donner à document.createElement une nouvelle valeur. Si createElement avait été une simple variable, on aurait donc une syntaxe comme celle là.


// valeur originale de x
var x = 2

// on exécute une fonction anonyme qui va appliquer une opération sur x, puis retourne le résultat
var x = function(old_x){
	return x+2;
}(x);


Or createElement est une fonction, notre fonction anonyme devra donc elle-même renvoyer une fonction :

document.createElement = function(old){
  return function(){
    
  	// la version original de document.createELement est exécutée
  	old.apply(this, arguments);

  	// on peut ici faire ce qu'on veut d'autre avec ses arguments

  };
}(document.createElement); 

//la fonction anonyme est exécutée instantanément, prenant pour paramètre (nommé old) la version originale de document.createElement

Ici on fait un double wrapping : on laisse createElement faire ce qu'elle veut pour créer la balise, puis on remplace sa fonction setAttribute par une fonction maison qui va simplement renvoyer le contenu de l'attribut si il contient pinimg.

Toute cette explication nous mène donc au code final assez peu lisible, mais efficace :

document.createElement = function(old_create) {
    return function() {

        // la version originale de createDocument est exécutée
        var ret = old_create.apply(this, arguments);

        // après l'exécution de createDocument et la création de la balise,
        // on utilise la même méthode de wrapping pour changer le contenu 
        // de setAttribute
        ret.setAttribute = function(old_setattribute){
        	return function(){
                // l'ancienne version n'est même pas exécutée, on se contente
                // de récupérer les infos nécessaires et les exporter dans le log
        		if (arguments[1].includes("pinimg")){
        			console.log(arguments[1])
        		}
        	}
        }(ret.setattribute)
        return ret;
    };
}(document.createElement)


Téléchargement des images

Dans mon projet original, le log copié devait être manuellement transformé par une suite de rechercher-remplacer pour obtenir une liste d'adresses précédées d'un WGET, qui était ensuite exécuté en ligne de commande. J'ai pris cette fois ci là peine de faire un vrai script qui s'occupe de toutes ces étapes tout seul, en isolant la bonne adresse, puis en utilisant la librairie urlretrieve pour aller télécharger chacune des images et les stocker dans un dossier. Voici la version finale du script :

#! /usr/bin/python3
# -*- coding: utf-8 -*-
__author__    = "Pierre Etheve"
__email__  = "pierre.etheve.lfdv@gmail.com"

import urllib.request as rq
import sys

unique_pins = []
start = 0
folder = "pins"
source = "estampe_pinimg.txt"


# Processing des options, peu intéressant, à la reflexion j'aurais eu une 
# meilleure syntaxe en utilisant la librairie opt
def getOptions() :
	global folder, start, source
	args = sys.argv
	option = ""
	for el in sys.argv :
		if el[0] != "-" :
			if option == "path" :
				folder = el
				print("Setting path to %s"%folder)
			elif option == "start" :
				start = int(el)
				print("Setting start to %d"%start)
			elif option == "source" :
				source = el
				print("Setting start to %s"%source)

			option = ""

		else :
			option = el[1:]

# télécharger le pin à partir de l'url et le stocker dans le dossier 'folder'
def getPin(url) :
	for i in range(start, len(unique_pins)) :
		link = unique_pins[i]
		rq.urlretrieve(link, "%s/%05d.jpg"%(folder, i))
		print("Saving to %s/%05d.jpg"%(folder,i))

# si aucune option n'est montrée, print l'aide et clore le programme
if len(sys.argv) == 1 :
	print("./pin_dl.py -path [DESTINATION FOLDER] -start [SKIP TO # PIN] -source [xxx_pinimg.txt]")
	sys.exit()

# process les options
getOptions()


# trier les url pour ne garder que celles en HD
with open(source) as infile : 
	for line in infile :
		if "4x" in line :
			link = line.split(", ")[-1][:-3]
			if link not in unique_pins :
				unique_pins.append(link)
print("Found %d unique pins"%len(unique_pins))

# pour chaque URL HD, lancer un télécharger grace à get_pin()
for link in unique_pins :
	getPin(link);