@andie39: Das siehst Du falsch. Das findet nur "borzaska" und nicht alles was genau so lang ist. Und ist unnötig kompliziert und ineffizient. Zum einen weil es ``in`` nachprogrammiert, aber in Python und mit vielen Kopien von Teilzeichenketten, und weil es nach einem Treffer immer noch weiter sucht statt die Suche abzubrechen. Denn wenn `available` einmal `True` ist, kann sich da durch weitersuchen nichts mehr dran ändern.
Der Autor vergleicht dann den Wahrheitswert explizit mit `True` — wo ja wieder der Wert heraus kommt, den `available` sowieso schon hatte. Und später dann das ``for i in range(len(sequence)):`` „anti-pattern“. Das ist offensichtlich kein Python-Programmierer.
Hier ist das gesamte Skript an einem Stück:
Code: Alles auswählen
#! python3
import bs4, requests, smtplib
# ------------------- E-mail list ------------------------
toAddress = ['example1@email.com','example2@email.com']
# --------------------------------------------------------
#Download page
getPage = requests.get('http://www.somesite.com/menu')
getPage.raise_for_status() #if error it will stop the program
#Parse text for foods
menu = bs4.BeautifulSoup(getPage.text, 'html.parser')
foods = menu.select('.foodname')
the_one = 'borzaska' # This is the name of the food you are looking for
flength = len(the_one)
available = False
for food in foods:
for i in range(len(food.text)):
chunk = food.text[i:i+flength].lower()
if chunk == the_one:
available = True
if available == True:
conn = smtplib.SMTP('smtp.gmail.com', 587) # smtp address and port
conn.ehlo() # call this to start the connection
conn.starttls() # starts tls encryption. When we send our password it will be encrypted.
conn.login('youremail@gmail.com', 'appkey')
conn.sendmail('youremail@gmail.com', toAddress, 'Subject: Borzaska Alert!\n\nAttention!\n\nYour favourite food is available today!\n\nBon apetite!:\nFood Notifier V1.0')
conn.quit()
print('Sent notificaton e-mails for the following recipients:\n')
for i in range(len(toAddress)):
print(toAddress[i])
print('')
else:
print('Your favourite food is not available today.')
Ich würde da die She-Bang-Zeile ändern, damit das nicht nur unter Windows läuft.
Die Importe sollten laut PEP8 einzelne Anweisungen sein.
Namensschreibweisen halten sich nicht an die Konventionen.
`toAddress` ist Einzahl, steht aber für mehrere Adressen. Und das ist eine Konstante, die man entsprechend KOMPLETT_GROSS schreiben sollte.
`getPage` ist inhaltlich falsch, weil der Name nach einer Funktion/Methode klingt, nicht nach der Antwort eines HTTP-Servers.
`the_one` wäre auch besser eine Konstante am Anfang des Quelltextes, denn das ist ja etwas das der Benutzer an seinen Geschmack (hihi, Wortspiel) anpassen müsste. Und der Name ist zwar ein bisschen witzig, aber nicht sehr aussagekräftig.
Dann sähe die Schleife als Zwischenstand so aus:
Code: Alles auswählen
favourite_dish_name = FAVOURITE_DISH_NAME.lower()
foods = bs4.BeautifulSoup(response.text, "html.parser").select(".foodname")
available = False
for food in foods:
if favourite_dish_name in food.text.lower():
available = True
break
Das kann man mit `any()` und einem Generatorausdrück deutlich kürzen:
Code: Alles auswählen
favourite_dish_name = FAVOURITE_DISH_NAME.lower()
foods = bs4.BeautifulSoup(response.text, "html.parser").select(".foodname")
available = any(favourite_dish_name in food.text.lower() for food in foods)
Und eigentlich braucht man `available` dann nicht mehr wenn man den Ausdruck direkt in das ``if`` als Bedingung einsetzt.
`smtplib.SMTP`-Objekte sind Kontextmanager, das sollte man also mit ``with`` verwenden.
Die Anmeldedaten wären auch wieder besser am Anfang des Quelltextes als Konstanten aufgehoben.
Ich würde dann ungefähr hier raus kommen:
Code: Alles auswählen
#!/usr/bin/env python3
import smtplib
import bs4
import requests
MENU_URL = "http://www.somesite.com/menu"
FAVOURITE_DISH_NAME = "Borzaska"
SENDER = "youremail@gmail.com"
RECIPIENTS = ["example1@email.com", "example2@email.com"]
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SMTP_USERNAME = SENDER
SMTP_PASSWORD = "********"
def main():
response = requests.get(MENU_URL)
response.raise_for_status()
favourite_dish_name = FAVOURITE_DISH_NAME.lower()
foods = bs4.BeautifulSoup(response.text, "html.parser").select(".foodname")
if any(favourite_dish_name in food.text.lower() for food in foods):
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as smtp:
smtp.ehlo()
smtp.starttls()
smtp.login(SMTP_USERNAME, SMTP_PASSWORD)
smtp.sendmail(
SENDER,
RECIPIENTS,
(
f"Subject: {FAVOURITE_DISH_NAME} Alert!\n"
f"\n"
f"Attention!\n"
f"\n"
f"Your favourite food is available today!\n"
f"\n"
f"Bon apetite!:\n"
f"Food Notifier V1.0"
),
)
print("Sent notificaton email(s) to the following recipients:\n")
for recipient in RECIPIENTS:
print(recipient)
print()
else:
print("Your favourite food is not available today.")
if __name__ == "__main__":
main()
Wobei ich heutzutage nicht mehr davon ausgehen würde, dass das jeder SMTP-Server annimmt, mit nur einem "Subject"-Header. Das mag legal sein, oder zumindest tolerabel aus technischer Sicht, aber es gibt einfach zu viele schlechter Spammer-Skripte und kein ordentlicher E-Mail-Client sendet nur mit dem "Subject"-Header und sonst nix, so dass das für Server ein sinnvolles Filterkriterium sein kann. Und wenn es der SMTP-Server über den die Nachricht versendet wird annimmt, kann das immer noch ein Grund für einen anderen Server auf dem Weg zum Empfänger diese Mail auszusortieren.
Ich würde das übrigens nicht als Crawler bezeichnen. Crawler verarbeiten die Seite und folgen dann Links auf der Seite um weitere Seiten zu verarbeiten. Nur eine Seite nach Informationen absuchen nennt man üblicherweise Scraper.