Web-Page mit Refresh + Simple Web-Crawler gleichzeitig

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

Hallo liebes Forum,

in einer Anwendung auf einem ESP32 D1 Mini NodeMCU Board stelle ich im Heimnetz eine kleine Web-Page zur Verfügung die regelmäßig automatisch aktualisiert wird, und gleichzeitig benötigt die Anwendung Informationen aus dem Internet. Da die Internetseite sehr klein und übersichtlich ist, kann ich den Response-Text direkt mit einfachen Python-Befehlen auslesen und benötige kein BeautifulSoup etc.

Da die Applikation inzwischen recht umfangreich ist, habe ich die für die Frage relevanten Techniken in ein kleines Test-Programm übertragen (das Test-Programm selbst ist zugegebenermaßen unsinnig und hat auch keine Auswertung durch den Crawler).

Beide Funktionen funktionieren, bis bei der Abfrage der Internet-Seite einmalig eine Exception -202 erfolgt. Davon "erholt" sich das Programm nicht mehr. Die bereitgestellte Web-Page im Heimnetz funzt aber noch, und auch die damit verbundene Funktion (LED ein/aus).
Ich konnte leider keinen Hinweis auf die Bedeutung des Exception-Codes "-202" bekommen.
Er tritt immer dann auf, wenn auf der Web-Page im Heimnetz eine Aktion erfolgt (Tasten-Druck oder automatischer Refresh) und gleichzeitig der request.get von meinem Web-Crawler durchgeführt wird. In meiner eigentlichen Applikation habe ich noch einen Mechanismus ergänzt, der die Refreshzeit erhöht, wenn der Fehler einmalig aufgetreten ist. Das hat die Laufzeit erheblich verlängert, aber nach einem halben Tag funktioniert auch dann keine einzige Crawer-Abfrage mehr (der Rest läuft fehlerfrei weiter). Da ich mir diesen Mechanismus in diesem Test-Programm erspart habe ist hier die Refresh-Zeit von eigentlich 2 auf 3 Sekunden erhöht.

Nun die 3 Fragen:
  • Was kann ich generell tun, um die beiden Funktionen sicherer laufen zu lassen?
  • Was bedeutet die Exception -202?
  • Wie kann ich detailliertere Fehlerinformationen in dem Programm abrufbar machen?

Und hier das Test-Programm:

Code: Alles auswählen

try:
    import urequests as requests
except:
    import requests

# Complete project details at https://RandomNerdTutorials.com  !BUT MODIFIED!

from time import sleep

try:
  import usocket as socket
except:
  import socket
  


from machine import Pin
import network

import esp
esp.osdebug(None)

import gc
gc.collect()

ssid = 'XXXXXX'       ####
password = 'YYYYYYY'  ####

station = network.WLAN(network.STA_IF)          
station.active(True)
station.connect(ssid, password)


for retry in range(100):
    connected = station.isconnected()
    if connected:
        print('Connection successful')
        print(station.ifconfig())
        break
    sleep(0.1)
    print('.', end='')
    


led = Pin(2, Pin.OUT)

