On a parfois besoin de faire des tâches répétitives nécessitant de
remplir toujours les mêmes champs, cliquer toujours aux mêmes
endroits… Pour automatiser ces actions dans un navigateur, il existe
selenium. On peut ainsi naviguer vers une page, récupérer des éléments
via xpath, id, nom… puis agir dessus : envoyer des touches, appuyer
sur des boutons, etc.
De plus, on peut lancer les scripts dans des serveurs X virtuels
comme Xvfb : l’affichage n’est vas visible à l’écran et on peut ainsi
utiliser crontab pour lancer les scripts même lorsque notre session
est fermée.
Ça ne marche par contre pas quand il y a du des applets java, voire
flash, ou pour bouger la souris. Pour cela il faut utiliser d’autres
outils, comme sikuli, ou encore plus simple, pyautogui. Par contre il
va falloir dire à pyautogui
que l’affichage n’est pas celui de notre
session, mais celui de xvfb. Pour cela, voir cette réponse sur
stackoverflow qui tient en une ligne :
pyautogui._pyautogui_x11._display = Xlib.display.Display(
os.environ['DISPLAY']
)
Voici un exemple (pour m’en souvenir) de tout cela combiné :
import pyautogui
import os
import Xlib.display
from time import sleep
from selenium import webdriver
from pyvirtualdisplay.smartdisplay import SmartDisplay
# pour visible=1, installer xserver-xephyr
# sinon, xfvb prend le dessus
display = SmartDisplay(visible=0, size=(800, 600))
display.start()
browser = webdriver.Firefox()
browser.get('https://duckduckgo.com/')
browser.save_screenshot('nrst.png')
print browser.title
search = browser.find_element_by_id('search_form_input_homepage')
search.send_keys("auie")
# la souris se déplace dans le SmartDisplay
pyautogui._pyautogui_x11._display = Xlib.display.Display(
os.environ['DISPLAY'])
try:
x, y = pyautogui.locateCenterOnScreen('loupe.png')
pyautogui.moveTo(x, y, 0.5, pyautogui.easeOutQuad)
pyautogui.click()
except:
print "pas trouvé de loupe!"
sleep(3)
browser.save_screenshot('auie.png')
browser.quit()
display.stop()
Utiliser python pour faire des graphes à la xkcd, c’est possible grâce
aux développeurs de matplotlib ! Stéphane Blondon nous en parle sur
son blog avec quelle police utiliser et dans quel paquet Debian elle
se trouve.
Parfois on a (j’ai) envie de coder (ou faire coder des étudiants) et
on ne sait pas quoi faire — ou alors on veut s’entrainer, tester de
nouvelles choses. Voici une liste non-exhaustive de sites web — non
testés pour la plupart — qui proposent des exercices pour apprendre à
programmer, des défis pour faire s’affronter des programmes, voire
pour gagner de l’argent (mais ce n’est pas le sujet).
Lire la suite de Besoin de se dégourdir les doigts ?
Quand je veux récupérer plusieurs pages, je le fais souvent avec
wget
. L’utilisation des options --load-cookies
, --save-cookies
et --keep-session-cookies
avec --post-data
étant plus que
pratique. Pour parser, ensuite, c’est souvent pénible, même si awk
est là pour aider.
man wget
donne :
# Log in to the server. This can be done only once.
wget --save-cookies cookies.txt \
--post-data 'user=foo&password=bar' \
http://server.com/auth.php
# Now grab the page or pages we care about.
wget --load-cookies cookies.txt \
-p http://server.com/interesting/article.php
Pour parser, j’aime bien python ; je trouve ça plus lisible. Par
contre, ce que j’utilisais jusqu’à présent pour me logguer sur une
page était moins glop :
import urllib, urllib2, cookielib
username = 'foo'
password = 'bar'
cj = cookielib.CookieJar()
handle = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
login_data = urllib.urlencode({'username' : username, 'pwd' : password})
handle.open('http://www.example.com/login.php', login_data)
res = handle.open('http://www.example.com/my_account.php')
print res.read()
Mais ça, c’était avant. Depuis, j’ai découvert Requests. Pour faire un
POST
, c’est aussi simple que :
import requests
url= 'http://www.example.com/login.php'
login_data = {username = 'myuser', password = 'mypassword'}
req = requests.post(url, login_data)
page = req.content
Si on a besoin de gérer les cookies, on peut passer par une Session
:
import requests
url= 'http://www.example.com'
login_data = {username = 'myuser', password = 'mypassword'}
s = requests.Session()
req = s.post(url + '/login.php', login_data)
req = s.get(url + '/my_account.php')
C’est quand même plus lisible, non ?
En fait, récemment, je voulais parcourir les images de la galerie d’un
site. Mais cette galerie contenait 10 images seulement par page, pour
40 pages. J’ai donc fait un script pour mettre toutes les images
sur une seule page.
Les images sont encapsulées par une balise a class="foo"
et donc
j’utilise BeautifulSoup
pour toutes les lister
(soup.findAll("a","foo")
).
Comme j’ai « besoin » de consulter les images plusieurs fois, j’ai
préféré enregistrer les images en local plutôt que de laisser un lien
sur le site. Pour ça j’ai utilisé stream=True
de la méthode .get
(cf ce lien sur SO).
J’ai pas fait très propre puisque j’affiche la page sur stdout
que
je pipe avec tee
pour mettre dans un fichier. Mais au moins, je vois
si ça marche :þ
# -*- coding: utf-8 -*-
from BeautifulSoup import BeautifulSoup
from requests import session
login_data = { 'login': 'fredtantini',
'password': 'toto1234',
'action': 1 }
urlToVisit = 'http://www.example.com'
#on ouvre une session
with session() as s:
#on se connecte
req = s.post(urlToVisit + '/login.php', data=login_data)
print "<html><body>"
#comme je sais qu’il y a 40 pages
for nPage in xrange(1,41):
#on récupère la page
request = s.get(urlToVisit + '/some_page.php?p='+str(nPage))
#que l’on passe à BS
soup = BeautifulSoup(request.text)
print "<h1> Page - ",nPage,"</h1>"
#Pour tous les liens
for i in soup.findAll("a","foo"):
#on récupère l’image
r = s.get(urlToVisit + '/' + i.img['src'], stream=True)
#on la télécharge par parties (cf lien SO plus haut)
if r.status_code == 200:
with open(i.img['alt'] + ".jpg", 'wb') as f:
for chunk in r.iter_content():
f.write(chunk)
print str(i).replace('./images/', './')
print "</body></html>"