Hello !

Wow ça fait exactement 251 jours que je n’ai pas de partage avec vous le temps passe tellement vite, je suis désolé. Ces temps ci j’étais sur les codes, config de serveurs, tests, optimisations, … bref. Mais ne vous inquiétez j’ai des surprises pour vous alors stay tuned 😉

Avant de commencer je vais vous donner la définition de Web Scraping d’après Wikipédia :

Le web scraping (parfois appelé Harvesting) est une technique d’extraction du contenu de sites Web, via un script ou un programme, dans le but de le transformer pour permettre son utilisation dans un autre contexte.

La semaine passé j’ai remarqué que mon collègue regardait les parutions de gbitch à chaque fois qu’il avait du temps libre. Des fois il a du mal à le lire à cause d’une connexion qui n’est pas stable. Alors je lui ai proposé d’écrire un script pour extraire toutes les images en local 🙂 D’accord je vais vous montrer comment j’ai fait. C’est un tout petit script de rien du tout.

J’ai commencé par analyser la structure de la partie qui nous intéresse sur la page. Je vais vous faire une capture d’écran :

Capture du 2015-06-29 06:50:54

 

Comme vous pouvez le voir nous avons la liste des collections (des images) et une pagination. Ça c’est notre première page maintenant on regarde le code sources de la pagination pour voir si on peut extraire la liste des autres pages. Il suffit de faire un simple clic droit et inspecter le code :

Capture du 2015-06-29 06:57:21

Voilà ! Les liens se trouvent sur une balise a.numero_page qui sont dans un bloc div#other_une donc on peut dire que le sélecteur CSS pour accéder à la liste des urls est : #other_une a.numero_page.

Maintenant on fait la même chose avec les images :

Capture du 2015-06-29 08:29:19

D’après le code source on peut dire que le sélecteur CSS pour accéder aux images est le suivant : .unegbich img.

C’est tout ce dont nous avons besoin pour extraire les URL des images. Maintenant passons au script.

Nous allons utiliser deux gems : rest_client qui va nous permettre de récupérer le code HTML d’une page web et la gem nokogiri qui est un parseur HTML et XML. Pour les installer il faut taper ces deux commandes (il faut mettre sudo devant si le système l’exige)

gem install rest_client
gem install nokogiri

première étape : récupération des URL des pages.

require 'nokogiri'
require 'rest_client'

base_url = "http://gbich.com/"
lien = "#{base_url}index.php?choix=collectionune&&limite=0"
html = RestClient.get(lien)
doc = Nokogiri::HTML(html)
pages = doc.css("#other_une a.numero_page")
pages.map! { |p| p['href'] }
puts pages

Nous avons récupérer le code HTML de la page avec RestClient.get(lien) puis le parser avec le code Nokogiri::HTML(html) et enfin récupérer la liste des urls des pages avec le sélecteur CSS doc.css("#other_une a.numero_page"). On obtiendra le résultat suivant après l’exécution du script :

#
index.php?choix=collectionune&&limite=30
index.php?choix=collectionune&&limite=60
index.php?choix=collectionune&&limite=90
index.php?choix=collectionune&&limite=120
index.php?choix=collectionune&&;limite=150
index.php?choix=collectionune&&;limite=180
index.php?choix=collectionune&&;limite=210
index.php?choix=collectionune&&;limite=240
index.php?choix=collectionune&&;limite=270
index.php?choix=collectionune&&;limite=300
index.php?choix=collectionune&&;limite=330
index.php?choix=collectionune&&;limite=360
#
index.php?choix=collectionune&&limite=30
index.php?choix=collectionune&&limite=60
index.php?choix=collectionune&&limite=90
index.php?choix=collectionune&&limite=120
index.php?choix=collectionune&&;limite=150
index.php?choix=collectionune&&;limite=180
index.php?choix=collectionune&&;limite=210
index.php?choix=collectionune&&;limite=240
index.php?choix=collectionune&&;limite=270
index.php?choix=collectionune&&;limite=300
index.php?choix=collectionune&&;limite=330
index.php?choix=collectionune&&;limite=360