def web_page():
  if led.value() == 1:
    gpio_state="ON"
  else:
    gpio_state="OFF"
  
  html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" http-equiv="refresh" content="3" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none; 
  border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
  .button2{background-color: #4286f4;}</style></head><body> <h1>ESP Web Server</h1> 
  <p>GPIO state: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
  <p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
try:
    while True:
        
        
        print('https://www.crawler-test.com/description_tags/description_with_whitespace')
        try:
            response = requests.get('https://www.crawler-test.com/description_tags/description_with_whitespace')
            responseTxt = response.text
            response.close()
            print('Answer  from Crawler-Test received ')         
            #print(responseTxt)        
        except Exception as e:
            if hasattr(e, 'message'):
                print('\033[31mNo answer  from Crawler-Test (exception %s)\033[0m' % e.message)
            else:
                print('\033[31mNo answer  from Crawler-Test (exception No %s)\033[0m' % e)
            responseTxt = ''
           
        except OSError as e:
            if hasattr(e, 'message'):
                print('\033[31mNo answer  from Crawler-Test (OSError %s)\033[0m' % e.message)
            else:
                print('\033[31mNo answer  from Crawler-Test (OSError No %s)\033[0m' % e)       
            responseTxt = ''

                  
        s.settimeout(0.10)
        try:
            conn, addr = s.accept()
            conn.settimeout(0.50)
            print('Got a connection from %s' % str(addr))
            request = conn.recv(1024)
            request = str(request)
            #print('Content = %s' % request)
            led_on = request.find('/?led=on')
            led_off = request.find('/?led=off')
            if led_on == 6:
              print('LED ON')
              led.value(1)
            if led_off == 6:
              print('LED OFF')
              led.value(0)
            response = web_page()
            conn.send('HTTP/1.1 200 OK\n')
            conn.send('Content-Type: text/html\n')
            conn.send('Connection: close\n\n')
            conn.sendall(response)
            conn.close()
      
        except OSError as e:
            pass

except KeyboardInterrupt:
    print('Ende')
    pass
Herzlichen Dank für eure Hilfe.

Lieben Gruß
Äd
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die allwissende Müllhalde hat das hier zu sagen: https://stackoverflow.com/questions/653 ... n-on-esp32
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Äd Franzis: Es gibt keine Expception -202, Ausnahmen haben Namen und -202 ist kein gültiger Name in Python. Neben dem Namen solltest Du auch den kompletten Traceback verraten, damit man sieht *wo* die Ausnahme auftritt.

Der Quelltext ist sehr unregelmässig eingerückt. Konvention ist vier Leerzeichen pro Ebene.

Verwende keine nackten ``except:`` ohne konkrete Ausnahme(n). Sonst werden dort *alle* Ausnahmen behandelt, auch welche mit denen man gar nicht rechnet, und bei solchen ist dann die Behandlung die im ``except``-Block steht, selten die Angemessene. Bei dem Importen am Anfang erwartet man beispielsweise einen `ModuleNotFoundError` und möchte eigentlich wissen wenn es eine andere Ausnahme ist.

Den ganzen Code auf Modulebene zu schreiben und darin eine einzige Funktion zu verstecken ist unübersichtlich. Zumal das keine saubere Funktion ist, weil die undurchsichtigerweise auf die globale Variable `led` zugreift, statt sie als Argument übergeben zu bekommen.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Wenn man aus syntaktischen Gründen einen Namen schreiben muss, der aber nirgends verwendet wird, hat sich der Name `_` durchgesetzt. Dann wundert sich der Leser nicht ob das ein Fehler oder unvollständiger Code ist, wenn der Name nicht verwendet wird.

Den Fall, dass keine Verbindung zum WLAN hergestellt werden kann, sollte man behandeln, und nicht einfach weitermachen als wäre nichts gewesen. Denn dann funktioniert der Rest vom Code ja nicht wie erwartet.

Einbuchstabige Namen sind selten gute Namen. Das geht bei (Lauf)indexwerten wie `i`, `j`, und `k` oder Koordinaten wie `x`, `y`, und `z`, weil das eine starke Konvention ist, die man aus der Mathematik kennt, aber komplexere Objekte sollten nicht mit einzelbuchstaben benannt werden.

Auch kryptische Abkürzungen sollte man vermeiden. Also nicht `conn` schreiben wenn man `connection` meint.

Den ``try``-Block zum Abbruch des Programms per Strg+C würde ich um das gesamte Hauptprogramm legen, denn vielleicht möchte man ja auch während der 10 Sekunden beim warten auf die WLAN-Verbindung das Programm abbrechen, ohne einen unnötigen Traceback zu sehen.

Ein ``pass`` macht nur Sinn wenn es die einzige Anweisung in einem Block ist.

Man sollte die Antwort vom `requests.get()` darauf prüfen ob da die erwarteten Daten im Rumpf enthalten sind, oder ob das eine HTTP-Fehlermeldung ist.

`response.close()` braucht man nicht aufrufen, das macht das Objekt schon selbst wenn man die Daten abgefragt hat. Das braucht man nur in seltenen Fällen wenn man die Antwort streamt, und auch dann nur, wenn man die Verbindung zum Server schliessen will bevor man die Daten komplett empfangen hat.

Wenn man `Exception` und `OSError` sowieso im Grunde gleich behandelt, kann man die auch in *einem* ``except``-Block behandeln. Und da ein `OSError` auch eine `Exception` ist, braucht man `OSError` gar nicht extra erwähnen, denn das ist in `Exception` enthalten. Weshalb der momentane Code an der Stelle auch fehlerhaft ist, denn wenn man erst `Exception` behandelt, dann wird der ``except OSError:``-Block danach nie ausgeführt.

Die Behandlung des Ausnahmeobjekts ist auch komisch umständlich. Gib das einfach aus und gut ist.

``%`` würde ich für Zeichenkettenformatierung in neuem Code nicht mehr verwenden, sofern es dafür keinen guten Grund gibt. Es gibt die `format()`-Methode und f-Zeichenkettenliterale. Es macht auch keinen Sinn einen Wert für einen %s-Platzhalter vorher mit `str()` in eine Zeichenkette umzuwandeln.

Für das schliessen der Verbindung zum Client sollte man die ``with``-Anweisung verwenden. Dann wird die Verbindung beispielsweise auch sauber geschlossen wenn eine Ausnahme in dem Block auftritt.

Beim `recv()` machst Du einen typischen Fehler der überall im Netz auch falsch gezeigt wird: ``recv(1024)`` liefert irgendetwas zwischen 1 und 1024 Bytes aus dem TCP-Datenstrom. Es gibt da auch kein Konzept von ”Nachrichten“, also das liefert nicht garantiert 10 Bytes wenn auf der anderen Seite 10 Bytes mit einem `sendall()` abgesendet wurden. Code muss im Extremfall damit klar kommen, dass jeder `recv()`-Aufruf nur ein einziges Byte aus dem Datenstrom liefert. Dein Code geht aber davon aus, dass die erste Zeile der HTTP-Anfrage komplett darin enthalten ist, oder zumindest die vollständige URL. Damit das stimmt, muss man aber Code schreiben der solange `recv()` aufruft, bis mindestens diese erste Zeile komplett enthalten ist.

Einfacher wird es wenn man sich ein Dateiobjekt von dem `socket` geben lässt und dessen `readline()`-Methode verwendet.

`find()` und der Index 6 sind recht ”magisch”. Verständlicher wäre der Code wenn man die Startzeile in ihere Bestandteile zerlegt und dann die URL einfach per ``==`` vergleicht.

Die `send()`-Aufrufe gehen so nicht, Sockets versenden Bytes und keine Zeichenketten. Die muss man also vorher in Bytes umwandeln.

`web_page()` ist ein guter Name für einen Wert der eine Webseite repräsentiert, aber nicht für eine Funktion. Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt, die sie durchführen. Dann weiss der Leser was die Funktion macht und kann den Wert leichter von eher passiven Werten unterscheiden.

Allerdings könnte man sich die Funktion sparen, denn die Abfrage von der LED gehört da eher nicht rein, und dann bleibt einfach nur eine HTML-Vorlage in die ein Wert eingefügt wird.

Einen `OSError` einfach so stillschweigend verschlucken und weitermachen als wäre nichts passiert ist so gar keine gute Idee.

Zwischenstand (ungetestet):

Code: Alles auswählen

from time import sleep

import esp
import network
from machine import Pin

try:
    import usocket as socket
except ModuleNotFoundError:
    import socket

try:
    import urequests as requests
except ModuleNotFoundError:
    import requests

LED_PIN = 2

SSID = "XXXXXX"  ####
WLAN_PASSWORD = "YYYYYYY"  ####

URL = (
    "https://www.crawler-test.com/description_tags/description_with_whitespace"
)

STATE_NAME_TO_VALUE = {"off": False, "on": True}
VALUE_TO_STATE_NAME = {
    value: key for key, value in STATE_NAME_TO_VALUE.items()
}
assert len(STATE_NAME_TO_VALUE) == len(VALUE_TO_STATE_NAME)

#
# FIXME That <meta> tag mixing viewport and refresh, ugh...
#
HTML_TEMPLATE = """\
<html>
  <head>
    <title>ESP Web Server</title>
    <meta name="viewport" http-equiv="refresh" content="3" content="width=device-width, initial-scale=1">
    <link rel="icon" href="data:,">
    <style>
      html {
        font-family: Helvetica;
        display:inline-block;
        margin: 0px auto;
        text-align: center;
      }
      h1 { color: #0F3376; padding: 2vh; }
      p { font-size: 1.5rem; }
      .button {
        display: inline-block;
        background-color: #e7bd3b
        border: none;
        border-radius: 4px;
        color: white;
        padding: 16px 40px;
        text-decoration: none;
        font-size: 30px;
        margin: 2px;
        cursor: pointer;
      }
      .button2 { background-color: #4286f4; }
    </style>
  </head>
  <body>
    <h1>ESP Web Server</h1>
    <p>GPIO state: <strong>%s</strong></p>
    <p><a href="/?led=on"><button class="button">ON</button></a></p>
    <p><a href="/?led=off"><button class="button button2">OFF</button></a></p>
  </body>
</html>
"""


def main():
    try:
        esp.osdebug(None)

        led = Pin(2, Pin.OUT)

        station = network.WLAN(network.STA_IF)
        station.active(True)
        station.connect(SSID, WLAN_PASSWORD)
        for _ in range(100):
            if station.isconnected():
                print("Connection successful")
                print(station.ifconfig())
                break
            sleep(0.1)
            print(".", end="")
        else:
            raise RuntimeError("can't connect to WLAN")

        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind(("", 80))
        server_socket.listen(5)
        while True:
            print(URL)
            try:
                response = requests.get(URL)
                response.raise_for_status()
                response_text = response.text
                print("Answer from Crawler-Test received:")
                print(response_text)
            except Exception as error:
                print(f"\033[31mNo answer from Crawler-Test ({error})\033[0m")
                response_text = ""

            server_socket.settimeout(0.1)
            try:
                connection, address = server_socket.accept()
                with connection:
                    connection.settimeout(0.5)
                    print(f"Got a connection from {address}.")
                    request_start_line = connection.makefile("rb").readline()
                    try:
                        _method, url, _version = request_start_line.decode(
                            "ascii"
                        ).split()
                    except (UnicodeDecodeError, ValueError):
                        #
                        # TODO Maybe send an error response to the client here.
                        #
                        print(
                            f"error in request start line: {request_start_line}"
                        )
                    else:
                        if url.startswith("/?led="):
                            _, _, state_name = url.partition("=")
                            print(f"LED {state_name.upper()}")
                            try:
                                state = STATE_NAME_TO_VALUE[state_name]
                            except KeyError:
                                #
                                # TODO Maybe send error response to the client
                                #   here.
                                #
                                pass
                            else:
                                led.value(state)

                        connection.send(
                            b"HTTP/1.1 200 OK\r\n"
                            b"Content-Type: text/html\r\n"
                            b"Connection: close\r\n\r\n"
                        )
                        connection.sendall(
                            (
                                HTML_TEMPLATE
                                % VALUE_TO_STATE_NAME[led.value()].upper()
                            ).encode("cp1252")
                        )

            except OSError as error:
                #
                # FIXME This is no adequate handling of this error!
                #
                print(error)

    except KeyboardInterrupt:
        print("Ende")


if __name__ == "__main__":
    main()
Die `main()`-Funktion ist hier deutlich zu lang, das sollte man sinnvoll auf weitere Funktionen aufteilen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

Herzlichen Dank, H.L.
für die, die sehr ausführliche Antwort.

Ich werde sie noch Schritt für Schritt durchgehen, aber ich sehe schon jetzt, dass ich sehr viel von dir lernen kann.
Einige Punkte sind durch die Reduzierung des Codes in das Test-Programm entstanden, da ich wieder auf die Quelle des Codes zurückgegriffen habe, und da waren z.B. die falschen Einrückungen wieder drin (mich nervt das auch :) ).

