2 votes

L'extraction de données sur le Web à l'aide de Beautifulsoup/Selenium ne parvient pas à extraire la classe div à l'aide de find ou find_all.

J'essaie d'extraire des données du site Web. https://roll20.net/compendium/dnd5e/Monsters%20List#content et j'ai quelques problèmes.

Mon premier script que j'ai essayé retournait une liste vide lors de la recherche par div et nom de classe, ce qui, je crois, est dû au fait que le site utilise Javascript ? Mais je ne suis pas certain que ce soit le cas ou pas.

Voici ma première tentative :

import requests
from bs4 import BeautifulSoup
import pandas as pd

page = requests.get('https://roll20.net/compendium/dnd5e/Monsters%20List#content')

soup = BeautifulSoup(page.text, 'html.parser')

card = soup.find_all("div", class_='card')

print(card)

Celui-ci renvoie une liste vide, alors je me suis fatigué à utiliser Selenium et à gratter avec cela. Voici ce script :

import requests
from bs4 import BeautifulSoup
import pandas as pd
from selenium import webdriver

url='https://roll20.net/compendium/dnd5e/Monsters%20List#content'
driver = webdriver.Firefox(executable_path = 'C:\Windows\System32\geckodriver')
driver.get(url)

page = driver.page_source
page_soup = soup(page,'html.parser')

En démarrant le script avec cela, j'ai ensuite essayé ces 3 différentes options (je les ai exécutées individuellement, je les ai juste listées ensemble ici pour des raisons de simplicité) :

for card in body.find('div', {"class":"card"}):
    print(card.text)

print(card)

for card in body.find_all('div', {"class":"card"}):
    print(card.text)

print(card)        

card = body.find_all('div', {"class":"card"})

print(card)

Ils renvoient tous le même message d'erreur : AttributeError : L'objet ResultSet n'a pas d'attribut 'find'. Vous traitez probablement une liste d'éléments comme un seul élément. Avez-vous appelé find_all() alors que vous vouliez appeler find() ?

Où est-ce que je me trompe ici ?

Edita:

Fazul, merci pour votre contribution à ce sujet. Je suppose que je devrais être plus précis. Je cherchais plutôt à obtenir le contenu de chaque carte. Par exemple, la carte a une classe "body" et dans cette classe body il y a de nombreux champs qui sont les données que je cherche à extraire. Peut-être ai-je mal compris votre script et ce que vous avez déclaré. Voici une capture d'écran pour peut-être aider à préciser un peu plus ma question sur le contenu que je cherche à extraire.

enter image description here

Donc tout ce qui se trouverait sous le corps, c'est-à-dire le nom, le titre, le sous-titre, etc. Ce sont les textes que j'essayais d'extraire.

0voto

Ram Points 914

Cette page est chargée par JavaScript. Donc beautifulsoup ne fonctionnera pas dans ce cas. Vous devez utiliser Selenium.

Et l'élément que vous recherchez - <div> avec le nom de la classe comme card se montrer uniquement lorsque vous cliquez sur la flèche déroulante . Puisque vous ne faites pas de click dans votre code, vous obtenez un résultat vide.

  • Utilisez Selenium pour cliquer sur le <div> avec le nom de la classe comme dropdown-toggle . Cet événement de clic charge le <div class='card'>
  • Ensuite, vous pouvez extraire les données dont vous avez besoin.

0voto

Mhd O. Points 23

Comme la page est chargée par JavaScript, vous devez utiliser Selenium.

Ma solution est la suivante :

Chaque carte a un lien comme indiqué : imge

Utilisez BeautifulSoup pour obtenir le lien de chaque carte, puis ouvrez chaque lien en utilisant Selenium en mode sans tête car il est également chargé par JavaScript.

Ensuite, vous obtenez les données dont vous avez besoin pour chaque carte en utilisant BeautifulSoup

Code de travail :

from selenium import webdriver
from webdriver_manager import driver
from webdriver_manager.chrome import ChromeDriver, ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from time import sleep
from bs4 import BeautifulSoup

driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://roll20.net/compendium/dnd5e/Monsters%20List#content")
page = driver.page_source

# Wait until the page is loaded
sleep(13)

soup = BeautifulSoup(page, "html.parser")

# Get all card urls
urls = soup.find_all("div", {"class":"header inapp-hide single-hide"})

cards_list = []
for url in urls:
    card = {}
    card_url = url.a["href"]
    card_link = f'https://roll20.net{card_url}#content'

    # Open chrom in headless mode
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options)
    driver.get(card_link )
    page = driver.page_source
    soup = BeautifulSoup(page, "html.parser")
    card["name"] = soup.select_one(".name").text.split("\n")[1]
    card["subtitle"] = soup.select_one(".subtitle").text
    # Do the same to get all card detealis 

    cards_list.append(card)
    driver.quit()

Vous devez installer la bibliothèque

pip install webdriver_manager

Cette bibliothèque ouvre le pilote de chrome sans avoir besoin de geckodriver et obtient un pilote à jour pour vous.

0voto

https://roll20.net/compendium/compendium/getList?bookName=dnd5e&pageName=Monsters%20List&_=1630747796747

Vous pouvez obtenir les données json à partir de ce lien. Il suffit de les analyser et d'obtenir les données dont vous avez besoin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X