Différences entre les versions de « Utilisateur:Pierreetheve »
Ligne 15 : | Ligne 15 : | ||
[[Fichier:Structure_petheve.png]] | [[Fichier: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à. | ||
+ | |||
+ | |||
+ | <syntaxhighlight lang="javascript">// 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);</syntaxhighlight> | ||
+ | |||
+ | |||
+ | Or createElement est une fonction, notre fonction anonyme devra donc elle-même renvoyer une fonction : | ||
+ | |||
+ | <syntaxhighlight lang="javascript"> | ||
+ | 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 | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 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 : | ||
+ | |||
+ | <syntaxhighlight lang="javascript"> | ||
+ | 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) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | == 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 : | ||
+ | |||
+ | <syntaxhighlight lang="python"> | ||
+ | #! /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); | ||
+ | </syntaxhighlight> |
Version du 17 janvier 2021 à 15:45
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
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);