Layer 1

jeudi 27 septembre 2012

Module "pywinauto": automatisez vos taches quotidiennes

Introduction:
Pour se connecter à votre banque en ligne vous devez passer par différentes étapes. Vous devez tour à tour entrer votre nom puis un numéro de compte puis votre date de naissance et enfin cliquer votre mot de passe sur un clavier numérique virtuel...
Bref si vous avez plusieurs banques en ligne, cela devient pénible non seulement de retenir l'ensemble des informations demandées mais aussi de cliquer ou de se position sur ces claviers numériques: la moindre erreur ou si vous avez un temps d’inactivité trop long, vous devez de nouveau vous identifier.

L'idéal serait d'automatiser l'ouverture d'un navigateur et la connexion sécurisé au site de sa banque.

Exemple pratique:
Le module python  le plus abouti pour l'automatisation des tâches windows est "pywinauto". Ce module est basé sur des appels win32  via le module python standard ctypes.

PyWinAuto rend les appels win32/ctypes très pythonique et (donc) puissant.

L'exemple pratique 'autologin.py' proposé en donne l'illustration: Il permet de lancer le navigateur Chrome, de se connecter sur le site d'une banque (ici INGDIRECT) et de rentrer automatiquement toutes les informations demandées pour la connexion sécurisée.

Les étapes de développement du module 'autologin.py' sont résumés ici:
(retrouver les n°étapes dans le code)

Figure 1: Différentes étapes de l'algorithme autologin.py (cf code ci-dessous)


Etape 1,2,3 et 4:

Nous allons tout d'abord créé une Class pour rassembler les différentes méthodes de notre algorithme. A la création d'un objet de la class banques nous allons essayer de se connecter au process du navigateur Chrome. Pour cela il nous faut retrouver le process 'chrome' lié à la fenêtre principale. L'utilisation d'un utilitaire visualisant l'ensemble des process et fenêtres est INDISPENSABLE.
Vous pouvez utiliser Spy++ de Microsoft si vous avez Visual Studio ou des alternatives comme Winspector ou encore Windows Detective connaitre le type de fenêtre à rechercher:

Avec Windows Detective vous allez rapidement identifier une hiérarchie des fenêtres Chrome:

Figure 15: Les fenêtres Chrome
La fenêtre Chrome principale a comme nom de class Chrome_WidgetWin_1 et son titre comporte "(...) - Google Chrome". On remarque aussi que la fenêtre fils Chrome_OmniboxView représente le texte URL de la fenêtre principal (fenêtre active). Les autres onglets (fenêtres non active) ont comme nom de class Chrome_WidgetWin_0.
Grâce à ces informations, nous allons pouvoir rechercher le process correspondant à la fenêtre principale de Chrome et s'y connecter.
L'objet app de la class Application du module pywinauto.application est ainsi connecter au process Chrome de la fenêtre principale. On va aussi pouvoir créer deux objets: self.ChromeUrl qui sera lié à la fenêtre Chrome_OmniboxView et self.ChromeWin lié à la fenêtre principale Chrome_WidgetWin_1.


Le première chose que l'on va envoyer sur la fenêtre principale Chrome sera un CTRL-T pour ouvrir un nouvel onglet (cf la méthode newtab(self)). Il existe différentes fonctions associées à l'automatisme des fenêtres que vous pouvez consulter ici: Methods available to each different control type et vous retrouverez les différents caractères spéciaux que l'on utiliser avec TypeKeys (wrapper de la fonction SendKeys.

L'objet banque est initialisé et est prêt pour être utilisé. La méthode banque.ingdirect(compte,motdepasse) regroupe les différentes actions spécifiques associé à cette banque: Entrée de l'adresse du site ingdirect, entrée du n° de compte et de la date de naissance: on utilise la méthode TypeKeys sur la fenêtre ChromeUrl et ChromeWin pour automatiser ces actions.

Outre les automatisations de lancement du process Chrome (1), des entrées textes (2,3) et des cliques de souris (7,8), il faut aussi pouvoir découvrir les emplacements où l'on doit cliquer sur le clavier numérique virtuel (6).

En effet à chaque connexion sur le site de la banque le clavier virtuel est différent:

Figure 20: exemple de clavier virtuel IngDirect
Figure 30: autre clavier virtuel IngDirect
Dans un premier temps, nous allons récupérer chaque chiffre de ce clavier sous forme de vignette, en utilisant paint par exemple (cf figure 4).  Cela permettra plus tard de rechercher chaque vignette constituant notre code secret dans l'image de ce clavier virtuel et ainsi de cliquer automatiquement dessus (7). Nous allons aussi extraire le bouton "Valider" qui nous servira pour valider le code bancaire.

Nous mettons donc de coté dans un dossier spécial toutes ces 11 vignettes (9):
Figure 40 : liste des vignettes du clavier numérique INGDIRECT virtuel

Pour pouvoir trouver les coordonnées de chaque vignette dans l'image de l'écran nous utiliserons une fonction puissante du module python cv2 qui est un wrapper "pythonique" de la librairie OpenCV, la fonction cv2.matchTemplate, son utilisation est décrite ici.

Figure 50: Seul 3 chiffres sont à renseigner...
Comme vous le verrez il existe encore une petite subtilité "IngDirect" pour rentrée le code secret bancaire: bien que ce code comporte 6 chiffres, seule 3 chiffres sur 6 sont demandés au client (figure 5). Il faut donc repérer l'emplacement des chiffres du code à rentrer et pour cela on utilisera la même méthode que prédécemment en repérant une vignette représentant le souligné orange.



