Python and beautifulsoup4 met dopostback probleem

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • sp_mike
  • Registratie: November 2016
  • Laatst online: 24-05 19:32
Ik heb een redelijk simpel Python script om beursinformatie op te halen. Ik ben een beginner met Python maar dit loopt prima:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen('https://www.guruwatch.nl/adviezen/default.aspx').read()
soup = BeautifulSoup(html, 'lxml')
table = soup.find('table',{'class':'ContentTable'})

rows = table.find_all('tr')

for row in rows:
data = row.find_all('td')

if (len(data) > 0):
    cell = data[0]
    print(cell.text)
    cell = data[1]
    print(cell.text)
    cell = data[2]
    print(cell.text)
    cell = data[3]
    print(cell.text)
    cell = data[4]
    print(cell.text)
    cell = data[5]
    print(cell.text)


Dit pakt netjes de eerste pagina van de tabel en leest de data uit.van: https://www.guruwatch.nl/adviezen/default.aspx
Als ik, handmatig, op "volgende" klik wordt dit uitgevoerd:

code:
1
javascript:__doPostBack('ctl00$ctl00$ContentPlaceHolder1$RightContent$ListAdviezen$cmdNext','')


Is er een manier om dit te automatiseren zodat ik alle data uit kan lezen? Ik las dat het wellicht met "Selenium" maar ik begrijp niet helemaal hoe dat in mijn situatie werkt.

Uiteindelijk wil ik het geautomatiseerd gaan draaien vanaf mijn Synology

Beste antwoord (via sp_mike op 10-01-2022 19:32)


  • DHH
  • Registratie: Augustus 2014
  • Laatst online: 07-09-2024

DHH

Selenium lijkt me inderdaad de makkelijkste optie hiervoor. Het lijkt erop dat Selenium nieuw voor je is, dus ik heb geprobeerd in onderstaand voorbeeld zo veel mogelijk comments te schrijven om e.e.a. te verduidelijken, maar geef het gerust aan als je vragen hebt.

Om het voorbeeld te kunnen gebruiken heb je de geckodriver nodig, welke je hier kan downloaden: https://github.com/mozilla/geckodriver/releases (onderaan de pagina). I.g.v. Windows, pak de .zip uit en sla de .exe op in dezelfde folder als je script (of verwijs bij 'driver = webdriver.Firefox('<gecko-driver-path>') naar de juiste locatie.

Andere browsers zouden ook moeten werken, maar dan zal je even moeten zoeken naar de geckodriver voor de desbetreffende browser.


Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import time

from selenium import webdriver
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

MAX_WAIT = 10

def deal_with_cookies(driver):
    # Zoals je waarschijnlijk gemerkt hebt, moet je cookies accepteren om door te kunnen gaan. 
    # De cookie-wall wordt (met enige vertraging) geladen in een iframe
    # We wachten met het laden tot de iframe beschikbaar is
    element = WebDriverWait(driver, MAX_WAIT).until(EC.presence_of_element_located((By.XPATH, '//*[@id="gdpr-consent-notice"]')))
    driver.switch_to.frame(element)
    # Vervolgens zoeken we de button en klikken we op accepteren
    button = WebDriverWait(driver, MAX_WAIT).until(EC.element_to_be_clickable((By.XPATH, '//button[@id="save"]')))
    # Meestal gebruik je button.click() echter werkte dit in dit geval niet. Geen idee waarom, maar ENTER werkt wel, 
    # verschilt per site/button
    button.send_keys(Keys.ENTER)
    # Terug naar de oorspronkelijke frame met de bruikbare informatie
    driver.switch_to.default_content()

def click_volgende(driver):
    # D.m.v. de browser inspector kan je het element opzoeken. Dit kan o.m. via Xpath, id of name. 
    # Dit element heeft geen 'name', dus ik gebruik 'id'
    button = driver.find_element_by_id('ctl00_ctl00_ContentPlaceHolder1_RightContent_ListAdviezen_cmdNext')
    button.click()

def get_table_data(driver):
    # Nu halen we de tabel met class_name 'ContentTable' op. 
    # Vervolgens zoeken we alle table rows ('tr') in deze tabel en printen we de text per table row.
    # Je zou natuurlijk ook per table row kunnen zoeken naar de onderliggende 'td' voor de content per cell, 
    # maar dat mag je zelf doen :)
    start_time = time.time()
    try:
        table = driver.find_element_by_class_name('ContentTable')
        rows = table.find_elements_by_tag_name('tr')
        print([row.text for row in rows])  # of laadt in een dict / dataframe / database / whatever 
    except (AssertionError, WebDriverException) as e:
        if time.time() - start_time > MAX_WAIT:
            print("Unable to find element, ending script")
            raise e
        time.sleep(0.5)


def main():
    driver = webdriver.Firefox()
    driver.get('https://www.guruwatch.nl/adviezen/default.aspx')
    deal_with_cookies(driver)
    # in dit geval laad ik als voorbeeld pagina's 1 t/m 3, dit wil je misschien aanpassen naar alle pagina's. 
    # maar daar kom je vast wel uit 
    for page in range(1,4):
        print(f"Scraping page {page}.")
        get_table_data(driver)
        click_volgende(driver)
        # Ik laat 5 seconden tussen elke pagina, om wat tijd te geven om de pagina 
        # te laden en de website niet teveel te belasten.
        time.sleep(5)
    driver.quit()

if __name__ == "__main__":
    main()


Mocht je de smaak te pakken krijgen, kijk dan ook altijd even of de website toestaat dat je deze 'crawlt' en of er restricties zijn. Vaak kan je dit vinden op <site>/robots.txt.

