Fehler bei Abfrage (Tuples)

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

rogerb hat geschrieben: Samstag 11. September 2021, 16:51 @Assassin4711,

dies ist zumindest mal ein Anfang. Es wird zunächst unterschieden ob es Zeile für den Ort oder ein Match ist. Dann werden die einzelnen Zellen in Variablen gespeichert. Aber das kannst du ja nach deinen Bedürfnissen anpassen.

Code: Alles auswählen

from selenium import webdriver


def main():
    browser = webdriver.Chrome('C:\webdrivers\Chromedriver.exe')
    browser.get("https://www.oddsportal.com/matches/tennis/")

    # tabelle mit den Matches
    match_table = browser.find_element_by_id("table-matches")
    rows = match_table.find_elements_by_tag_name("tr")

    for row in rows:
        if "dark" in row.get_attribute("class"):
            # dark: alle Zeilen die den Autragungsort enthalten
            cells = row.find_elements_by_tag_name("th")

            data_field1 = cells[0].text.replace("\n", "").strip()
            data_field2 = cells[1].text.replace("\n", "").strip()
            # usw.

        elif "deactivate" or "odd" in row.get_attribute("class"):
            # deactivate oder odd: alle Zeilen, die ein Match enthalten
            cells = row.find_elements_by_tag_name("th")

            data_field1 = cells[0].text.replace("\n", "").strip()
            data_field2 = cells[1].text.replace("\n", "").strip()
            # usw.


if __name__ == "__main__":
    main()
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

eh wo ist mein Text hin ...

Frage /n ist ein Zeilenumbruch?

Und zweite Frage, wie kann ich die Zeile mit der Uhrzeit und den Spielen ansprechen? Ich habe es so probiert, aber das geht leider nicht:

Code: Alles auswählen

        elif "table-time" in row.get_attribute("class"):
            cells = row.find_elements_by_tag_name("tr")
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

so jetzt dachte ich ich hätte die Lösung gefunden, aber leider gibt es noch einen kleinen Hinkelstein:

Code: Alles auswählen

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import pandas as pd

#def main():
driver = webdriver.Chrome('C:\webdrivers\Chromedriver.exe')
driver.get("https://www.oddsportal.com/matches/tennis/")
    # tabelle mit den Matches
match_table = driver.find_element_by_id("table-matches")
rows = match_table.find_elements_by_tag_name("tr")
    
for row in rows:
        if "dark" in row.get_attribute("class"):
            # dark: alle Zeilen die den Autragungsort enthalten
            cells = row.find_elements_by_tag_name("a")

            data_field1_country = cells[0].text.replace("\n", "").strip()
            data_field2_tournament = cells[1].text.replace("\n", "").strip()
            
            # usw.

        elif not "dark" in row.get_attribute("class"):
            cells = row.find_elements_by_tag_name("td")

            data_field3_time = cells[0].text.replace("\n", "").strip()
            data_field4_player = cells[1].text.replace("\n", "").strip()
            data_field5_homeodd = cells[2].text.replace("\n", "").strip()
            data_field6_awayodd = cells[3].text.replace("\n", "").strip()
  
            print (data_field1_country + ";" + data_field2_tournament + ";" + data_field3_time + ";" \
                   + data_field4_player + ";" + data_field5_homeodd + ";" + data_field6_awayodd)   
Wenn die Spiele live sind erscheint neben der Quote das Live Ergebnis. Und anscheinend wird das dann als neue Spalte angesehen. Bei live Spielen wird dann das Ergebnis als Heim Quote erkannt. Wie kann ich denn den value der Spalte auf z.B. ":" (Ergebnisse haben ja immer dieses Zeichen mit drin) prüfen und in dem Fall dann eine Spalte daneben nehmen?
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

habe es jetzt so gelöst, vielleicht nicht schön aber es funktioniert erstmal:

Code: Alles auswählen

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import pandas as pd

#def main():
driver = webdriver.Chrome('C:\webdrivers\Chromedriver.exe')
driver.get("https://www.oddsportal.com/matches/tennis/")
    # tabelle mit den Matches