Und besonderen Dank für den Code. Der sieht wirklich viel aufgeräumter aus.
Natürlich habe ich ihn auch gleich auf den ESP32 geladen. Leider gab es bei jedem f-String Syntax-Fehlermeldungen. Ich habe das f mal provisirisch entfernt und befasse mich später damit.
In dem Moment, in dem ich die Web-Seite geladen habe, wird das Programm mit folgender Meldung abgebrochen:

Code: Alles auswählen

Traceback (most recent call last):
  File "<stdin>", line 165, in <module>
  File "<stdin>", line 112, in main
AttributeError: 'socket' object has no attribute '__exit__'
Bis ich den Grund verstanden habe, werde ich das "with" provisorisch entfernen.
Nun steigt das Programm wieder aus, wenn die Web-Seite geladen/aktualisiert wird:

Code: Alles auswählen

Traceback (most recent call last):
  File "<stdin>", line 165, in <module>
  File "<stdin>", line 120, in main
NameError: name 'UnicodeDecodeError' isn't defined
Also auch mal provisorisch auskommentiert.
Mir scheint, "mein" Mikropython versteht wohl in Strings "{error}" etc. nicht: Es wird nicht der Inhalt der Variable, sondern der String "{error}" selber ausgegeben, z.B.:

Code: Alles auswählen