AVERTISSEMENT : Il n'y a évidemment aucune protection concernant la confidentialité de vos informations bancaires puisque les codes nécessaires pour vous connecter sont en clair dans le code.




 1 # -*- coding: utf-8 -*-
 2 '''
 3 Created on 1 Septembre 2012: 'autologin.py' 
 4 -Utilisation du module PyWinAuto pour piloter chrome est aller sur des sites bancaires\
 5 -Utilisation cv2 = OpenCV pour comparer des images\
 6 @author: Python4d
 7 '''
 8 from time import sleep
 9 from pywinauto.application import Application #http://pywinauto.googlecode.com/hg/pywinauto/docs/index.html
10 from pywinauto.win32functions import SetCursorPos
11 import cv2
12 
13 class banques:
14     """Classe principale permettant de regrouper les fonctions d'automatisation"""
15     def __init__(self):
16         "Constructeur de l'objet principale du module pywinauto: l'applicaton à automatiser"
17         self.app = Application()
18         #Etape 0 (cf blog www.python4d.com)
19         try: #Connection de l'objet app avec le process lié à la fenêtre principale Google Chrome
20             self.app.connect_(title_re=".* - Google Chrome", class_name=r"Chrome_WidgetWin_1") 
21         except: #lancer un nouveau process Chrome et s'y connecter
22         #Etape 1(cf blog www.python4d.com)
23             self.app.start_(r"C:\Users\damien\AppData\Local\Google\Chrome\Application\chrome.exe", timeout=2)
24             sleep(5)
25         self.app["Chrome_WidgetWin_1"].Maximize()
26         #Mettre le curseur souris à un endroit qui ne perturbe pas le focus des fenêtres du browser
27         SetCursorPos(0, 0)
28         sleep(1)
29         #fenêtre de l'application tout entière Chrome
30         self.ChromeWin = self.app["Chrome_WidgetWin_1"]
31         #fenêtre (textbox) de l'URL
32         self.ChromeUrl = self.app["Chrome_WidgetWin_1"]["Chrome_OmniboxView"]
33         #Etape n°2 (cf blog www.python4d.com)
34         self.newtab()
35 
36     def newtab(self):
37         "Méthode qui lance un CTRL-T sur l'application Chrome: création d'un nouvel onglet"
38         sleep(2)
39         self.ChromeWin.TypeKeys(r"^T") 
40         #Mettre le curseur souris à un endroit qui ne perturbe pas le focus des fenêtres du browser
41         SetCursorPos(0, 0)
42         sleep(1)
43         
44     def matchtemplate(self, img1, img2):
45         "Méthode de comparaison entre deux images [cv2.matchTemplate (OpenCV)]"
46         imgVignette=cv2.imread(img2)
47         imgEcran=cv2.imread(img1)
48         result=cv2.matchTemplate(imgEcran,imgVignette,5)
49         (_, _, _, maxLoc) = cv2.minMaxLoc(result, mask=None)
50         return maxLoc
51         
52     def ingdirect(self, compte="12345{TAB}01011901{ENTER}", mdp="012345"):
53         "Methode automatisant les entrées claviers et souris pour le site INGDIRECT"
54         dirbase = r".//ingdirect//"
55         adrbanque = r"https://secure.ingdirect.fr/public/displayLogin.jsf"
56         #Etape n°3 (cf blog www.python4d.com)
57         self.ChromeUrl.TypeKeys("^a" + adrbanque + "{ENTER}"),sleep(4)
58         #Etape n°4 (cf blog www.python4d.com)
59         self.ChromeWin.TypeKeys(compte),sleep(5)
60         #Etape n°5 (cf blog www.python4d.com)
61         WinBoursoramaImage = self.ChromeWin.CaptureAsImage()
62         WinBoursoramaImage.save(dirbase + "WebImage.png")
63         coordy = [0,]*10
64         coordx = [0,]*10
65         x, y = self.matchtemplate(dirbase + "WebImage.png", dirbase + "VALIDER.png")
66         #Etape n°6/9 (cf blog www.python4d.com)
67         for i in range(0, 10):
68             small = str(i) + '.png'
69             coordx[i], coordy[i] = self.matchtemplate(dirbase + "WebImage.png", dirbase + small)
70         premiere_absisse = x #absisse du premier code correspond à l'absisse de la forme "VALIDER". 
71         #Etape n°7/9 (cf blog www.python4d.com)
72         for i in range(3):
73           codex, codey = self.matchtemplate(dirbase + "WebImage.png", dirbase + "code.png")
74           code=int(round(abs(premiere_absisse-codex)/32.0)) #on considère que chaque emplacement de code est séparé par 32 points (! dépend de la résolution !)
75           self.ChromeWin.ClickInput(coords=(coordx[int(mdp[code])] + 10, coordy[int(mdp[code])] + 10), double=False)
76           WinBoursoramaImage = self.ChromeWin.CaptureAsImage()
77           WinBoursoramaImage.save(dirbase + "WebImage.png")
78         #Etape n°8 (cf blog www.python4d.com)
79         self.ChromeWin.ClickInput(coords=(x + 10, y + 10), double=False)  
80 
81 if __name__ == "__main__":
82   #connection sur l'application chrome et création d'un nouvel ongletAvertissement
83   banque=banques()
84   banque.ingdirect("12345{TAB}02021968{ENTER}","987654")
85   

3 commentaires:

  1. tu aurais pu mettre tes vrais numéros d'accès à tes comptes, ça rendrait ton programme encore plus utile !

    RépondreSupprimer
  2. C'est très intéressant mais pour mieux comprendre, j'aimerai visualiser les figures, hors j'ai un sens interdit! à la place. Comment faire pour les visualiser ?

    De plus, dans le code, à chaque étape, il y a un renvoi vers un blog . Je n'ai rien trouvé en relation avec ces étapes dans le blog ? pourrais-tu être un peu plus précis ?

    RépondreSupprimer