match_table = driver.find_element_by_id("table-matches")
rows = match_table.find_elements_by_tag_name("tr")
    
for row in rows:
        if "dark" in row.get_attribute("class"):
            # dark: alle Zeilen die den Autragungsort enthalten
            cells = row.find_elements_by_tag_name("a")

            data_field1_country = cells[0].text.replace("\n", "").strip()
            data_field2_tournament = cells[1].text.replace("\n", "").strip()
            
            # usw.

        elif not "dark" in row.get_attribute("class"):
            cells = row.find_elements_by_tag_name("td")

            data_field3_time = cells[0].text.replace("\n", "").strip()
            data_field4_player = cells[1].text.replace("\n", "").strip()
            if ":" in cells[2].text.replace("\n", "").strip():
                data_field5_homeodd = cells[3].text.replace("\n", "").strip()
                data_field6_awayodd = cells[4].text.replace("\n", "").strip()
            elif not ":" in cells[2].text.replace("\n", "").strip():
                data_field5_homeodd = cells[2].text.replace("\n", "").strip()
                data_field6_awayodd = cells[3].text.replace("\n", "").strip()
            

            print (data_field1_country + ";" + data_field2_tournament + ";" + data_field3_time + ";" \
                   + data_field4_player + ";" + data_field5_homeodd + ";" + data_field6_awayodd)
 
DANKE euch allen. Werde euch bestimmt bald wieder nerven ...
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Assassin4711: Bei Zeichenkettenliteralen mit Backslashes sollte man entweder alle escapen oder ein ”rohes” Zeichenkettenliteral verwenden.

Den Backslash um logische Zeilen fortzuführen braucht man nur sehr selten. Solange es noch ausstehende schliessende Klammern gibt, wie bei dem `print()`-Aufruf, weiss der Compiler auch so, dass die logische Zeile noch nicht zuende sein kann.

Statt die Zeilen in zwei Schritten über ID und Tag-Namen zu ermitteln, könnte man das auch in *einen* CSS-Selektor stecken.

Statt ``not a in b`` würde man eher ``a not in b`` schreiben. Ersteres sind zwei Operatoren, letzteres nur ein Operator. Jup ``not in`` ist *ein* Operator der aus zwei Schlüsselwörtern besteht.

Aber die Bedingung braucht man nicht, denn da wird ja nur das Gegenteil von dem ``if`` davor geprüft, und dazu braucht man keine explizite Prüfung, da kann man einfach ``else`` verwenden.

Die nummerierten `data_field*`-Präfixe sollten nicht sein.

Ist das ``.replace("\n", "")`` vor jedem ``.strip()`` tatsächlich notwendig/sinnvoll? `strip()` entfernt auch Zeilenendezeichen an beiden Enden. Das `replace()` bringt also nur etwas wenn auch Zeilenendezeichen innerhalb der Zeichenkette vorkommen. Dann ist aber das ersetzen durch eine leere Zeichenkette eventuell keine gute Idee, sondern man möchte da vielleicht eher ein Leerzeichen haben, weil sonst das Ende von der ersten Zeile *direkt* an den Anfang der nächsten Zeile angefügt wird. Aber auf den ersten Blick sehen die Daten nicht aus, als wären da mehrzeilige Texte‽ Auch im Code sieht das eher so aus als wenn das einfach Cargo-Cult-mässig einfach an jeden Zugriff auf `text`-Attribute drangehängt ist, denn um zu prüfen ob ein Doppelpunkt in einem Text enthalten ist, braucht man weder Zeilenendezeichen entfernen, noch Whitespace an den beiden Enden.

Bei dem Test auf ":" ist auch wieder unnötigerweise ein ``elif`` mit genau der gegensätzlichen Bedingung statt einfach einem ``else``.

Die beiden Zweige machen auch nahezu das gleiche, die unterscheiden sich nur durch den Versatz welche beiden Tabellenzellen verwendet werden.