No answer from Crawler-Test ({error})
Jetzt läuft das Programm mal ohne Absturz durch. Allerdings kommt der Web-Crawler gar nicht mehr auf die Testseite und die locale Web-Page wird auch nicht mehr geladen.

Ich schaue mir das alles im Detail später noch mal an. Aber vielleicht hat ja noch wer eine Idee, wie wir beide Aufgaben des Programms zum Laufen kriegen.

Ich verwende Thonny 3.3.3 unter Windows10(64-bit) (mit Python 3.7.9 und Tk8.6.9.) und auf den ESP habe ich esp32-idf3-20210202-v1.14.bin geladen.

Lieben Gruß
Äd
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hast du meinen Post gelesen?
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Äd Franzis: Ich programmiere ”richtiges” Python, kann also nicht so wirklich einschätzen wo MicroPython sich anders verhält. Dass das keine f-Zeichenkettenliterale kann ist schade. Insbesondere weil das eigentlich Performanter umgesetzt werden kann wenn sich der Compiler um so etwas schon kümmert als rein dynamische `format()`-Aufrufe.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

__deets__ hat geschrieben: Samstag 17. April 2021, 17:29 Hast du meinen Post gelesen?
Guten Abend, lieber __deets__,

ja, das habe ich, bin aber noch daran das zu verarbeiten; nur verstehe ich leider bisher noch nicht viel davon. Auch sehe ich nicht, dass der Fragesteller dort sein Problem lösen konnte, oder lese ich das falsch?

