Scraping des liens d'un résultat de recherche


#1

Bonjour,

Depuis quelques jours j’essaye de scraper un site internet sur ses résultats de recherche.
http://www.mygaloo.fr/annuaire-associations/statut_Registered_NotRegistered_Unknown/

Je recherche à avoir la totalité de liens vers les association exemple :
http://www.mygaloo.fr/association-description/consud

Quand on lance une recherche, le site envoi à API une requête POST avec Access-Control-Allow-Origin pour récupérer ses résultats, 20 résultats de recherches sont alors affiché et un bouton “plus de résultats” s’affiche qui lui aussi envoi une requête POST avec ACAO.

J’ai essayé de lancer un instance de chrome qui simule le scroll et l’appui sur le bouton “Plus de résultats” mais au bout de 300-400 requête plus rien ne s’affiche alors que d’après api il y aurait plus de 50 000 pages disponibles, et je ne comprend pas pourquoi (l’instance ne dépasse pas 1Go de RAM, chrome ne plante pas et au vu du fait que nombres de requêtes pour que cela bug est aléatoire je ne pense pas que cela soit un règles coté Server)

Mon petit programme est en Ruby.

Pourriez-vous m’aider ?

Merci à vous.


#2

Bonsoir,

Comme sur beaucoup de sites de ce type avec soit de l’infinite scroll, soit click sur bouton “Plus de résultats”, il y a de très fortes chances qu’il y ait une limite sur le nombre de pages accessibles. En gros, ils limitent volontairement le nombre de fois où l’utilisateur pourra cliquer sur “Plus de résultats”.

Plusieurs raisons à cela:

  • Pour justement se protéger de bots et autre process de scraping
  • Pour des questions de perf. de leur database, les requêtes de type SELECT […] LIMIT X COUNT Y résultant en de médiocres performances dès lors que la limite X augmente.

C’est un peu comme L-inked-in qui volontairement ne permet que de cliquer sur les 100 premières pages de résultats, pour une recherche donnée.

Du coup, afin de contourner ce problème, il te faudra découper ta recherche principale, en plusieurs sous requêtes. Pour ce faire, il te faudra générer des combinaisons de critères les filtres dispo sur le site: “Secteurs d’activité” , “Vie associative”, “Localités”, “Statut”.


#3

Merci pour ta réponse
Je vais essayer de swap les localités


#4

Bon j’ai regardé en speed mais à priori on peut faire beaucoup plus simple :

L’API en question : hxxp://api.mygaloo.fr/Association/GetAssociationSearchSet?token={-Variable.token-}

a besoin des paramètres suivants (text/plain) :

{“Page”:{-Variable.page_number-},“Rows”:{-Variable.rows_number-},“SearchFilter”:{“KeyLegalStatusSet”:[“Registered”,“NotRegistered”,“Unknown”],“KeyOrder”:“GalooScoringDesc”,“NameOrDescription”:"",“SearchInDescription”:true,“IdPublicationGroupSet”:[“11111111-1111-1111-1111-111111111111”]}}

On se rend compte rapidement que le paramètre “Rows” correspond au nombre de résultats renvoyés.

En testant sur Zenno avec comme paramètre Rows:100000 sur 1 thread tu récupères en moins de 5 secondes 10000 résultats (pas testé au dessus…).

Donc en multithread avec 10 proxies tu récupères 100000 résultats toutes les 5 secondes.Sachant que sur la page d’accueil ils indiquent 1508583 associations ça veut dire que tu peux tout scraper en 1 ou 2 minute en gros.

Reste à parser le JSON derrière (assez long sur Zenno je sais pas avec Ruby).

Le tout sans se prendre la tête en amont avec 1 bot fait en 20 minutes analyse de l’API comprise.

Méthode :

Step 0 : On load 1 proxy privé.

Step 1 : Go to hxxp://www.mygaloo.fr/annuaire-associations/statut_Registered_NotRegistered_Unknown/

