Suivre une commande d'achat comme un hacker avec PhantomJS, Selenium, requests et BeautifulSoup (FR)
mar. 25 avril 2017 Dan LousquiPour la petite histoire, j'ai récemment (18/03/2017) commandé une console "Nintendo Switch" sur la boutique Internet d'une grande enseigne de vente (celle au logo doré).
Pour ceux qui ne savent pas, l'article "Nintendo Switch" a été en rupture de stock, dès son jour de lancement, et l'est encore au moment de la rédaction de cet article (26/04/2017). Ainsi... ça fait plus d'un mois que je patiente la réception de ma commande.
Edit: finalement, la commande a été préparée à la date de publication de l'article et le script a bien marché :-)
Du coup, pendant ce temps, j'ai gagné un nouveau passe-temps. Je m'amuse à appuyer sur F5 sur mon navigateur sur deux sites:
- Un site de jeux vidéo possédant l'un des plus grands forums Internet, avec un fil de discussion dédié au problème de stock de la console sur le site en question ;
- Le site du revendeur, pour voir si le statut de ma commande avance.
Etant flemmard, mon sport préféré est l'automatisation de ces tâches :-)
C'est parti !
Suivre un thread d'un forum
Le concept
Nous avons donc:
- Un fil d'actualité sur un forum ;
- Plusieurs pages, pour pouvoir afficher les messages;
- Parmi les informations interessantes par message, nous avons besoin de :
- La date du message ;
- Le pseudo de l'auteur d'un message ;
- Le message !
Le fonctionnement est le suivant:
- On initie une liste de message déjà affiché;
- On initie le numéro de page courante;
- Toutes les 30 minutes :
- On Récupère le contenu de la page en cours (avec
requests
) ; - On parse le contenu HTML de la page (avec
BeautifulSoup
) ; - On récupère tous les messages (on sait qu'il s'agit des
div
avec laclass
inner-head-content
) ; - Pour chaque message :
- On récupère la date ;
- On récupère l'auteur ;
- On récupère le contenu ;
- Si le message ne fait pas parti des messages déjà affichés:
- On l'affiche ;
- On l'ajoute à la liste des messages déjà affichés ;
- Si il y a une page suivante (on sait qu'il s'agit des
div
avec laclass
pagi-after
):- On incrémente le numéro de page actuelle.
- On Récupère le contenu de la page en cours (avec
Le script
Faisons tout ça en python
:-)
from bs4 import BeautifulSoup
import requests
import time
viewed = []
page=218
while True:
url = "http://www.SUPER_SITE_DE_JEUX_VIDEO.com/forums/42-3007199-50107898-{}-0-1-0-precommandes-livraison.htm"
html = requests.get(url.format(page)).content
bs = BeautifulSoup(html, "html.parser")
messages = bs.findAll("div", {'class':"inner-head-content"})
for message in messages:
date = message.find("div", {"class":"bloc-date-msg"}).text.replace("\n","")
nick = message.find("span").text.replace("\n","").replace(" ","")
content = message.find("div", {"class":"bloc-contenu"}).text.replace("\n","")
if date not in viewed:
viewed.append(date)
print("{} - {}\n{}\n--------------\n".format(date,nick,content))
if "Page suivante" in bs.find("div", {'class':"pagi-after"}).text:
page += 1
else:
time.sleep(1800)
Suivre la commande sur un site de revendeur
Le concept
Cette fois, c'est légèrement plus compliqué, on doit aller sur le site du revendeur pour voir le statut de notre commande. Pour cela, deux solutions sont possibles :
- On effectue une requête légitime avec un browser, on récupère la requête et on la rejoue avec
requests
en python ; - On crée un browser "virtuel" avec
PhantomJS
et on effectue tout le scénario (authentification + affichage des commandes).
Le site du revendeur possède beaucoup de JavaScript, et j'ai peur que l'état de la commande soit lié à la session. Je préfère donc utiliser la deuxième méthode afin d'être sûr d'avoir une information "fraiche" et non celle en cache. De plus, ça fera manipuler PhantomJS
:-)
Le fonctionnement est le suivant:
- On récupère login / mot de passe de l'utilisateur ;
- Toutes les 30 minutes :
- On crée une instance de
PhantomJS
; - On redirige cette instance vers la partie authentifiée du site du revendeur ;
- L'instance de
PhantomJS
est automatiquement redirigée vers la mire d'authentification ; - On remplit le login (on sait qu'il s'agit d'un
input
avec l'id
Login_Email
) ; - On remplit le password (on sait qu'il s'agit d'un
input
avec l'id
Login_Password
) ; - On soumet le formulaire ;
- On parse le contenu HTML de la page (avec
BeautifulSoup
) ; - On récupère la
div
avec l'avancement de la dernière commande (on sait qu'il s'agit desdiv
avec laclass
ma-GaugeWrap
) ; - On récupère et affiche les étapes passées (on sait qu'il s'agit des
div
avec laclass
ma-GaugeStep--passed
) ; - On récupère et affiche les étapes en cours (on sait qu'il s'agit des
div
avec laclass
ma-GaugeStep--current
) ;
- On crée une instance de
Le script
Faisons tout ça en python
:-)
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
from bs4 import BeautifulSoup
import datetime
import getpass
import time
login = "mon@email.com"
password = getpass.getpass(prompt='Password: ')
while True:
driver = webdriver.PhantomJS()
driver.set_window_size(1024, 768)
driver.get('https://secure.SUPER_BOUTIQUE_EN_LIGNE.com/MyAccount/Order')
inp = driver.find_element_by_id("Login_Email")
inp.clear()
inp.send_keys(login)
inp = driver.find_element_by_id("Login_Password")
inp.clear()
inp.send_keys(password)
inp.send_keys(Keys.RETURN)
html = driver.page_source
soup = BeautifulSoup(html)
last_command = soup.find("div", {"class":"ma-GaugeWrap"})
print(str(datetime.datetime.now()))
for data in last_command.findAll("div", {"class": "ma-GaugeStep--passed"}):
print(data.text.replace("\n\n\n","").replace("\n\n","-"))
for data in last_command.findAll("div", {"class": "ma-GaugeStep--current"}):
print(data.text.replace("\n\n\n","").replace("\n\n","#"))
time.sleep(1800)