Kannst du mir das vieleicht irgenwie verständlicher machen?

In einem Punkt bin ich Dank deines Links schon mal weiter : Ich habe den Code der lokale WEB-Seite komplett entfernt, und tatsächlich ist dann bei dem Crawler auch die Exception -202 aufgetreten - sogar noch schneller. Damit hatte ich nicht gerechnet (es sah zuvor für mich so aus, dass der Zugriff des Crawlers erst crashed, wenn die Web-Seite im lokalen Netzt aktualisiert wird).
Suche nun die Antwort dafür in deinem Link.


Lieben Gruß
Äd
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Hinweis legt einen DNS Fehler nahe, und schlägt vor, mal direkt die IP zu benutzen.
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

__blackjack__ hat geschrieben: Samstag 17. April 2021, 20:19 @Äd Franzis: Ich programmiere ”richtiges” Python, kann also nicht so wirklich einschätzen wo MicroPython sich anders verhält. Dass das keine f-Zeichenkettenliterale kann ist schade. Insbesondere weil das eigentlich Performanter umgesetzt werden kann wenn sich der Compiler um so etwas schon kümmert als rein dynamische `format()`-Aufrufe.
Ja, das ist viel einfacher. Ich habe einen Web-Crawler mit wesentlich komplizierteren Aufgaben auf einem Rapberry PI 3 am Laufen. Da kenne ich diese Probleme nicht. Aber die Leistung auf einem Controller ist halt deutlich begrenzter. Da ich aber wohl kaum der einzige bin, der die "urequests" Lib nutzt, hoffe ich doch, den Crawler auch auf dem EPS32 stabil am Laufen zu kriegen.

Lieben Gruß
Äd
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