Guruwatch heeft geen robots.txt, maar Tweakers bijv. wel: https://tweakers.net/robots.txt, voor meer informatie hierover zie https://developers.google.../robots/create-robots-txt.

Alle reacties


Acties:
  • 0 Henk 'm!

  • thlst
  • Registratie: Januari 2016
  • Niet online
Misschien kun je in de developer tools van je browser zien welke requests worden gemaakt als je op Volgende klikt

Acties:
  • 0 Henk 'm!

  • sp_mike
  • Registratie: November 2016
  • Laatst online: 24-05 19:32
Deze:

code:
1
https://iex.blueconic.net/DG/DEFAULT/rest/rpc/759?referer=https://www.guruwatch.nl/adviezen/default.aspx&bcsessionid=4d4f6ce6-c353-4141-bacd-204e0972fb55&bctempid=&overruleReferrer=&time=2022-01-08T12:30:02+01:00&ts=1641641402958


Helpt dit?

Acties:
  • Beste antwoord
  • 0 Henk 'm!

  • DHH
  • Registratie: Augustus 2014
  • Laatst online: 07-09-2024

DHH

Selenium lijkt me inderdaad de makkelijkste optie hiervoor. Het lijkt erop dat Selenium nieuw voor je is, dus ik heb geprobeerd in onderstaand voorbeeld zo veel mogelijk comments te schrijven om e.e.a. te verduidelijken, maar geef het gerust aan als je vragen hebt.

Om het voorbeeld te kunnen gebruiken heb je de geckodriver nodig, welke je hier kan downloaden: https://github.com/mozilla/geckodriver/releases (onderaan de pagina). I.g.v. Windows, pak de .zip uit en sla de .exe op in dezelfde folder als je script (of verwijs bij 'driver = webdriver.Firefox('<gecko-driver-path>') naar de juiste locatie.

Andere browsers zouden ook moeten werken, maar dan zal je even moeten zoeken naar de geckodriver voor de desbetreffende browser.


Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import time

from selenium import webdriver
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

MAX_WAIT = 10

def deal_with_cookies(driver):
    # Zoals je waarschijnlijk gemerkt hebt, moet je cookies accepteren om door te kunnen gaan. 
    # De cookie-wall wordt (met enige vertraging) geladen in een iframe
    # We wachten met het laden tot de iframe beschikbaar is
    element = WebDriverWait(driver, MAX_WAIT).until(EC.presence_of_element_located((By.XPATH, '//*[@id="gdpr-consent-notice"]')))
    driver.switch_to.frame(element)
    # Vervolgens zoeken we de button en klikken we op accepteren
    button = WebDriverWait(driver, MAX_WAIT).until(EC.element_to_be_clickable((By.XPATH, '//button[@id="save"]')))
    # Meestal gebruik je button.click() echter werkte dit in dit geval niet. Geen idee waarom, maar ENTER werkt wel, 
    # verschilt per site/button
    button.send_keys(Keys.ENTER)
    # Terug naar de oorspronkelijke frame met de bruikbare informatie
    driver.switch_to.default_content()

def click_volgende(driver):
    # D.m.v. de browser inspector kan je het element opzoeken. Dit kan o.m. via Xpath, id of name. 
    # Dit element heeft geen 'name', dus ik gebruik 'id'
    button = driver.find_element_by_id('ctl00_ctl00_ContentPlaceHolder1_RightContent_ListAdviezen_cmdNext')
    button.click()

def get_table_data(driver):
    # Nu halen we de tabel met class_name 'ContentTable' op. 
    # Vervolgens zoeken we alle table rows ('tr') in deze tabel en printen we de text per table row.
    # Je zou natuurlijk ook per table row kunnen zoeken naar de onderliggende 'td' voor de content per cell, 
    # maar dat mag je zelf doen :)
    start_time = time.time()
    try:
        table = driver.find_element_by_class_name('ContentTable')
        rows = table.find_elements_by_tag_name('tr')
        print([row.text for row in rows])  # of laadt in een dict / dataframe / database / whatever 
    except (AssertionError, WebDriverException) as e:
        if time.time() - start_time > MAX_WAIT:
            print("Unable to find element, ending script")
            raise e
        time.sleep(0.5)


def main():
    driver = webdriver.Firefox()
    driver.get('https://www.guruwatch.nl/adviezen/default.aspx')
    deal_with_cookies(driver)
    # in dit geval laad ik als voorbeeld pagina's 1 t/m 3, dit wil je misschien aanpassen naar alle pagina's. 
    # maar daar kom je vast wel uit 
    for page in range(1,4):
        print(f"Scraping page {page}.")
        get_table_data(driver)
        click_volgende(driver)
        # Ik laat 5 seconden tussen elke pagina, om wat tijd te geven om de pagina 
        # te laden en de website niet teveel te belasten.
        time.sleep(5)
    driver.quit()

if __name__ == "__main__":
    main()


Mocht je de smaak te pakken krijgen, kijk dan ook altijd even of de website toestaat dat je deze 'crawlt' en of er restricties zijn. Vaak kan je dit vinden op <site>/robots.txt.

Guruwatch heeft geen robots.txt, maar Tweakers bijv. wel: https://tweakers.net/robots.txt, voor meer informatie hierover zie https://developers.google.../robots/create-robots-txt.

Acties:
  • +1 Henk 'm!

  • sp_mike
  • Registratie: November 2016
  • Laatst online: 24-05 19:32
Enorm bedankt, ik ga het vanavond gelijk proberen.
Het werkt. Ga het nog even finetunen maar ontzettend bedankt!

[ Voor 38% gewijzigd door sp_mike op 10-01-2022 19:32 ]