Step 2 : On récupère le token par le DOM par exemple en utilisant cette regex : (?<=token=).*?(?=#)

Step 3 : Requête post API : hxxp://api.mygaloo.fr/Association/GetAssociationSearchSet?token={-Variable.token-}

avec comme paramètres :

{“Page”:{-Variable.page_number-},“Rows”:10000,“SearchFilter”:{“KeyLegalStatusSet”:[“Registered”,“NotRegistered”,“Unknown”],“KeyOrder”:“GalooScoringDesc”,“NameOrDescription”:"",“SearchInDescription”:true,“IdPublicationGroupSet”:[“11111111-1111-1111-1111-111111111111”]}}

++

PS : J’ai remplacé les http par des hxxp…


#5

Pourrait tu m’en dire plus parce effectivement j’avais vu la requête API, mais je ne sais pas comment contourner le Access-Control-Allow-Origin


#6

Perso j’utilise Zenno , donc je commence par aller sur la page hxxp://www.mygaloo.fr/annuaire-associations/statut_Registered_NotRegistered_Unknown/ ensuite je fais un “save profile” qui enregistre notamment les cookies et je fais un “load profile” dans la requête post.

Extrait headers :


#7

C’est malheureusement impossible de récupérer le token via cette méthode si le script n’est pas au sein d’un contexte web browser, car ce token est généré à la volée au tout début du loading de la page, via un appel AJAX en POST via l’URL hxxp://api.mygaloo.fr/Auth/GetToken?token=undefined , qui retourne un JSON de type: {“Token”:“rul4pspxxxxxdjegozik3”,“UserInformation”:null}

Le token n’existe pas donc au sein du DOM de base, donc deux solutions:

  • Soit faire une 1ère requête POST en XHR comme indiqué au dessus, pour obtenir le token
  • Soit simplement récupérer le token obtenu via son navigateur web, via un copié collé

En fait le Access-Control-Allow-Origin est uniquement un header retourné via la réponse HTTP, mais n’est en rien bloquant, et ne fait pas partie de header à placer dans la requête à envoyer, tu peux donc totalement ignorer cet aspect.

D’ailleurs, tu peux même récupérer tous les résultats via de basiques requêtes CURL via ligne de commande (voir via petit script en bash), avec cette commande (en remplaçant la valeur du token par le tien, et en paramétrisant les valeurs de “Page” et “Rows”:

curl 'http://api.mygaloo.fr/Association/GetAssociationSearchSet?token=[PUT_YOUR_TOKEN_HERE]' -H 'Origin: http://www.mygaloo.fr' -H 'Accept-Language: en-US,en;q=0.8,fr;q=0.6' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' -H 'Content-Type: text/plain' -H 'Accept: application/json, text/plain, */*' -H 'Referer: http://www.mygaloo.fr/annuaire-associations/statut_Registered_NotRegistered_Unknown/' -H 'Connection: keep-alive' --data-binary '{"Page":2,"Rows":20,"SearchFilter":{"KeyLegalStatusSet":["Registered","NotRegistered","Unknown"],"KeyOrder":"GalooScoringDesc","NameOrDescription":"","SearchInDescription":true,"IdPublicationGroupSet":["11111111-1111-1111-1111-111111111111"]}}'


#8

DOM et token :slight_smile:

La méthode fonctionne sur Zenno ce que j’ai indiqué dans le post en question, pour le reste à vous de voir.


#9

Tu as le token dans ton DOM en contexte browser, après que le Javascript ait dynamiquement modifié le contenu brut du HTML. Lorsque tu requêtes en PHP, Python, ou Ruby, il n’y a pas ces liens avec token dans le HREF…

Il y a une requête AJAX en POST qui vient justement modifié le DOM, DOM que tu obtiens dans un contexte browser.

Preuve en image:

1 - DOM, tel quel, renvoyé par le serveur web distant, avant DOM ready:

2 - DOM ready, après modification Javascript et requêtes AJAX (contexte in browser):


#10

Mais personne n’a dit le contraire @ScrapingExpert. Juste que dans la méthode évoquée (avec Zenno) on récupère bien le token avec de DOM car en contexte browser dans les premiers steps, ensuite on fait la POST request (similaire à curl sur Zenno) en utilisant le token récupéré avant avec la regex d’exemple.


#11

Je disais ça pour notre amigo… :wink:


#12

Merci les amis pour vos réponses, je pensais à tort que le Access-Control-Allow-Origin bloqué les requêtes ne venant pas du site, car j’avais essayé d’accéder à l’API et j’avais une erreur à chaque fois.

Suite à ton message hier @karni j’ai réessayé hier soir et je suis arrivais à utiliser les API, me reste plus qu’à faire un petit programme, pour automatiser tout ça.

En ce qui concerne le token je vais le copié collé il est valable pas mal de temps.

Pour finir @karni tu as fait le test avec 100 000 rows ? parce que moi si je met plus que 1000 rows, l’API me sort une erreur.

Merci encore


#13

@Knp: J’ai lancé un process test avec 1 000 rows par requêtes, soit 1509 requêtes à faire, avec une seule IP, et un seul token (copié collé depuis le browser). Ca tourne niquel, aucun blocage de leur API.

Bon par contre, c’est assez long (j’ai mis un random wait de 5-15 seconds entre chaque requête). Ca tourne depuis 14H, il est minuit passé, on en est à 1220 pages / 1509, avec 5Go de fichiers JSONs.

Enjoy !


#14

:heart_eyes::heart_eyes:


#15

J avais teste avec 10000 par thread sur 10 threads (10 IP , 10 tokens simultanés) mais ça semble bloquer now ,j ai reteste avec 3000 par thread et c est bon.


Team : CamilleBriceJulienVivianBorisXavierSteven.
Follow @growthhackingfr