J'essaie d'analyser les noms traversant plusieurs pages d'une page Web à l'aide d'un script python. Avec ma tentative actuelle, je peux obtenir les noms de sa page de destination. Cependant, je ne trouve aucune idée pour récupérer les noms des pages suivantes en utilisant également les requêtes et BeautifulSoup.

lien de site Web

Ma tentative jusqu'à présent:

import requests
from bs4 import BeautifulSoup

url = "https://proximity.niceic.com/mainform.aspx?PostCode=YO95"

with requests.Session() as s:
    r = s.get(url)
    soup = BeautifulSoup(r.text,"lxml")
    for elem in soup.select("table#gvContractors tr:has([id*='_lblName'])"):
        name = elem.select_one("span[id*='_lblName']").get_text(strip=True)
        print(name)

J'ai essayé de modifier mon script pour obtenir uniquement le contenu de la deuxième page pour m'assurer qu'il fonctionne lorsqu'un bouton de page suivante est impliqué, mais malheureusement, il récupère toujours les données de la première page :

import requests
from bs4 import BeautifulSoup

url = "https://proximity.niceic.com/mainform.aspx?PostCode=YO95"

with requests.Session() as s:
    r = s.get(url)
    soup = BeautifulSoup(r.text,"lxml")
    payload = {i['name']:i.get('value','') for i in soup.select('input[name]')}
    payload['__EVENTARGUMENT'] = 'Page$Next'
    payload.pop('btnClose')
    payload.pop('btnMapClose')
    res = s.post(url,data=payload,headers={
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36',
        'X-Requested-With':'XMLHttpRequest',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Referer': 'https://proximity.niceic.com/mainform.aspx?PostCode=YO95',
        })
    sauce = BeautifulSoup(res.text,"lxml")
    for elem in sauce.select("table#gvContractors tr:has([id*='_lblName'])"):
        name = elem.select_one("span[id*='_lblName']").get_text(strip=True)
        print(name)
1
robots.txt 11 févr. 2020 à 09:34

1 réponse

Meilleure réponse

La navigation vers la page suivante est effectuée via une requête POST avec le curseur __VIEWSTATE.

Comment vous pouvez le faire avec les demandes :

  1. Faire une demande GET à la première page ;

  2. Analyser les données requises et le curseur __VIEWSTATE ;

  3. Préparer la requête POST pour la page suivante avec le curseur reçu ;

  4. Exécutez-le, analysez toutes les données et le nouveau curseur pour la page suivante.

Je ne fournirai aucun code, car cela nécessite d'écrire presque tout le code du robot.

==== Ajouté ====

Vous l'avez presque fait, mais il y a deux choses importantes que vous avez manquées.

  1. Il est nécessaire d'envoyer des en-têtes avec la première requête GET. S'il n'y a pas d'en-têtes envoyés - nous obtenons des jetons cassés (il est facile à détecter visuellement - ils n'ont pas == à la fin)

  2. Nous devons ajouter __ASYNCPOST à la charge utile que nous envoyons. (C'est très intéressant : ce n'est pas un booléen True, c'est une chaîne 'true')

Voici le code. J'ai supprimé bs4 et ajouté lxml (je n'aime pas bs4, c'est très lent). Nous savons exactement quelles données nous devons envoyer, alors analysons seulement quelques entrées.

import re
import requests
from lxml import etree


def get_nextpage_tokens(response_body):
    """ Parse tokens from XMLHttpRequest response for making next request to next page and create payload """
    try:
        payload = dict()
        payload['ToolkitScriptManager1'] = 'UpdatePanel1|gvContractors'
        payload['__EVENTTARGET'] = 'gvContractors'
        payload['__EVENTARGUMENT'] = 'Page$Next'
        payload['__VIEWSTATEENCRYPTED'] = ''
        payload['__VIEWSTATE'] = re.search(r'__VIEWSTATE\|([^\|]+)', response_body).group(1)
        payload['__VIEWSTATEGENERATOR'] = re.search(r'__VIEWSTATEGENERATOR\|([^\|]+)', response_body).group(1)
        payload['__EVENTVALIDATION'] = re.search(r'__EVENTVALIDATION\|([^\|]+)', response_body).group(1)
        payload['__ASYNCPOST'] = 'true'
        return payload
    except:
        return None


if __name__ == '__main__':
    url = "https://proximity.niceic.com/mainform.aspx?PostCode=YO95"

    headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Referer': 'https://proximity.niceic.com/mainform.aspx?PostCode=YO95',
            }

    with requests.Session() as s:
        page_num = 1
        r = s.get(url, headers=headers)
        parser = etree.HTMLParser()
        tree = etree.fromstring(r.text, parser)

        # Creating payload
        payload = dict()
        payload['ToolkitScriptManager1'] = 'UpdatePanel1|gvContractors'
        payload['__EVENTTARGET'] = 'gvContractors'
        payload['__EVENTARGUMENT'] = 'Page$Next'
        payload['__VIEWSTATE'] = tree.xpath("//input[@name='__VIEWSTATE']/@value")[0]
        payload['__VIEWSTATEENCRYPTED'] = ''
        payload['__VIEWSTATEGENERATOR'] = tree.xpath("//input[@name='__VIEWSTATEGENERATOR']/@value")[0]
        payload['__EVENTVALIDATION'] = tree.xpath("//input[@name='__EVENTVALIDATION']/@value")[0]
        payload['__ASYNCPOST'] = 'true'
        headers['X-Requested-With'] = 'XMLHttpRequest'

        while True:
            page_num += 1
            res = s.post(url, data=payload, headers=headers)

            print(f'page {page_num} data: {res.text}')  # FIXME: Parse data

            payload = get_nextpage_tokens(res.text)  # Creating payload for next page
            if not payload:
                # Break if we got no tokens - maybe it was last page (it must be checked)
                break

Important

Réponse pas un HTML bien formé. Il faut donc s'en occuper : couper la table ou autre chose. Bonne chance!

3
MadRay 11 févr. 2020 à 18:21