Il a doublé urls, en fait il y a une pagination avant et après la liste des images c’est pourquoi on a ce résultat. Maintenant ce qu’on va faire c’est enlever l’ancre « # » de la liste et prendre la moitié du tableau puis mettre l’URL de base (http://gbich.com/) sur les liens et enfin ajouter le premier lien sur la liste. On ajoute ces 4 petites lignes :

pages -= ['#']
pages = pages[0..pages.size/2-1]
pages.map! { |p| base_url+p }
pages = ["http://gbich.com/index.php?choix=collectionune&&limite=0"] + pages

deuxième étape : récupération les URLs des images à extraire.

On ajoute ces lignes sur le script :

images = []
for page in pages
  html = RestClient.get page
  doc = Nokogiri::HTML(html)
  for r in doc.css(".unegbich img")
    images << base_url + r['src']
  end
end
puts images

troisième étape (fin) : téléchargement des images 🙂

On va exécuter cette commande ruby script.rb > sortie_images.txt pour mettre la liste des URL des images sur un fichier sortie_images.txt.

Ensuite on utilise la commande wget pour télécharger les images. On crée un nouveau répertoire ensuite on se déplace dessus et exécuter la commande : wget -i sortie_images.txt et vous aurez les images en quelques secondes, minutes, heures, ... euh ça dépend de votre connexion internet 😛

Voilà c'est tout pour aujourd'hui, vous pouvez jeter un coup d'oeuil sur scrapy pour aller plus loin. Amusez vous bien 😉

 

Voici le script final :

require 'nokogiri'
require 'rest_client'

images = []
base_url = "http://gbich.com/"
lien = "http://gbich.com/index.php?choix=collectionune&&limite=0"
html = RestClient.get(lien)
doc = Nokogiri::HTML(html)
pages = doc.css("#other_une a.numero_page")
pages.map! { |p| p['href'] }

pages -= ['#']
pages = pages[0..pages.size/2-1]
pages.map! { |p| base_url+p }
pages = ["http://gbich.com/index.php?choix=collectionune&&limite=0"] + pages

for page in pages
  html = RestClient.get page
  doc = Nokogiri::HTML(html)
  for r in doc.css(".unegbich img")
    images << base_url + r['src']
  end
end

puts images

Je suis entrain de travailler sur une application en ruby on rails et il j’utilise les relations polymorphiques pour les héritages.

Après, vous pouvez utiliser les nested_form pour ne pas vous répéter sur la création des formulaires : DRY 🙂

Maintenant, supposons qu’on ait les modèles ChienChat et Rat qui héritent du modèle Animal où vous faites un belongs_to :entity, polymorphic: true pour l’héritage.

Si vous voulez créer un lien pour afficher les détails d’un animal il suffit de faire : <%= link_to animal.nom, animal.entity %> et le tour est joué.

Maintenant comment est ce qu’on va faire pour créer un lien pour éditer un animal ? Et pour la suppression ? Vous me direz <%= link_to 'modifier', edit_animal_path(animal) %> mais ce ne marchera que si on modifie tous les animaux sur un seul formulaire et là y aura beaucoup de conditions à vérifier pour afficher les champs qui concernent l’animal en question à moins que … je ne sais pas 🙂

Euh il y a une solution :

<% if @animal.entity.is_a?(Chien) %>
    <%= link_to 'modifier', edit_chien_path(animal.entity) %>
<% end %>
<% if @animal.entity.is_a?(Chat) %>
    <%= link_to 'modifier', edit_chat_path(animal.entity) %>
<% end %>
<% if @animal.entity.is_a?(Rat) %>
    <%= link_to 'modifier', edit_rat_path(animal.entity) %>
<% end %>

Mais, comme vous le constatez, cette solution n’est pas propre. C’est là qu’intervient notre cher polymorphic_url ou polymorphic_path qui va nous nettoyer tout ça :

<%= link_to 'modifier', edit_polymorphic_path(animal.entity) %>

hmmm ^_^ C’est magique non ?

Vous pouvez consulter la documentation pour plus de détails.

