#1 Le 18/03/2022, à 14:19
- BenjiBoy
curl, wget, erreur 405 et requête GET uniquement
Salut tout le monde,
j'ai un problème que j'ai bien poncé, mais je ne trouve pas de solution.
Pour une asso dans laquelle je suis, je souhaite télécharger les données de consommation électrique sur le site du fournisseur d'énergie.
En passant par un navigateur lamba, il faut se connecter à son compte, naviguer un peu et arriver sur une interface ou l'on voit un graphe et cliquer sur le bouton "télécharger", ce qui télécharge les données en format CSV.
Il y a un graphe par jour.
Je veux :
- créer un script qui le fait automatiquement pour avoir des données à jour,
- télécharger les anciennes conso via un script aussi, parce que sinon il faut se taper 365 fois la manip "couton télécharger", ce qui n'est pas satisfaisant...
Pour le point 1 ça va, je vais m'en sortir, mais pour le point 2, le problème c'est que lorsque j'utilise les navigateurs terminal (elinks, w3m, lynx, link2) à chaque fois j'ai un erreur "Method not allowed" qui me dit qu'il attend un méthode GET.
Pareil avec curl ou wget, l'authentification aboutit à une erreur 405 qui correspond au fait que la méthode n'est pas la bonne (POST au lieu de GET).
Et j'ai essayé pas mal de chose mais je suis très preneur de suggestions ...
N'hésitez pas à poser des question si ce n'est pas clair, ça fait plusieurs heures que je suis dessus donc je suis peut-être brouillon.
a+
Hors ligne
#2 Le 18/03/2022, à 14:29
- BenjiBoy
Re : curl, wget, erreur 405 et requête GET uniquement
Ah aussi, le dernier truc que j'ai essayé, c'est l'option -GET de curl, et là j'ai une autre erreur que je n'interprète pas :
curl: (58) could not load PEM client certificate, OpenSSL error error:02001002:system library:fopen:No such file or directory, (no key found, wrong pass phrase, or wrong file format?)
voilà.
Hors ligne
#3 Le 18/03/2022, à 14:52
- Watael
Re : curl, wget, erreur 405 et requête GET uniquement
salut,
je ferais ce type de manipulations avec python + BeautifulSoup
il y a peut-être mieux à faire...
documente-toi un peu en attendant une validation.
Connected \o/
Welcome to sHell. · eval is evil.
En ligne
#4 Le 21/03/2022, à 15:28
- BenjiBoy
Re : curl, wget, erreur 405 et requête GET uniquement
Salut,
j'ai essayé à l'instant avec cette sauce là, et j'aboutis à la même erreur.
>>> br.submit()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/vialb/miniconda3/lib/python3.7/site-packages/mechanize/_mechanize.py", line 697, in submit
return self.open(self.click(*args, **kwds))
File "/home/vialb/miniconda3/lib/python3.7/site-packages/mechanize/_mechanize.py", line 257, in open
return self._mech_open(url_or_request, data, timeout=timeout)
File "/home/vialb/miniconda3/lib/python3.7/site-packages/mechanize/_mechanize.py", line 313, in _mech_open
raise response
mechanize._response.httperror_seek_wrapper: [b]HTTP Error 405: Method Not Allowed[/b]
Hors ligne
#5 Le 21/03/2022, à 15:37
- Watael
Re : curl, wget, erreur 405 et requête GET uniquement
c'est un peu court, pour voir s'il y a des erreurs dans ton code.
Connected \o/
Welcome to sHell. · eval is evil.
En ligne
#6 Le 22/03/2022, à 07:57
- BenjiBoy
Re : curl, wget, erreur 405 et requête GET uniquement
Voilà le code ce-dessous, très certainement perfectible (j'ai trouvé des lignes ça et là) mais il est peu probable qu'il y ait une solution tel quel, comme l'erreur 405 implique manifestement un blocage - volontaire - côté serveur.
En fouillant encore un peu, j'ai trouvé "scrapy" comme fonction python, je suis en train de tester cela actuellement.
Je pense à mon humble avis que la solution consiste à tromper l'interface web pour qu'elle pense que ce n'est pas un "code" qui essaie de se logger, mais un navigateur web.
Et je ne sais d'ailleurs même pas si c'est possible, a priori avec scrapy oui.
import mechanize
from bs4 import BeautifulSoup
import urllib2
import http.cookiejar
cookielib = http.cookiejar
cj = cookielib.CookieJar()
br = mechanize.Browser()
br.set_handle_robots(False)
br.set_cookiejar(cj)
br.open("adresse_url.com")
br.select_form(nr=0)
br.form['email'] = 'truc'
br.form['password'] = 'bidule'
br.submit()
Hors ligne
#7 Le 22/03/2022, à 08:43
- bruno
Re : curl, wget, erreur 405 et requête GET uniquement
Bonjour,
Avant de te lancer dans ton script, regarde si tu ne trouve pas ton bonheur avec Woob
#8 Le 22/03/2022, à 10:35
- BenjiBoy
Re : curl, wget, erreur 405 et requête GET uniquement
Ca à l'air vraiment intéressant mais mon niveau de python se limite à google / stackoverflow et copier/coller des lignes ... Tester et retester jusqu'à ce que ça marche :-p
Je vais tâcher de trouver des codes "tout prêt" que je peux modifier pour correspondre à mon besoin.
Merci en tout cas,
a+
Hors ligne
#9 Le 23/03/2022, à 11:25
- BenjiBoy
Re : curl, wget, erreur 405 et requête GET uniquement
Yop,
j'ai trouvé "Selenium" qui n'est pas la solution la plus appropriée mais, ça fait le job. Le site ne rejette pas la requête (erreur 405) puisque c'est un navigateur qui la fait.
En fait il ouvre un lecteur sous python, donc on peut automatiser, seulement les réponses aux requêtes restent très lentes par rapport à une solution scraping "pure".
J'ai réussi à récupérer quelques valeurs, maintenant faut que je fasse une loop dans mon script, et comme je l'ai dis je suis pas un pro de python, donc ça va prendre un peu de temps :-).
Si ça fonctionne bien - relativement bien - je posterai mon code pour qu'éventuellement ça serve à d'autres.
a+
Hors ligne
#10 Le 25/03/2022, à 22:04
- LeoMajor
Re : curl, wget, erreur 405 et requête GET uniquement
bonjour,
Au commencement le standard POST, pour l'authentification ou pour envoyer des données; x-www-form-urlencoded est le plus simple, mais il y en a d'autres ....
cat ~/scripts/login-ubuntu.bash
#!/bin/bash
#ok it works wget + curl
#https://forum.ubuntu-fr.org/login.php
#action="login.php?action=in"
#name="form_sent" value="1"
#name="redirect_url" value="http://forum.ubuntu-fr.org/index.php"
#name="req_username"
#name="req_password"
#name="login" value="Identification"
rm /tmp/cookies.txt /tmp/rep*.html 2>/dev/null
read -p "utilisateur? " user
read -p "mot_de_passe? " pass
data="form_sent=1&redirect_url=http://forum.ubuntu-fr.org/index.php&req_username=$user&req_password=$pass"
h='Content-Type: application/x-www-form-urlencoded'
#wget -O /tmp/rep1.html --header "$h" --post-data "$data" --save-cookies /tmp/cookies.txt --keep-session-cookies --delete-after https://forum.ubuntu-fr.org/login.php?action=in #ok
#wget -O /tmp/rep2.html --load-cookies /tmp/cookies.txt https://forum.ubuntu-fr.org/index.php #ok
#curl -o /tmp/rep3.html -b /tmp/cookies.txt https://forum.ubuntu-fr.org/index.php #ok variante
#---curl
curl -v -o /tmp/rep1.html -X POST --header "$h" --data "$data" -c /tmp/cookies.txt https://forum.ubuntu-fr.org/login.php?action=in
sleep 1
curl -v -o /tmp/rep2.html -b /tmp/cookies.txt https://forum.ubuntu-fr.org/index.php
sleep 1
grep -C 5 -i "$user\|déconnexion" /tmp/rep2.html
sleep 1
tmp=($(awk 'BEGIN{ FPAT="id=[0-9]+.*&|csrf_token=.*\""} /id=/ && /csrf_token=/ {gsub(/&|\"/,"",$1); print $1}' /tmp/rep2.html ))
session=( ${tmp[-2]} ${tmp[-1]})
unset tmp
echo "${session[@]}"
sleep 1
curl -v -o /tmp/rep3.html -b /tmp/cookies.txt "https://forum.ubuntu-fr.org/profile.php?${session[0]}"
sleep 1
curl -v -o /tmp/rep4.html -b /tmp/cookies.txt "https://forum.ubuntu-fr.org/login.php?action=out&${session[0]}&${session[1]}"
sleep 1
#logout login.php?action=out&id=...&csrf_token=....
exit
<form id="login" method="post" action="login.php?action=in" onsubmit="return process_form(this)">
<div class="inform">
<fieldset>
<legend>Indiquez ci-dessous votre nom d'utilisateur et votre mot de passe</legend>
<div class="infldset">
<input type="hidden" name="form_sent" value="1">
<input type="hidden" name="redirect_url" value="http://forum.ubuntu-fr.org/index.php">
<label class="conl required"><strong>Nom d'utilisateur <span>(obligatoire)</span></strong><br><input type="text" name="req_username" size="25" maxlength="25" tabindex="1"><br></label>
<label class="conl required"><strong>Mot de passe <span>(obligatoire)</span></strong><br><input type="password" name="req_password" size="25" tabindex="2"><br></label>
<div class="rbox clearb">
<label><input type="checkbox" name="save_pass" value="1" tabindex="3">Me connecter automatiquement lors de mes prochaines visites.<br></label>
</div>
<p class="clearb">Si vous n'êtes pas encore inscrit(e) ou si vous avez oublié votre mot de passe, veuillez cliquer sur le lien approprié ci-dessous.</p>
<p class="actions"><span><a href="register.php" tabindex="5">Pas encore inscrit ?</a></span> <span><a href="login.php?action=forget" tabindex="6">Mot de passe oublié ?</a></span></p>
</div>
</fieldset>
</div>
<p class="buttons"><input type="submit" name="login" value="Identification" tabindex="4"></p>
</form>
Hors ligne
#11 Le 28/03/2022, à 09:33
- BenjiBoy
Re : curl, wget, erreur 405 et requête GET uniquement
Salut tous,
LeoMajor, merci pour ce code, je vais regarder cela lorsque j'aurais un peu de temps, voir si ça produit l'erreur 405 ou pas.
Sinon, comme promis, mon code (j'ai remplacé les infos sensibles bien sûr).
A noter que l'utilisation du Xpath est assez casse-gueule, nombre de site ont des Xpath qui évolue d'un chargement à l'autre ; en gros ce qui marcherait pendant 10 téléchargements ne fonctionnerait plus les coups d'après. Mais là le bouton "télécharger" impossible de le trouver autrement.
J'ai rajouté des time.sleep() parce que parfois la page n'a pas le temps de se charger, comme c'est assez long. Typiquement l'animation "fade-out" de la bannière cookies qui met trop de temps à disparaître alors que l'info "a disparue" apparaît immédiatement et le script clique dans le vide.
J'ai mis des commentaires en Fr et en En dans le texte, pardon...
Autre chose d'important : pour avoir les ID et les Xpath ou autre discriminant, j'ai utilisé l'inspecteur de firefox, suffit de faire un clique droit sur l'objet et de mettre "inspecter", puis de regarder les infos qu'on cherche.
from pyvirtualdisplay import Display
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from datetime import date, timedelta
#background display
#choisir une resolution suffisante sinon les champ se superposent parfois et invalident certaine commande.
display = Display(visible=0, size=(1366, 768))
display.start()
#login infos
username = "user_ou_mail"
password = "mot_de_passe"
#browser selection
driver = webdriver.Firefox()
#URL
driver.get("URL_site")
#cookies banner refuse
driver.find_element(By.ID, "consentRefuseAllCookies").click()
time.sleep(2)
#user submit
driver.find_element(By.ID, "email").send_keys(username)
driver.find_element(By.ID, "btn-submit-email").click()
#password submit
driver.find_element(By.ID, "password").send_keys(password)
driver.find_element(By.ID, "btn-submit-password").click()
time.sleep(2)
def daterange(start_date, end_date):
for n in range(int((end_date - start_date).days)):
yield start_date + timedelta(n)
start_date=date(2021, 3, 11)
end_date=date(2022, 3, 27)
for single_date in daterange(start_date, end_date):
#print(single_date.strftime("%Y-%m-%d"))
#URL daily consumption
driver.get("URL_precise_ou_se_trouve_le_champ_a_cliquer" + (single_date.strftime("%Y/%m/%d")))
time.sleep(2)
#retrieve the DL button and click
driver.find_element(By.XPATH, "XPATH").click()
driver.close()
Typiquement la récupération d'une année de fichier sur le site visé, ça m'a pris 10 minutes. Ce sont de petits fichiers (1,1 ko) je ne sais pas comment ça se passerait pour des fichiers plus volumineux.
Dernière modification par BenjiBoy (Le 28/03/2022, à 09:40)
Hors ligne