@Zephry: Die Klasse ist sehr komisch. Das sieht eher aus wie normale Funktionen die aus irgend einem nicht ersichtlichen Grund in eine Klasse gestopft wurden.
Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Klassennamen werden in Python ”MixedCase” geschrieben. Also `Crawl`, wobei das vom Inhalt eher ein Funktionsname wäre, weil es eine Tätigkeit beschreibt. Als Klasse würde das `Crawler` heissen. Wobei wie gesagt die Klasse IMHO überhaupt gar keinen Sinn macht.
`quit()` gibt es so eigentlich gar nicht. Das existiert zwar global, weil das für die interaktive Python-Shell dokumentiert ist, aber nicht für Programme. Es ist auch total unsauber und unübersichtlich welche Methoden/Funktionen hier die Macht haben einfach so das gesamte Programm abzubrechen. Das `quit()` am Ende des Programs ist auch überflüssig. Man verwendet am besten gar keine Aufrufe die das Programm abbrechen, sondern strukturiert den Programmablauf ordentlich, so dass er zu einem ”natürlichen" Ende kommt. Falls man dem aufrufenden Prozess einen anderen Rückgabecode als 0 liefern möchte, *dann* kann man `sys.exit(return_code)` dafür verwenden. In der `main()`-Funktion, oder einer die semantisch Nahe mit dem Hauptprogramm verwandt ist. Nicht in Funktionen die einzeln für sich eine Programmlogik bezogene Tätigkeit durchführen.
Die ganzen `print()`-Ausgaben gehören eigentlich auch nicht in Funktionen mit Programmlogik. Da könnte man Logging betreiben, aber keine unbedingte Kommunikation mit dem Benutzer.
`get_post()` ist als Name inhaltlich Falsch. Weder das `get` stimmt — bei so einem Namen erwartet man, dass die Methode/Funktion etwas als Ergebnis liefert — noch `post` als Einzahl stimmt, und die Methode/Funktion macht auch noch deutlich mehr vorarbeit, die man nicht bei dem Namen vermuten würde.
Bei `get_posts()` haben wir wieder einen irreführenden Namen, weil die Methode/Funktion überhaupt gar nichts zurück gibt. Sollte sie vielleicht, denn sie sammelt ja Daten aus der Webseite, die als Ergebnis taugen würden.
Das `articles` und `num_articles` erst an eine Zeichenkette gebunden werden um dann nach der Schleife über die Zeichen von `articles` den gleichen Namen an eine Zahl zu binden ist extrem verwirrend. `i` ist auch kein Name für was man für etwas anderes als eine ganze Zahl verwenden sollte, *insbesondere* wenn das auch noch eine Laufvariable ist. In der gleichen Funktion wird dann später `i` auch an ganze Zahlen gebunden. Das man in Python innerhalb der gleichen Funktion den selben Namen nacheinander an alle möglichen Werte mit den verschiedensten Datentypen binden *kann*, heisst nicht, dass das auch nur annähernd eine gute Idee ist das auch zu *tun*.
Das hier:
Code: Alles auswählen
num_articels = ""
for i in articels:
if i != ".":
num_articels = num_articels + i
articels = int(num_articels)
Ist eine recht umständliche und nicht leicht lesbare Art das hier zu schreiben:
Wobei auch hier immer noch gilt, dass es eher keine gute Idee ist, dass `articels` vorher an eine Zeichenkette und nachher an eine Zahl gebunden ist.
`found` ist IMHO zu generisch benannt, weil man sich erst den Code anschauen muss um eine Idee davon zu bekommen was diese Liste dann tatsächlich enthält. `image_paths` oder so etwas in der Richtung wäre besser. Und da die beiden Operationen darauf hinzufügen von Elementen und testen ob Element schon enthalten sind, würde sich hier eine Menge statt einer Liste anbieten.
`i` und `iterat` sind auch keine guten Namen. Das eine ist der Bildindex bezogen auf alle Bilder, und das andere der Index relativ zum angezeigten Webseitenausschnitt. Das sollte man den Namen mindestens ansatzweise entnehmen können.
Wobei `i` auch redundant ist, denn das ist immer die Länge von `found` wenn ich das richtig sehe‽
Zeichenketten und Werte mit ``+`` und `str()` zusammenstückeln ist eher BASIC als Python. In Python gibt es dafür Zeichenkettenformatierung mit der `format()`-Methode, und ab Python 3.6 f-Zeichenkettenliterale.
Wenn man zusätzlich zu den Elementen eines iterierbaren Objekts noch eine laufende Zahl braucht, nimmt man die `enumerate()`-Funktion dafür.
Ganz wichtig: Nur Ausnahmen behandeln, die man auch erwartet und für die die Behandlung die man da macht auch wirklich garantiert sinnvoll ist. Nackte ``except``\s ohne konkrete Ausnahmen darf man eigentlich nur benutzen wenn man die Ausnahme protokollieren möchte und sie dann wieder auslöst, oder wenn an der Stelle 100% sicher ist, dass danach kein Code mehr ausgeführt wird.
Ungetesteter Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
#
# FIXME All ``except``\s should only handle explicitly know exception and not
# just *all* possible exceptions!
#
def create_chrome_session():
options = webdriver.ChromeOptions()
# options.add_argument("headless")
# options.add_argument("disable-gpu")
# options.add_argument("--log-level=3")
return webdriver.Chrome("chromedriver.exe", options=options)
def login(browser, username, password):
browser.get("https://www.instagram.com/accounts/login/")
sleep(1)
browser.find_element_by_name("username").send_keys(username)
password_element = browser.find_element_by_name("password")
password_element.send_keys(password)
password_element.send_keys(Keys.ENTER)
sleep(6)
try:
browser.find_element_by_xpath("(//div[@class='_47KiJ'])")
return True
except:
return False
def get_posts(browser):
try:
article_count_as_text = browser.find_element_by_xpath(
"(//span[@class='g47SY '])"
).get_attribute("innerHTML")
except:
return None
else:
article_count = int(article_count_as_text.replace(".", ""))
image_paths = set()
image_index = 1 # Index within viewport.
#
# FIXME Potential endless loop.
#
while len(image_paths) < article_count:
try:
image_path = browser.find_element_by_xpath(
f"(//img[@class='FFVAD'])[{image_index}]"
).get_attribute("src")
except:
browser.execute_script(
"window.scrollTo(0, document.body.scrollHeight);"
)
image_index = 1
sleep(3)
else:
image_paths.add(image_path)
#
# TODO Doesn't belong here. Replace with logging or callback.
#
print(f"[{len(image_paths)}/{article_count}]")
image_index += 1
return image_paths
def main():
crawler = Crawler(
"Benutezrname", "Passwort", "https://instagram.com/USERNAME"
)
print("Chrome Sitzung wird vorbereitet und geöffnet...")
try:
browser = create_chrome_session()
except:
print(
"\nDer Google Chrome Treiber wurde nicht gefunden...\n"
"Downloade den zu passenden Treiber und verschiebe ihn mit dem"
" Namen 'chromedriver.exe' in das aktuelle Verzeichnis"
)
else:
print("Login wird versucht...")
if not login(browser, "Benutzername", "Passwort"):
print("\nBenutzername oder Passwort waren nicht korrekt...")
else:
print("Login erfolgreich")
user_url = "https://instagram.com/USERNAME"
print(f"{user_url!r} wird gesucht...")
try:
browser.get(user_url)
except:
print(
f"\nEs scheint so, als ob {user_url!r} keine gültige"
f" Instagram-Adresse sei\n"
f"Bitte verwende folgendes Pattern:"
f" https://instagram.com/USERNAME"
)
else:
image_paths = get_posts(browser)
if image_paths is None:
print("\nBenutzer wurde nicht gefunden...")
else:
for number, image_path in enumerate(image_paths, 1):
print(f"{number} {image_path}\n")
if __name__ == "__main__":
main()