Je reviens à Ameli-direct et mon post précédent. Lors de l'écriture de celui-ci, je n'avais pas entièrement saisi par quelle procédure se faisait l'obtention de l'url qui permettait d'accéder aux informations des médecins. Lors d'une recherche sur le site, FireBug affiche la chose suivante dans l'onglet script.
J'en déduis qu'il faut imiter ces deux étapes afin de pouvoir récupérer l'url avec les détails sur les médecins. La première étape est une requête post qui est définie dans la form présente sur la page et envoyée quand on clique sur le bouton rechercher défini par la classe input. En simplifiant, cela donne le code suivant :
La form contient donc directement l'url à envoyer au serveur. Cette url retourne une réponse :
C'est cette réponse qui est appelée à l'aide du GET que l'on a déjà vu plus haut. C'est elle qui contient le HTML avec les informations sur les médecins.
Dans la suite, nous allons essayer d'imiter la démarche :
Tout d'abord, récupérons la page web de départ.
import urllib2
source = urllib2.urlopen('http://ameli-direct.ameli.fr/').read()
Cherchons la présence de la form et de l'url qu'elle contient.
ind = source.find('<form action="')
ind
2847
source[ind:].split('\n')[0]
'<form action="/recherche-d3295fb997a541f7ac1f7d159ecc223c.html" method="post">'
post_url = source[ind:].split('\n')[0].split('"')[1]
post_url
'/recherche-d3295fb997a541f7ac1f7d159ecc223c.html'
Comme on peut le voir dans les images ci-dessus, des cookies servent à envoyer au serveur les informations sur les choix effectués par l'utilisateur. Ici, le cookie infosoins semble bien mystérieux. C'est lui qui est censé contenir les informations issus de la form. On trouve une explication sur le submit ici.
Lorsqu'un formulaire est soumis (appui sur le bouton de soumission), les données présentes dans le formulaire sont envoyées au script CGI sous forme de paires nom/valeur, c'est-à-dire un ensemble de données représentées par le nom de l'élément de formulaire, le caractère "=", puis la valeur associée. L'ensemble de ces paires nom/valeur étant séparées entre elles par des esperluettes (caractère &). Les données envoyées ressembleront donc à ceci :
champ1=valeur1&champ2=valeur2&champ3=valeur3
Ceci est fait à l'aide de l'encodage par défaut :
La balise FORM possède comme attribut facultatif ENCTYPE qui spécifie le codage des données dans l'URL, toutefois il n'est pas nécessaire de le préciser car la valeur attribuée par défaut (application/x-www-form-urlencoded) est la seule valeur valide.
Dans notre cas, les noms utilisés sont les suivants :
Et les valeurs associées sont celles renseignées par l'utilisateur.
"&".join(["=".join((f, v)) for (f, v) in zip(['ps_nom', 'ps_profession_label', 'ps_acte_label', 'ps_localisation'], ["''", 'Ophtalmologiste', "''", '75013'])])
"ps_nom=''&ps_profession_label=Ophtalmologiste&ps_acte_label=''&ps_localisation=75013"
D'après ce lien on peut soumettre les données de la manière suivante :
import urllib
import urllib2
values = dict(zip(['ps_nom', 'ps_profession_label', 'ps_acte_label', 'ps_localisation'], ["''", 'Ophtalmologiste', "''", '75013']))
data = urllib.urlencode(values)
req = urllib2.Request('http://ameli-direct.ameli.fr' + post_url, data)
response = urllib2.urlopen(req)
the_page = response.read()
--------------------------------------------------------------------------- HTTPError Traceback (most recent call last) <ipython-input-41-60ba6eea244a> in <module>() 6 7 req = urllib2.Request('http://ameli-direct.ameli.fr' + post_url, data) ----> 8 response = urllib2.urlopen(req) 9 the_page = response.read() C:\Python27\lib\urllib2.pyc in urlopen(url, data, timeout) 125 if _opener is None: 126 _opener = build_opener() --> 127 return _opener.open(url, data, timeout) 128 129 def install_opener(opener): C:\Python27\lib\urllib2.pyc in open(self, fullurl, data, timeout) 408 for processor in self.process_response.get(protocol, []): 409 meth = getattr(processor, meth_name) --> 410 response = meth(req, response) 411 412 return response C:\Python27\lib\urllib2.pyc in http_response(self, request, response) 521 if not (200 <= code < 300): 522 response = self.parent.error( --> 523 'http', request, response, code, msg, hdrs) 524 525 return response C:\Python27\lib\urllib2.pyc in error(self, proto, *args) 446 if http_err: 447 args = (dict, 'default', 'http_error_default') + orig_args --> 448 return self._call_chain(*args) 449 450 # XXX probably also want an abstract factory that knows when it makes C:\Python27\lib\urllib2.pyc in _call_chain(self, chain, kind, meth_name, *args) 380 func = getattr(handler, meth_name) 381 --> 382 result = func(*args) 383 if result is not None: 384 return result C:\Python27\lib\urllib2.pyc in http_error_default(self, req, fp, code, msg, hdrs) 529 class HTTPDefaultErrorHandler(BaseHandler): 530 def http_error_default(self, req, fp, code, msg, hdrs): --> 531 raise HTTPError(req.get_full_url(), code, msg, hdrs, fp) 532 533 class HTTPRedirectHandler(BaseHandler): HTTPError: HTTP Error 403: Forbidden
data
'ps_localisation=75013&ps_nom=%27%27&ps_profession_label=Ophtalmologiste&ps_acte_label=%27%27'
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
opener.addheaders.append(('Cookie', 'infosoins=d89o88m733i5ut177eaj2cihi2; AmeliDirectPersist=2990399799.20480.0000'))
opener.addheaders.append(('Referer', 'http://ameli-direct.ameli.fr/'))
opener.addheaders.append(('Host', 'ameli-direct.ameli.fr'))
f = opener.open('http://ameli-direct.ameli.fr' + post_url)
--------------------------------------------------------------------------- HTTPError Traceback (most recent call last) <ipython-input-31-85e060d66a2d> in <module>() 5 opener.addheaders.append(('Host', 'ameli-direct.ameli.fr')) 6 ----> 7 f = opener.open('http://ameli-direct.ameli.fr' + post_url) C:\Python27\lib\urllib2.pyc in open(self, fullurl, data, timeout) 408 for processor in self.process_response.get(protocol, []): 409 meth = getattr(processor, meth_name) --> 410 response = meth(req, response) 411 412 return response C:\Python27\lib\urllib2.pyc in http_response(self, request, response) 521 if not (200 <= code < 300): 522 response = self.parent.error( --> 523 'http', request, response, code, msg, hdrs) 524 525 return response C:\Python27\lib\urllib2.pyc in error(self, proto, *args) 446 if http_err: 447 args = (dict, 'default', 'http_error_default') + orig_args --> 448 return self._call_chain(*args) 449 450 # XXX probably also want an abstract factory that knows when it makes C:\Python27\lib\urllib2.pyc in _call_chain(self, chain, kind, meth_name, *args) 380 func = getattr(handler, meth_name) 381 --> 382 result = func(*args) 383 if result is not None: 384 return result C:\Python27\lib\urllib2.pyc in http_error_default(self, req, fp, code, msg, hdrs) 529 class HTTPDefaultErrorHandler(BaseHandler): 530 def http_error_default(self, req, fp, code, msg, hdrs): --> 531 raise HTTPError(req.get_full_url(), code, msg, hdrs, fp) 532 533 class HTTPRedirectHandler(BaseHandler): HTTPError: HTTP Error 403: Forbidden