Cet article va vous montrer comment ajouter un bouton d’export CSV sur votre application rails.
Vous allez voir c’est très simple.
Il faut commencer par importer la bibliothèque csv dans votre fichier config/application.rb comme suit :

require 'rails/all'
require 'csv'

Ensuite on implémente une méthode de classe as_csv dans le modèle en question :

def self.as_csv
  CSV.generate do |csv|
    csv << column_names
    all.each do |item|
      csv << item.attributes.values_at(*column_names)
    end
  end
end

Ce code va exporter toutes les données avec tous les attributs du modèle. Si vous voulez faire des filtres sur le modèle pour n'afficher que les attributs nomprenom et email vous pouvez écrire le code suivant :

def self.as_csv
  CSV.generate do |csv|
    colonnes = %w(prenom nom email)
    csv << colonnes
    all.each do |item|
      csv << item.attributes.values_at(*colonnes)
    end
  end
end

Tout ce qui reste à faire c'est d'ajouter ce bout de code à votre contrôleur pour qu'il prenne en compte l'extension csv.

def index
  @users = User.all

  respond_to do |format|
    format.html
    format.csv { send_data User.as_csv }
  end
end

Voilà c'est tout. Si vous accédez à l'URL http://localhost:3000/users.csv une belle fenêtre s'ouvrira pour vous proposer d'enregistrer votre fichier CSV qui contient la liste de tous les utilisateurs 🙂

J’ai l’habitude de travailler avec le Framework Ruby On Rails et il y a un filtre before_filter sur les contrôleurs qui permet d’exécuter un ensemble de taches avant de lancer une action. C’est utile pour si on veut vérifier qu’un utilisateur s’est bien connecté et a les droits d’exécuter une action par exemple.

Récemment, j’ai commencé à développer une application Django au bureau et je me suis rendu compte que cette fonctionnalité n’existe pas dans cet univers 🙁

Après l’avoir googlé je suis tombé sur un middleware qui permet de le faire sur Django 🙂 (encore merci M. Google).

Voici le middleware en question :

import sys

class BeforeFilterMiddleware(object):
    def __init__(self):
        self.setting = 'BEFORE_FILTER'
        self.filters = []

    def _call_filters(self, request, view, args, kwargs):
        response = None
        for f in self.filters:
            response = f(request, view, args, kwargs)
            if response and response.has_header('location'):
                self.filters = []
                return response
        self.filters = []
        return response

    def process_view(self, request, view, args, kwargs):
        module = sys.modules[view.__module__]
        if hasattr(module, self.setting):
            for func, views in getattr(module, self.setting).items():
                exclude = '-%s' % view.func_name
                if not views or view.func_name in views: # or views and exclude not in views:
                    if hasattr(module, func):
                        if getattr(module, func) not in self.filters:
                            self.filters.append(getattr(module, func))
        if self.filters:
            return self._call_filters(request, view, args, kwargs)
        return None

Utilisation :

J’ai enregistré ce script dans un fichier outils/my_middlewares/before_filter.py du répertoire principal de mon application.

Ensuite, je l’ai ajouté sur la liste des middlewares de mon application : la constante MIDDLEWARE_CLASSES du fichier de configuration settings.py, maintenant il doit ressembler à ca :

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'outils.my_middlewares.before_filter.BeforeFilterMiddleware',
)

C’est tout pour la configuration.

Maintenant si on veut, par exemple, exécuter une fonction admin_required avant de lancer les actions new et create et owner_required pour l’action edit il suffit d’ajouter ces lignes de code sur ladite view :

BEFORE_FILTER = {'admin_required': ('new', 'create'),
                 'owner_required': ('edit',),
                 }

def admin_required(request, view, args, kwargs):
    if not is_admin():
        messages.warning(request, u"Vous ne pouvez pas accéder à cette page")
        return HttpResponseRedirect(reverse('root'))

def owner_required(request, view, args, kwargs):
    if not is_owner():
        messages.warning(request, u"Vous ne pouvez pas modifier une ressource qui ne vous appartient pas")
        return HttpResponseRedirect(reverse('root'))

C’est tout ! Vous avez des questions ? Commentez 🙂

puts "Bonjour tout le monde"