Ich würde den Code so schreiben, dass er nicht in einen `NameError` läuft, wenn nicht zuerst ein Spielort in der Tabelle erkannt wurde.

Zusammenstückeln von Zeichenketten und Werten mit ``+`` ist eher BASIC als Python. In diesem Fall soll die gleiche Zeichenkette zwischen Werte gesetzt werden. Dafür gibt's die `join()`-Methode auf Zeichenketten. Falls das am Ende in einer CSV-Datei landen soll, würde sich das `csv`-Modul aus der Standardbibliothek anbieten. Das kann nämlich auch mit so Sachen wie Trennzeichen *innerhalb* von Zellen korrekt umgehen.

Code: Alles auswählen

#!/usr/bin/env python3
from selenium import webdriver


CHROME_DRIVER = R"C:\webdrivers\Chromedriver.exe"


def iter_stripped_texts(elements, start_index=None, end_index=None):
    return (
        element.text.strip()
        for element in elements[slice(start_index, end_index)]
    )


def main():
    driver = webdriver.Chrome(CHROME_DRIVER)
    driver.get("https://www.oddsportal.com/matches/tennis/")
    country = tournament = None
    for row in driver.find_elements_by_css_selector("#table-matches tr"):
        if "dark" in row.get_attribute("class"):
            # dark: alle Zeilen die den Autragungsort enthalten
            country, tournament = iter_stripped_texts(
                row.find_elements_by_tag_name("a")
            )
        else:
            if country is None:
                raise ValueError(
                    "expected row with country and tournament first."
                )

            cells = row.find_elements_by_tag_name("td")
            time, player = iter_stripped_texts(cells, 0, 2)
            offset = 3 if ":" in cells[2].text else 2
            home_odd, away_odd = iter_stripped_texts(cells, offset, offset + 2)

            print(
                ";".join(
                    [country, tournament, time, player, home_odd, away_odd]
                )
            )


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

Vorab: Vielen DANK

Puh das ist schon manchmal der Wahnsinn wie gut man sich mit irgendwas auskennen kann. Ich habe bis dato immer nur VBA programmiert und das auch immer nur so, dass ich mein Problem gelöst bekommen habe.

Python ist eben für mich nochmal ein Buch mit "Sieben Siegeln" ... Einfach weil die Art wie man den Code schreibt, bzw. schreiben muss nochmal komplett anders ist. Es macht ab und an anscheinend auch nochmal unterschied ob ich den Befehl einrücke oder nicht. Darauf zu achten Das war in VBA eher nur optisch von Nöten.

Und vieles von dir geschriebene, für das ich sehr dankbar bin, verstehe ich komplett null 8)

Das mit dem elif ist mir jetzt nach deiner Schilderung auch aufgefallen. Macht wahrlich wenig Sinn ...

Ich hätte jetzt u deinem Code viele viele Fragen, dass würde dir aber unnötig Zeit rauben.

Doch die ein oder andere muss ich stellen:

was bedeutet das

Code: Alles auswählen

if __name__ == "__main__":
    main()
Und eine weitere Frage wie bekomme ich Überschriften hin damit ich dann (nächste Frage) die Daten in eine csv Datei überführen kann?
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Assassin4711,

Code: Alles auswählen

if __name__ == "__main__":
    main()
Nennt man "import guard", oder Import Schutz.
Wie du siehst wird die main() Funktion nur aufgerufen, wenn die Bedingung erfüllt ist. Das ist immer dann der Fall, wenn das Script direkt ausgeführt wird. Dann ist __name__ nämlich gleich "__main__". Wenn dieses Modul von anderen Modulen importiert wird, ist die Bedingung nicht erfüllt, denn __name__ würde dann den Namen des Moduls und nicht mehr "__main__" enthalten.
Also einfach gesagt die main() Funktion kann nur laufen wenn man dieses Modul direkt ausführt.

Die andere Frage verstehe ich leider nicht. "wie bekomme ich Überschriften hin"?
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

