J'essaie de récupérer tous les liens d'articles d'un site et ai-je réussi à le faire?
La page du site dispose d'un bouton Afficher plus
pour charger plus d'articles.
J'utilise Selenium pour cliquer sur ce bouton qui fonctionne également.
Le problème est que cliquer sur Afficher plus
ne change pas l'URL de la page, donc je ne peux gratter que les liens initiaux affichés par défaut. p>
Voici l'extrait de code:
def startWebDriver(): global driver options = Options() options.add_argument("--disable-extensions") driver = webdriver.Chrome(executable_path = '/home/Downloads/chromedriver_linux64/chromedriver',options=options) startWebDriver() count = 0 s = set() driver.get('https://www.nytimes.com/search? endDate=20181231&query=trump&sort=best&startDate=20180101') time.sleep(4) element = driver.find_element_by_xpath('//*[@id="site-content"]/div/div/div[2]/div[2]/div/button') while(count < 10): element.click() time.sleep(4) count=count+1 url = driver.current_url
Je m'attends à voir tous les liens d'articles affichés sur la page après avoir cliqué sur Afficher plus
10 fois
3 Réponses :
Il semble que votre ressource cible nous donne une belle API pour leurs articles.
Il sera beaucoup plus facile de l'utiliser à la place du sélénium.
Vous pouvez ouvrir cette page dans Chrome. Ensuite, ouvrez Dev Tools -> Network. Cliquez sur "Afficher plus" et vous pouvez voir la requête d'API nommée v2 (on dirait qu'il s'agit d'une passerelle GraphQL).
Quelque chose comme
{ "operationName":"SearchRootQuery", "variables":{ "first":10, "sort":"best", "beginDate":"20180101", "endDate":"20181231", "text":"trump" ... }}
Vous pouvez imiter cette demande mais demander autant de "premiers" articles que vous le souhaitez.
MODIFIER :
Vous pouvez cliquer avec le bouton droit de la souris dans DevTools et sélectionner "copier en tant que cURL". Puis collez-le sur votre terminal. Vous pouvez donc voir comment cela fonctionne.
Ensuite, vous pouvez utiliser une bibliothèque comme demande de le faire à partir de votre code.
Merci .. comment imiter cette demande?
Voici une imitation d'une requête POST utilisant les informations API comme je le vois dans l'onglet réseau. Je suis revenu aux en-têtes qui semblent obligatoires.
import requests url = 'https://samizdat-graphql.nytimes.com/graphql/v2' headers = { 'nyt-app-type': 'project-vi', 'nyt-app-version': '0.0.3', 'nyt-token': 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlYOpRoYg5X01qAqNyBDM32EI/E77nkFzd2rrVjhdi/VAZfBIrPayyYykIIN+d5GMImm3wg6CmTTkBo7ixmwd7Xv24QSDpjuX0gQ1eqxOEWZ0FHWZWkh4jfLcwqkgKmfHJuvOctEiE/Wic5Qrle323SMDKF8sAqClv8VKA8hyrXHbPDAlAaxq3EPOGjJqpHEdWNVg2S0pN62NSmSudT/ap/BqZf7FqsI2cUxv2mUKzmyy+rYwbhd8TRgj1kFprNOaldrluO4dXjubJIY4qEyJY5Dc/F03sGED4AiGBPVYtPh8zscG64yJJ9Njs1ReyUCSX4jYmxoZOnO+6GfXE0s2xQIDAQAB' } data = ''' {"operationName":"SearchRootQuery","variables":{"first":10,"sort":"best","beginDate":"20180101","text":"trump","cursor":"YXJyYXljb25uZWN0aW9uOjk="},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"d2895d5a5d686528b9b548f018d7d0c64351ad644fa838384d94c35c585db813"}}} ''' with requests.Session() as r: re = r.post(url, headers = headers, data = data) print(re.json())
Pour gratter tous les liens d'article, c'est-à-dire les attributs href
de URL en cliquant sur le lien avec le texte comme AFFICHER PLUS , vous pouvez utiliser la solution suivante:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException options = webdriver.ChromeOptions() options.add_argument("start-maximized") options.add_argument('disable-infobars') driver = webdriver.Chrome(chrome_options=options, executable_path=r'C:\Utility\BrowserDrivers\chromedriver.exe') driver.get("https://www.nytimes.com/search?%20endDate=20181231&query=trump&sort=best&startDate=20180101") myLength = len(WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.XPATH, "//main[@id='site-content']//figure[@class='css-rninck toneNews']//following::a[1]")))) while True: driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") try: WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//button[text()='Show More']"))).click() WebDriverWait(driver, 20).until(lambda driver: len(driver.find_elements_by_xpath("//main[@id='site-content']//figure[@class='css-rninck toneNews']//following::a[1]")) > myLength) titles = driver.find_elements_by_xpath("//main[@id='site-content']//figure[@class='css-rninck toneNews']//following::a[1]") myLength = len(titles) except TimeoutException: break for title in titles: print(title.get_attribute("href")) driver.quit()
avez-vous utilisé
set
sur les données affichées dans la première itération aux deuxième et troisième?oui .. J'utilise set pour collecter tous les liens d'articles
Le site ne change pas et ne changera pas l'URL sur laquelle vous vous trouvez, car il récupère les nouvelles données à afficher par ajax. Lorsque vous cliquez sur "Afficher plus", l'interface JS envoie une requête au serveur pour obtenir plus de données, et lorsque cela est rempli, la réponse est ajoutée au HTML de la page actuellement chargée; par exemple. une page dynamique triviale.