__deets__ hat geschrieben: Samstag 17. April 2021, 21:19 Der Hinweis legt einen DNS Fehler nahe, und schlägt vor, mal direkt die IP zu benutzen.
Ah... :)
Ich habe mal "https://34.192.11.205/description_tags/ ... whitespace" eingetragen. Am Anfang hat es noch "gestottert", aber im Moment läuft der Crawler gut durch. Ich lasse ihm mal über Nacht laufen. Danke für den Tipp.

Lieben Gruß
Äd
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

__deets__ hat geschrieben: Samstag 17. April 2021, 21:19 Der Hinweis legt einen DNS Fehler nahe, und schlägt vor, mal direkt die IP zu benutzen.
Hallo

leider gibt es ein neues Problem: Die URL für für meien Crawler lautet: "https://dlr-web-daten1.aspdienste.de/cg ... 0,11&ts=12" (fürs Testprogramm oben hatte ich's vereinfacht). Da komme ich aber irgendwie nicht an die IP. :?
Jemand eine Idee?

Lieben Gruß
Äd
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also ein Ping zumindest fuer mich funktioniert:

Code: Alles auswählen

ping dlr-web-daten1.aspdienste.de
PING dlr-web-daten1.aspdienste.de (217.5.174.46) 56(84) bytes of data.
Das Problem ist in meinen Augen hier auch eher das unterliegende ESP IDF, also die Entwicklungsumgebung der ESPs, auf die micropython auch nur aufsetzt. Da habe ich zu dem Fehler noch Eintraege aus dem letzten Herbst gefunden. Und micropython haengt gerne mal ein bisschen hinterher, was die Entwicklung angeht. Trotzdem solltest du mal im micropython-Forum nachfragen, ob da schon mal jemand das Problem beobachtet hat.
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

__deets__ hat geschrieben: Sonntag 18. April 2021, 14:00 Also ein Ping zumindest fuer mich funktioniert:

Code: Alles auswählen

ping dlr-web-daten1.aspdienste.de
PING dlr-web-daten1.aspdienste.de (217.5.174.46) 56(84) bytes of data.
Vielen Dank, __deets__.
Wenn ich aber die IP einfüge (https://217.5.174.46/cgi-bin/wetterakt. ... 0,11&ts=12), bekomme ich einen HTTP-Fehler 404 ("Not Found").
Und auch die Ping-Antwort ist nicht gut:

Code: Alles auswählen

Zeitüberschreitung der Anforderung.
Zeitüberschreitung der Anforderung.
Zeitüberschreitung der Anforderung.
Zeitüberschreitung der Anforderung.

Ping-Statistik für 217.5.174.46:
    Pakete: Gesendet = 4, Empfangen = 0, Verloren = 4
    (100% Verlust),
__deets__ hat geschrieben: Sonntag 18. April 2021, 14:00 Das Problem ist in meinen Augen hier auch eher das unterliegende ESP IDF, also die Entwicklungsumgebung der ESPs, auf die micropython auch nur aufsetzt. Da habe ich zu dem Fehler noch Eintraege aus dem letzten Herbst gefunden. Und micropython haengt gerne mal ein bisschen hinterher, was die Entwicklung angeht. Trotzdem solltest du mal im micropython-Forum nachfragen, ob da schon mal jemand das Problem beobachtet hat.

Das würde auch erklären, warum ich problemlos mit dem Raspberry PI 3 auf die URL zugreifen kann. Hier gibt es dieses Problem nicht und die SW läuft schon seit Wochen stabil.

Lieben Gruß
Äd
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Äd Franzis: Wenn man direkt die IP in die URL packt, dann wird die auch als Host:-Header übertragen und den wird der Server wahrscheinlich verwenden um den virtuellen Host aufzulösen und zu entscheiden von wo die Daten dann letztlich kommen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe mal einen reverse lookup gemacht, und da kommt tatsächlich ein anderer hostname mit. Wenn du den Header explizit angibst (wie von BlackJack angemerkt), dann klappt’s vielleicht.
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

__blackjack__ hat geschrieben: Sonntag 18. April 2021, 18:38 @Äd Franzis: Wenn man direkt die IP in die URL packt, dann wird die auch als Host:-Header übertragen und den wird der Server wahrscheinlich verwenden um den virtuellen Host aufzulösen und zu entscheiden von wo die Daten dann letztlich kommen.
__deets__ hat geschrieben: Montag 19. April 2021, 10:18 Ich habe mal einen reverse lookup gemacht, und da kommt tatsächlich ein anderer hostname mit. Wenn du den Header explizit angibst (wie von BlackJack angemerkt), dann klappt’s vielleicht.
Vielen Dank, @__deets__ und @__blackjack__

das will ich ja auch gerne machen, aber leider weiß ich nicht wie.
Meintet ihr so etwas:
response = requests.get('https://217.5.174.46/cgi-bin/wetterakt. ... 0,11&ts=12', headers={'Host': 'https://dlr-web-daten1.aspdienste.de'}) :?:
Damit bekomme ich leider einen HTML-Fehler '400 Bad Request' :(

Immerhin mault Python dabei jetzt nicht mehr mit einer Exception.

Lieben Gruß
Äd
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Der Host ist ›dlr-web-daten1.aspdienste.de‹.
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

Sirius3 hat geschrieben: Montag 19. April 2021, 18:18 Der Host ist ›dlr-web-daten1.aspdienste.de‹.
Bingo! Dat läuft. Vielen Dank. :)
Jetzt warten wir mal den Langzeittest ab, wie das über Nacht durchläuft. Allerdings habe ich auch eine 2. Änderung in der SW: die urequest-Lib habe ich durch die prequest-lib von https://forum.micropython.org/viewtopic ... 5&start=10 ersetzt (zugegeben, als Schuss ins Blaue).

Sind wir mal gespannt auf morgen. :)

Herzlichen Dank.

Lieben Gruß
Äd
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

Äd Franzis hat geschrieben: Montag 19. April 2021, 21:55 [...] Bingo! Dat läuft. Vielen Dank. :)
Jetzt warten wir mal den Langzeittest ab, wie das über Nacht durchläuft. [...]
20h ist die SW jetzt mal ohne Exception -202 durchgelaufen. -Hurra. Sieht schon mal sehr, sehr gut aus; ich lasse den Test weiter laufen.