Na wenn ich das Ergebnis in eine csv Datei packen will. Im dieser dann anschließend in Excel einzulesen dann benötige ich dich auch Überschriften. Und die würde ich eben schon gerne mit in der csv stehen haben.
Und wie erstelle ich Erbengemeinschaft eine csv?
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

Also Überschriften für die einzelnen Spalten.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die erste Zeile enthält die Überschriften.
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

Aber nicht wenn ich die Daten über den print Befehl anzeigen lasse. Da bedingt jupyter direkt mit dem ersten Datensatz.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ändert nichts an Antwort. So kommen nunmal Überschriften in csv Dateien. Was du mit jupyter und Print machst, ist bis dato dein Geheimnis. Kann man also auch nichts zu sagen.
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

Hmm jetzt steh ich auf dem Schlauch. Also den Code schreibe ich im jupyter Notebook. Und da bekomme ich ja, wenn ich das Programm laufen lasse unten das Ergebnis angezeigt. Und bei diesem Ergebnis habe ich keine Überschriften.
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

ich habe die Überschrift jetzt mit folgender Zeile hinbekommen:

Code: Alles auswählen

    überschrift = ['Land' , 'Tunier' , 'Startzeit' , 'Spieler' , 'Heim_Quote' , 'Auswärts_Quote']
    print(";".join(überschrift))
Wenn ich das Ergebnis kopiere und in eine Textdatei ablege kann ich diese auch in Excel öffnen. Kann man die daten auch anstatt mit dem Printbefehl anzuzeigen auch direkt in eine csv auf dem Laufwerk abspeichern?
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Ja, kann man mit dem eingebauten csv Modul.
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

einfachTobi hat geschrieben: Dienstag 14. September 2021, 09:25 Ja, kann man mit dem eingebauten csv Modul.
Dank dir für den Literatur Hinweis.

So ich habe jetzt mal versucht das neu gelernte auf eine weitere Tabelle anzuwenden:

Dazu habe ich den Code im oberen Bereich wie folgt geändert:

Code: Alles auswählen

from selenium import webdriver

CHROME_DRIVER = R"C:\webdrivers\Chromedriver.exe"


def iter_stripped_texts(elements, start_index=None, end_index=None):
    return (element.text.strip() for element in elements[slice(start_index, end_index)])


def main():
    driver = webdriver.Chrome(CHROME_DRIVER)
    driver.get("https://www.oddsportal.com/tennis/usa/cary-2-challenger-men/krueger-mitchell-kubler-jason-8MNxIA25/")
    
    country = tournament = None
    
    überschrift = ['Buchmacher' , 'Heim_Quote' , 'Away_Quote' , 'Payout']
    print(";".join(überschrift))
    
    for row in driver.find_elements_by_css_selector("#odds-data-table > div:nth-child(1) > table > tbody"):
Ziel ist es in der Tabelle die Buchmacher und deren Quoten aufzulisten.

Ich tappe aber bei dieser Zeile komplett im dunkeln was hier gemacht wird:

Code: Alles auswählen

def iter_stripped_texts(elements, start_index=None, end_index=None):
    return (element.text.strip() for element in elements[slice(start_index, end_index)])
Also habe ich einfach in den folgenden Zeilen Werte verändert um festzustellen wie sich das Ergebnis verändert.
Da ich der Tabelle jetzt kein Problem mit Zeilen habe die nicht die gewünschten Daten enthalten brauche ich auch den Zellinhalt nicht zu prüfen. Also war meine Idee die Daten für die Liste direkt in die Variablen einzuspielen.

Code: Alles auswählen

            cells = row.find_elements_by_tag_name("tr")
            bookie = iter_stripped_text(row.find_elements_by_tag_name("a"))
Aber leider funktioniert das so nicht.

Was habe ich falsch gemacht, bzw. wo habe ich falsch gedacht?
Assassin4711
User
Beiträge: 77
Registriert: Mittwoch 8. September 2021, 14:22

ahh ich habs hinbekommen ... ich hatte den falschen Selector ausgewählt...

Danke euch Jungs ...
Antworten