Aujourd’hui je vais vous parler d’un jeune langage de programmation pas comme les autres. Il a fêté ses 20 ans le 24 février 2013. Sa version 2.0.0 est sortie en release le même jour.

Ce langage m’a facilité ma vie de développeur depuis que je l’ai découvert ça fait maintenant 5 ans (depuis 2008). Ce qui m’a le plus séduit chez ce langage c’est sa simplicité et le Framework Ruby On Rails (développé par David Heinemeier Hasson) pour les développements d’application Web et le Framework Sinatra (développé par Blake Mizerany) qui est vraiment simple pour implémenter des Services Web. Ne vous inquétez pas, je vous parlerai de tous ça dans mes articles à venir 🙂

Maintenant passons à la présentation de ce langage et je vous montrerais quelques bouts de code 🙂 Vous allez adorer (hihi)

C’est quoi le langage Ruby

Il ne faut pas le confondre avec l’actrice Ruby de je ne sais quel film. Le Ruby dont je vous parle est un langage de programmation interprété, libre, 100% orienté objet, … Il est développé par le japonais Yukihiro Matsumoto “Matz” qui a été honoré du prix du logiciel libre 2011.

Installation

  • Si vous êtes sous linux
    • téléchargez le code source ici : http://www.ruby-lang.org/fr/downloads/
    • ensuite vous le décompresser
    • vous ouvrez votre terminal
    • vous vous déplacer dans le dossier
    • vous tapez les commandes suivantes
      • > ./configure
      • > make
      • > make install
  • Si vous êtes sous windows vous pouvez télécharger ruby installer ensuite il suffit de faire du “suivant, suivant, …, terminer :)”

 

voilà maintenant passons au code

les bases de ruby

Ruby vient avec un irb (Interactive Ruby) qui est un interpréteur interactif de commande Ruby.

Vous pouvez le lancer avec la commande

> irb

après vous aurez un truc du genre :

irb(main):001:0>