Wenn gleichzeitig die WEB-Seite aktualisiert wird (oder refreshed), dann kommt die Exception 16. Aber damit kann man sich arrangieren: Mit dem nächsten "ungestörten" request.get() läuft es ja wieder.

Die WEB-Seite läuft derzeit nicht so zuverlässig: Wenn man die Taste betätigt, geht manchmal der Befehl verloren (LED schaltet nicht). Aber ob das mit den Änderungen hier was zu tun hat oder von was anderem herkommt, will ich erst noch mal untersuchen.

Vielen Dank soweit schon mal an alle, die hier geantworte haben. Ihr wart/seit ein große Hilfe.


Lieben Gruß
Äd
Äd Franzis
User
Beiträge: 16
Registriert: Donnerstag 11. Februar 2021, 14:55

Hallo liebes Forum,

ihr hattet mir bei dem Thema "Web-Page mit Refresh + Simple Web-Crawler gleichzeitig" sehr geholfen und der Code lief nun über zwei Monate gut. Aber irgendwas muss auf Anbieterseite verändert worden sein, womit Python nicht (mehr) zurechtkommt - und zwar weder MicroPython noch "normales" Python.

Im Webbrowser funzt die Seite https://dlr-web-daten1.aspdienste.de/cgi-bin/wetterakt.pl[...] sehr gut - unverändert.

Auf dem ESP32 in MicroPython gibt dieser Code nun aber einen HTML-Fehler '403 Forbidden':

Code: Alles auswählen

try:
    import purequests as requests  #modified version from https://forum.micropython.org/viewtopic.php?f=2&t=5295&start=10
    #import urequests as requests  
except:
    import requests