Voilà maintenant vous pouvez discuter avec votre ami Ruby 🙂 (si vous ne voulez pas installer Ruby vous pouvez le tester sur votre navigateur http://joshnuss.github.io/mruby-web-irb/ mais ce n’est pas complet).

On va commencer par le fameux ‘Hello world’

irb(main):001:0> puts “Hello World”
Hello World
=> nil

‘puts’ est une commande pour afficher.

les variables

irb(main):001:0> a = 14
=> 14
irb(main):002:0> b = 11
=> 11
irb(main):003:0> puts "a + b vaut " + (a+b).to_s
a + b vaut 25
=> nil

Ici nous avons créé deux variables a et b en leur affectant les valeurs respectives 14 et 11 après nous avons affiché la somme avec la méthode puts. Nous avons calculé la somme, la transformer en chaîne de caractères avec la méthodes to_s pour que la concaténation avec la chaîne de caractère “a + b vaut “ marche. La concaténation se fait avec l’opérateur ‘+’.

On vient d’apprendre beaucoup de choses hein, okey je vais faire doucement 😀

les chaînes de caractères

irb(main):001:0> s = gets
coucou
=> "coucou\n"
irb(main):002:0> s.chomp
=> "coucou"
irb(main):003:0> s2 = gets.chomp
cou
=> "cou"
irb(main):004:0> puts s2 * 2
=> "coucou"

Qui a compris ?

Qui n’a pas compris ?

Ok je vous explique.

gets est une méthode qui permet de demander à l’utilisateur de saisir une valeur comme le readln de Pascal ou le cin de C++ mais il y’a une petite différente, si vous êtes un bon observateur vous pouvez le voir : la frappe sur la touche [ENTER] est incluse dans la chaîne de caractère. Mais ne vous inquiétez pas Matz à ajouté une méthode qui permet de l’enlever facilement => chomp.

Donc au lieu de faire les traitements séparément vous pouvez demander une valeur à l’utilisateur avec gets.chomp comme je l’ai fait à la troisième instruction.

La multiplication d’une chaîne de caractères par n fait une répétition de la chaîne n fois comme le montre l’exemple précédent.

Tout à l’heure je vous ai montré comment faire une concaténation avec l’opérateur ‘+’, mais s’il y a beaucoup d’élément à concaténer ça devient ennuyant. Une autre façon de concaténer c’est de mettre la variable ou bien l’expression dans la chaîne même entouré de #{...} mais dans ce cas il faut utiliser les guillemets au lieu d’apostrophes pour délimiter la chaîne sinon l’expression ne sera pas interprétée. Ok un exemple -_-

irb(main):001:0> pseudo = "scicasoft"
=> "scicasoft"
irb(main):002:0> puts "Bonjour #{pseudo} !"
"Bonjour scicasoft !"
=> nil

Voilà, vous êtes content maintenant ?

Voici d’autres méthodes intéressantes pour les chaînes de caractères :

  • to_i : transformer un chaîne en entier
  • to_f : transformer un chaîne en réel
  • length : retourne la taille d’une chaîne
  • upcase : convertit une chaîne en majuscule
  • downcase : le contraire de upcase 😛
  • capitalize : met en majuscule le premier caractère de la chaîne
  • reverse : inverse la chaîne

Tableaux

irb(main):001:0> mon_tableau = ["a", 12, "b"]
=> ["a", 12, "b"]

Nous venons de créer une tableau 🙂 Non je ne me suis pas trompé le tableau est mixte. Un seul tableau peut contenir des entiers, réels, chaînes et même des tableaux.

Il y’a aussi des méthodes très intéressantes pour les tableaux :

  • shuffle : permet de mélanger le contenu du tableau
  • reverse : inverse le tableau
  • sort : tri le tableau
  • count : renvoit sa taille
  • first : renvoit le premier élément du tableau
  • last : renvoit le dernier élément
  • empty? : vérifie si le tableau est vide

C’est pas tout. On peut concaténer 2 tableaux avec l’opérateur ‘+’, une méthode ‘uniq’ qui permet d’enlever les répétitions, l’opérateur ‘-’ qui calcule la différence entre deux tableaux, …

les structures de contrôle

  • if … else … end
reponse = 12
if (reponse == 42)
  puts "Bingo, LA réponse !"
else
  puts "Essaie encore"
end
  • unless … end

C’est le contraire de if (sauf si)

j = rand(10)
unless (j <= 5)
  puts "j vaut plus de 5"
end
  • while … end
puts "compte de 1 a 10"
i = 0
while (i < 10)
  i += 1
  puts i
end
  • for … end

la boucle for s’applique à des éléments de collections : list, array, ...

puts "compte de 1 a 10"
for e in (1..10)
  puts e
end
  • each
puts "compte de 1 a 10"
(1..10).each { |elt1|
  puts elt1
}

création d’une fonction

def say_hello
  puts "hello"
end

création d’une classe

class Personne
  def say_hello
    puts ‘hello’
  end

  def self.who_are_you?
    puts “je suis une personne”
  end
end

une méthode précédée de self. est une méthode de classe.

Les gems

Comme la plupart des langages de programmation actuels, Ruby s’appuie sur tout un écosystème de librairies.

Ces librairies sont bien souvent disponibles sous la forme d’une gem. RubyGems est le « gestionnaire de paquet de Ruby » permettant de télécharger et d’installer ces gems. Il facilite également leur création et leur partage. Il s’agit donc d’un équivalent à Apt ou Homebrew, mais pour Ruby spécifiquement.

utilisation

  • recherche d’une gem
gem search -r html

l’option r (remote) permet d’inspecter le dépot distant http://rubygems.org.

  • installer une gem
gem install djanoa

cette commande permet d’installer la gem djanoa

  • lister toutes les gems
gem list -r

permet de lister toutes les gems disponible

gem list

permet de lister les gems en local seulement

  • help

Une aide succincte est disponible directement en ligne de commande

gem help

C’est “tout”

Voilà j’ai assez parlé maintenant je vous laisse quelques liens pour si vous voulez approfondir :

Après je vais essayer de vous présenter quelques gems intéressants donc restez connecté 😉

([*(0..9)]+[*('a'..'z')]).shuffle[0..9].join