def precipCrawler(sid=1):

    
    print('calling https://dlr-web-daten1.aspdienste.de/cgi-bin/wetterakt.pl?c=9&sid='+str(sid)+'&tid=10%2C11&ts=12')

    try:
        #finding IP from a domain (e.g. dlr-web-daten1.aspdienste.de)
        response = requests.get('https://217.5.174.46/cgi-bin/wetterakt.pl?c=9&sid='+str(sid)+'&tid=10,11&ts=12', headers={'Host': 'dlr-web-daten1.aspdienste.de'})
        
        responseTxt = response.text
        response.close()
        #print(responseTxt)
        if '404 Not Found' in responseTxt:
            print('\033[31mAgrarmeteorologie RLP was not found (HTTP-Fehler 404)\033[0m')
            responseTxt = ''
            return ['', -1.0, '', -1.0, []]
        elif '400 Bad Request'  in responseTxt:
            print('\033[31mBad request to Agrarmeteorologie RLP (HTTP-Fehler 400)\033[0m')
            responseTxt = ''
            return ['', -1.0, '', -1.0, []]
        elif '403 Forbidden'  in responseTxt:
            print('\033[31mForbidden request to Agrarmeteorologie RLP (HTTP-Fehler 403)\033[0m')
            responseTxt = ''
            return ['', -1.0, '', -1.0, []]            
            
    except Exception as e:
        if hasattr(e, 'message'):
            print('\033[31mNo answer from Agrarmeteorologie RLP (exception %s)\033[0m' % e.message)
            ticks_Duration = ticks_us() - ticks_start
            if ticks_Duration > ticks_DurationMax: ticks_DurationMax =ticks_Duration
            print('Duration:', ticks_Duration/1000000, ' and Duration max:', ticks_DurationMax/1000000)            
        responseTxt = ''
        return ['', -1.0, '', -1.0, []]
    
    #[...] SOWEIT KOMMT DAS PROGRAMM GAR NICHT MEHR
    # Def wird bei " elif '403 Forbidden'  in responseTxt:" verlassen.
    
print(precipCrawler())
Wie gesagt: Dieser Code lief vor einer Woche noch prima.


Und hier der Code auf dem Raspberry Pi ("normales" Python):

Code: Alles auswählen

from bs4 import BeautifulSoup as bs, Tag
from requests import request


def Wetterstation5MinWerte(SID):


    DataSetMtx  =[]
    DataSetHMtx =[]
    url ="https://dlr-web-daten1.aspdienste.de/cgi-bin/wetter.min5.pl?c=9&sid=%s&t=1,2,6,7,8,9,10&hh=1" % (SID)
    try:
        r = request('GET', url)
      
    except Exception as e:        
        print(dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S:'),
              "url request for station %s (5min data) went wrong (Exception: %s)" % (SID, e))
        return []
    
    else:
        soup = bs(r.text,'html.parser')
        
    #[...]    
Die Def wird als Exception verlassen:

Code: Alles auswählen

2021-07-08 23:08:23: url request for station 1 (5min data) went wrong (Exception: HTTPSConnectionPool(host='dlr-web-daten1.aspdienste.de', port=443): Max retries exceeded with url: /cgi-bin/wetter.min5.pl?c=9&sid=1&t=1,2,6,7,8,9,10&hh=1 (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7304ae30>: Failed to establish a new connection: [Errno 101] Network is unreachable',)))
2021-07-08 23:08:23: url request for station 1 (5min data) went wrong (Exception: HTTPSConnectionPool(host='dlr-web-daten1.aspdienste.de', port=443): Max retries exceeded with url: /cgi-bin/wetterakt.pl?c=9&sid=1&tid=1,4,99,2,3,4,5,6,7,99,10,12,11,99,15,16,99,17,18,20&ts=12 (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x73062210>: Failed to establish a new connection: [Errno 101] Network is unreachable',)))
Die SW lief aber zuvor sehr gut und der Code wurde nicht geändert.

Und auch hier lässt sich die angeforderte Seite prima über einen normalen Web-Brower aufrufen:
https://dlr-web-daten1.aspdienste.de/cgi-bin/wetter.min5.pl[...]


Habt Ihr eine Idee, was da passiert sein kann? Könntet Ihr den Code vielleicht auf eurem System mal ausprobieren, ob ihr die gleichen Probleme habt?
Oder besser noch: Was kann ich machen, dass die SW auf dem Raspberry Pi und die auf dem ESP32 wieder läuft?


Herzlichen Dank im Voraus.

Lieben Gruß
Äd
Antworten