Norbert Strzata: Telefonbuch Rückwärtssuche

Hallo, ich habe ein Problem beim Auslesen des HTML von "dasoertliche.de". Ich rufe in .Net die Seite mit der betr. Rufnummer auf und kann im Quellcode den Namen und die Adresse auslesen. Der Code sieht so aus:

doc.Load(client.OpenRead("https://www.dasoertliche.de/?form_name=search_inv&buc=674&page=5&context=4&action=43&ph=" & 03642553535), Encoding.UTF8)

Dim entry = doc.GetElementbyId($"entry_{i}

If entry Is Nothing Then Exit Sub

Dim divLeftOnHit = entry.ChildNodes.FirstOrDefault(Function(a) a.HasClass("oe_hit"))

Dim _Name As String = HttpUtility _
    .HtmlDecode( _
        divLeftOnHit.ChildNodes _ 
                    .FirstOrDefault(Function(a) a.Name = "a") _
                    .InnerText _
                    .Replace(CrLf, "") _
    )

Dim _Adresse As String = divLeftOnHit _
    .ChildNodes _
    .FirstOrDefault(Function(a) a.Name = "address") _
    .InnerText _
    .Replace(CrLf, "") _
    .Replace(Tab, "")

liste.Add(_Name & ";" & _Adresse.Replace("&nbsp", "").Replace(",", " "))

Edit von Rolf B: Zeilenumbrüche in den Code gesetzt für Lesbarkeit. Syntaxkonform mit _ Zeichen

Das funktioniert in 90 % aller Fälle. Sind aber "Blumengrüsse" eingearbeitet, bekomme ich nicht mehr den Namen, sondern stattdessen die "Blumengrüsse".

Sorry - bekomme keine vernünftige Formatierung hin, deshalb als Bild. Von HTML verstehe ich absolut nichts. Nur von .Net Hab in mein Projekt Net.Http und HtmlAgilityPack eingebunden. Kann mir jemand helfen?

Grüße Norbert

  1. Hallo Norbert,

    bevor Du auch nur einen weiteren Schritt in diese Richtung tust, lies dir die Nutzungsbedingungen durch und prüfe, ob dein Tun davon abgedeckt ist. Im Zweifelsfall lass es sein oder frage bei DTM nach. Ansprechpartner findest Du im Impressum. Bringe Programme mit solchen Zugriffen keinesfalls an die Öffentlichkeit, bevor die Nutzung nicht klar erlaubt ist. Sonst hast Du eine Abmahnung im Briefkasten, bevor Du „Günter Freiherr von Gravenreuth“ buchstabiert hast.

    Danach kannst Du weitermachen. Oder auch nicht.

    Bei Deinem Code verstehe ich nicht, weshalb er überhaupt jemals einen Namen findet. Denn das a Element, aus dem Du den Namen ausliest, steckt in deinem Screenshot und auch bei meinen Abfragen grundsätzlich in einem h2 Element und sollte daher in den ChildNodes von divLeftOnHit niemals zu finden sein.

    Der Blumengrüße-Link folgt - so vorhanden, als ChildNode dieses div auf die Adresse und wird deshalb gefunden.

    Ohne Blumengrüße findet deine Abfrage die E-Mail. Sofern vorhanden. Oder die Webadresse. Sofern vorhanden.

    Oder gar nichts. Dann liefert FirstOrDefault NULL und die InnerText-Abfrage wirft mit Juhu eine NullReferenceException in die Luft. Da fehlt Dir Error-Handling. Oder ein try-catch drumherum. Aber vielleicht existiert der ja auch und Du hast ihn nur nicht gezeigt.

    Als Lösung: Du musst das h2 Element finden und darin das a Element. Das ist der Name.

    Ob Du das sinnvollerweise mit FirstOrDefault-Abfragen auf den ChildNodes machst, oder ein Query-Tool verwenden kannst, hängt davon ab, was doc ist. Ein XmlDocument?

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      Sonst hast Du eine Abmahnung im Briefkasten, bevor Du „Günther Freiherr von Gravenreuth“ buchstabiert hast.

      im Prinzip: Ja, full ACK.
      Allerdings dürfte der genannte Herr wohl keine Abmahnungen mehr ausstellen; an dem erfreuen sich mittlerweile die Würmer. 😉

      Live long and pros healthy,
       Martin

      --
      Lasst uns ins Horn brechen und aufstoßen.
      1. Hallo Martin,

        weiß ich doch. Wobei die Würmer aufpassen müssen, nicht an Bleivergiftung zu sterben, so wie ihr Futter.

        Aber sein giftiges Ideengut lebt fort und wird gerne auf die mehr oder weniger unschuldige Menschheit losgelassen.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo,

          weiß ich doch. Wobei die Würmer aufpassen müssen, nicht an Bleivergiftung zu sterben, so wie ihr Futter.

          Bleivergiftung? Bringe ich da gerade was durcheinander? War da wirklich schnelles Blei im Spiel?
          Ich dachte, der hätte sich in seiner Zelle in der U-Haft erhängt ...
          Also eher Sauerstoffmangel als Bleivergiftung[1].

          Aber sein giftiges Ideengut lebt fort und wird gerne auf die mehr oder weniger unschuldige Menschheit losgelassen.

          Zweifellos, ja.

          Live long and pros healthy,
           Martin

          --
          Lasst uns ins Horn brechen und aufstoßen.

          1. Verblüffend ist, dass bei einer Bleivergiftung letztendlich auch Sauerstoffmangel die primäre Todesursache ist: Durch die Anlagerung von Bleiionen an die roten Blutkörperchen können die ihre Aufgabe nicht mehr erfüllen, Sauerstoff zu- und Kohlendioxid abzutransportieren. ↩︎

          1. Hallo Der,

            War da wirklich schnelles Blei im Spiel?

            Wikipedia sagt: Schusswaffe. Man könnte spekulieren, wie man sich mit einer Schusswaffe erhängen kann, aber das wird mir nun zu makaber und pietätlos. Selbst bei „Tanja“ Dörr.

            Rolf

            --
            sumpsi - posui - obstruxi
    2. Hallo Rolf,
      hallo Alle,

      wäre denn die Software als solche schon ein Verstoß, oder erst das gezielte Grabben und Sammeln der Daten aus der fremden Datenhaltung?

      Schlièßlich könnte es ja auch ein Modul eines Screenreaders werden.

      LG + Gesundheit Localhorst

      1. Hallo localhorst,

        ob gegen Nutzungsbedingungen verstoßen wird, kommt drauf an, was die Software tut. Ein generelles Verbot eines automatisierten Zugriffs habe ich dort nicht herausgelesen.

        Was Norbert tun will, weiß ich nicht. Darum soll er ja auch an Hand der Nutzungsbedingungen selbst sein Gewissen erforschen.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,
          hallo Alle,

          ob gegen Nutzungsbedingunen verstoßen wird, kommt drauf an, was die Software tut. Was Norbert tun will, weiß ich nicht. Darum soll er ja auch an Hand der Nutzungsbedingungen selbst sein Gewissen erforschen.

          Ich denke, dass Nutzungsbedingungen hier nicht maßgeblich sein können, wenn sie nicht durch die gesetzlichen Rahmenbedingungen gedeckt sind. Man sollte da imho nicht so anbieterhörig sein.

          Im Zweifel sind die Nutzungsbedingungen immer noch überraschend, da sie einem erst nach dem Erstaufruf der Seite zur Kenntnis gebracht werden können. Anders ist das mit Seiten mit vorgeschalteter Authentifizierung. Da lässt sich die Reihenfolge der Aufrufe legal nachvollziehen und damit eventuell auch glaubhaft machen.

          Und es sei hiermit als Analogon daran erinnert, dass man Zeitungsartikel auch ausschneiden und sammeln darf. Man darf sie nur nicht ohne Genehmigung wiederveröffentlichen. Ähnlich dürfte das also mit (privaten) Rufnummersammlungen auch zu werten sein.

          LG + Gesundheit Localhorst

          1. Hallo,

            Und es sei hiermit als Analogon daran erinnert, dass man Zeitungsartikel auch ausschneiden und sammeln darf. Man darf sie nur nicht ohne Genehmigung wiederveröffentlichen. Ähnlich dürfte das also mit (privaten) Rufnummersammlungen auch zu werten sein.

            der Vergleich hat einen gewissen Charme, aber er hinkt. Zum Ausschneiden und Sammeln von Zeitungsausschnitten musst du zunächst mal ein Exemplar der Zeitung kaufen. Damit kannst du dann innerhalb deiner eigenen Welt machen, was du willst. Niemand merkt es.

            Beim automatisierten, möglicherweise massenhaften Abgreifen von Daten von einem Webserver beanspruchst du aber dessen Leistung (die kostenlos zu Verfügung gestellt wird). Tust du das nur für einen Datensatz, oder meinetwegen eine Handvoll, wird das niemanden stören. Wenn du aber automatisch Hunderttausende von Datensätzen abfischen willst, könnte das dem Betreiber des Servers durchaus auffallen und missfallen.

            Live long and pros healthy,
             Martin

            --
            Lasst uns ins Horn brechen und aufstoßen.
            1. Hallo Martin,
              hallo Alle,

              das massenhafte Abgreifen könnte der Anbieter verhindern. Das wäre technisch leicht möglich und zumutbar. Tut er dies nicht, trägt er eine Mitschuld, mindestens soviel, wie Jemand, der sein KFZ unbeaufsichigt mit laufendem Motor offen am Straßenrand stehen lässt mit einem Schild daran: hier kannst Du kostenlos autofahren.
              Dieser Vergleich hinkt nur insofern, dass der Benutzer das Auto zurückbringen müsste. Die Daten bleiben aber in ihrer Konsistenz ohnehin am Ort, auch wenn man sie nicht zurückbringt.

              Überwindet der Datensammler hingegen unerlaubt eine Hürde, trifft aller Wahrscheinlichkeit nach §202a StGB zu.

              Das bloße Missachten von "Nutzungsbedingungen" stellt aber nur dann einen wirksamen Verstoß dar, wenn diese (vorher!) Vertragsbestandteil geworden sind. Sonst könnte man als Anbieter auch irgendwo (versteckt) verlangen, dass die Webseite nur mit dem linken Auge kostenlos betrachtet werden darf. Die Benutzung des rechten Auges hingegen kostet ;-)

              Ergo:
              Das unsubstantiierte Schüren von Ängsten sollte hier nicht Raum greifen.
              Der sachliche Verweis auf geltende Gesetze kann hingegen hilfreich sein.

              LG + Gesundheit Localhorst

              1. Hallo localhorst,

                "dasoertliche" macht es sich da überraschend einfach:

                Mit Verbindungsaufbau zum Internet-Server kommt ein Vertrag zustande, für den folgende Bedingungen gelten:

                Möglicherweise hat das vor Gericht bei einem Gelegenheitsnutzer keinen Bestand, weil genau dieser Satz nicht oben auf der Haupt-Webseite steht. Keine Ahnung. Ich bin kein Anwalt und auch kein Fastix.

                Aber wenn jemand eine Seite scriptet und scrapet, um Daten abzugreifen, ist das anders. Wer das tut, weiß, dass das über die Grenzen einer Normalnutzung hinausgeht und kann sich da nicht auf Unkenntnis der Nutzungsbedingungen berufen.

                Das Abrufen hunderter Telefonnummern von "dasoertliche" habe ich nicht getestet. Möglicherweise bekomme ich dann sogar einen IP-Bann, wenn ich das versuche.

                das massenhafte Abgreifen könnte der Anbieter verhindern. Das wäre technisch leicht möglich und zumutbar. Tut er dies nicht, trägt er eine Mitschuld

                Wenn ich etwas verbiete, aber nicht aktiv verhindere, trage ich eine Mitschuld, wenn es jemand anderes tut? Diese Interpretation von "Richtig und Falsch" finde ich befremdlich.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hallo Rolf,
                  hallo Alle,

                  Wenn ich etwas verbiete, aber nicht aktiv verhindere, trage ich eine Mitschuld, wenn es jemand anderes tut? Diese Interpretation von "Richtig und Falsch" finde ich befremdlich.

                  Na gut. Dann eben nochmal:
                  Ich verbiete Dir hiermit, meine Postings (hier) mit beiden Augen zu lesen, im Quelltext anzuzeigen, oder auszudrucken.

                  :-)

                  LG + Gesundheit Localhorst

                  1. Hello Horst,

                    Wenn ich etwas verbiete, aber nicht aktiv verhindere, trage ich eine Mitschuld, wenn es jemand anderes tut? Diese Interpretation von "Richtig und Falsch" finde ich befremdlich.

                    Na gut. Dann eben nochmal:
                    Ich verbiete Dir hiermit, meine Postings (hier) mit beiden Augen zu lesen, im Quelltext anzuzeigen, oder auszudrucken.

                    :-)

                    Netter Versuch.
                    Ich würde jetzt antworten "LMAA, ich mache, was ich will" ;-)
                    Schließlich sind das Forum und das Archiv zum Lesen da. Und wenn ich mir alle deine Posts ausdrucken will für meine persönliche Sammlung, dann mache ich das. Es würde mMn auch keinen Unterschied machen, wenn es dein Forum wäre.

                    Glück Auf
                    Tom vom Berg

                    --
                    Es gibt nichts Gutes, außer man tut es!
                    Das Leben selbst ist der Sinn.
                    1. Hallo TS,
                      hallo Alle,

                      Wenn ich etwas verbiete, aber nicht aktiv verhindere, trage ich eine Mitschuld, wenn es jemand anderes tut? Diese Interpretation von "Richtig und Falsch" finde ich befremdlich.

                      Na gut. Dann eben nochmal:
                      Ich verbiete Dir hiermit, meine Postings (hier) mit beiden Augen zu lesen, im Quelltext anzuzeigen, oder auszudrucken.

                      :-)

                      Netter Versuch.
                      Ich würde jetzt antworten "LMAA, ich mache, was ich will" ;-)

                      So war es auch vorgesehen!

                      ♡♡♡ ♡♡♡ ♡♡♡ ♡♡♡
                      Hinweis:
                      Meine Antworten berücksichten (meistens) den Inhalt des gesamten bisherigen Threads. Der/die jeweilige Vorposter/in sollte sich daher nicht angegriffen fühlen
                      ♡♡♡ ♡♡♡ ♡♡♡ ♡♡♡

                      LG + Gesundheit Localhorst

    3. Hello Rolf,

      bevor Du auch nur einen weiteren Schritt in diese Richtung tust, lies dir die Nutzungsbedingungen durch und prüfe, ob dein Tun davon abgedeckt ist.

      Ich sehe da keine Möglichkeit, die Abfrage von Daten alleine durch Nutzungsbedingungen einzuschränken. Auch die Behauptung, durch Nutzung würde automatisch ein Vertrag entstehen, halte ich für nicht haltbar. Solange HTTP/s bestimmungsgemäß benutzt wird, wird mMn keine verbietbare Handlung vorgenommen.

      Allderdings dürfte jede Form der öffentlichen (Wieder-)Verbreitung der gesamten Datensammlung (Datenbank) oder wesentlicher Teile davon zu erheblichen Schadenersatzansprüchen führen.

      Siehe hierzu auch den Artikel der RAe Brennecke

      Was nun "erheblich" und was "öffentlich" ist, vermag ich nicht zu beantworten.

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. Hallo TS,
        hallo Alle,

        Siehe hierzu auch den Artikel der RAe Brennecke

        Ich hab da auch noch was. Was nun "erheblich" ist, das schreiben die da

        ♡♡♡ ♡♡♡ ♡♡♡ ♡♡♡
        LG + Gesundheit
        Localhorst

      2. Hallo,

        Ich sehe da keine Möglichkeit, die Abfrage von Daten alleine durch Nutzungsbedingungen einzuschränken.

        ich bin auch kein Experte, aber ich sehe da sowohl eine rechtliche als auch eine technische Handhabe.
        Rechtlich, weil der Eigentümer einer Sache sehr wohl festlegen kann, wie diese Sache genutzt werden darf, auch wenn er sie wissentlich der Allgemeinheit zur Verfügung stellt oder sogar aus der Hand gibt. Zum Beispiel könnte die Stadt verfügen, dass im öffentlichen Park Inline-Skating verboten ist. Oder eine Autovermietung könnte im Mietvertrag vorschreiben, dass das Fahrzeug nicht gewerblich genutzt werden darf. Kontrollieren kann der Vermieter das dann aber nicht, eine solche Bestimmung ist also schwierig durchzusetzen.
        Technisch, weil ich wiederholte Abfrage von derselben IP-Adresse aus innerhalb kurzer Zeit blockieren (graylisten) könnte.

        Auch die Behauptung, durch Nutzung würde automatisch ein Vertrag entstehen, halte ich für nicht haltbar. Solange HTTP/s bestimmungsgemäß benutzt wird, wird mMn keine verbietbare Handlung vorgenommen.

        Da gehe ich mit. Vor allem die Behauptung, bereits durch Herstellen einer Verbindung zum Server komme der Vertrag zustande. Das hieße, ich muss erst einen Vertrag eingehen, um überhaupt die Vertragsbedingungen zu erfahren. Das geht nun wirklich nicht.

        Allderdings dürfte jede Form der öffentlichen (Wieder-)Verbreitung der gesamten Datensammlung (Datenbank) oder wesentlicher Teile davon zu erheblichen Schadenersatzansprüchen führen.

        Hmm. Schadenersatz? Wem entsteht dadurch Schaden? In welcher Form? Eigentlich entsteht niemandem ein Schaden. Vor allem keiner, der sich irgendwie in Zahlen ausdrücken lässt. Außer eben, wenn die Massenabfrage solche Ausmaße annimmt, dass dabei der Betrieb des Servers (Performance) merklich gestört wird. Aber selbst das dürfte dann schwierig zu beziffern sein und führt im Bedarfsfall wahrscheinlich wieder zu irgendwelchen Mondsummen.

        Live long and pros healthy,
         Martin

        --
        Lasst uns ins Horn brechen und aufstoßen.
        1. Hallo Martin,
          hallo Alle,

          Du bringst jetzt aber nicht Urheberrecht und Sachenrecht durcheinander?

          Die Rechtsansprüche bei geklauten Datenbanken gehen aus §87ff des Urheberrechts hervor. Das wurde hier von @TS und von mir verlinkt.

          ♡♡♡ ♡♡♡ ♡♡♡ ♡♡♡
          Hinweis:
          Meine Antworten berücksichten (meistens) den Inhalt des gesamten bisherigen Threads. Der/die jeweilige Vorposter/in sollte sich daher nicht alleine angesprochen fühlen.
          ♡♡♡ ♡♡♡ ♡♡♡ ♡♡♡

          LG + Gesundheit
          Localhorst

          1. Hallo,

            Du bringst jetzt aber nicht Urheberrecht und Sachenrecht durcheinander?

            nein, das denke ich nicht. Aber übersiehst du vielleicht die Abgrenzung zwischen Urheberrecht und Nutzungsrecht?

            Das Urheberrecht spielt hier nach meiner Auffassung überhaupt keine Geige. Die Informationen in der Datenbank sind vom Anbieter dazu bestimmt, von jedermann abgerufen zu werden (dass die Daten auf legalem Weg in die Datenbank gekommen sind, setzen wir mal voraus). Es geht also nur um eine anbieterseitige Einschränkung der theoretisch möglichen Nutzung.

            Die Rechtsansprüche bei geklauten Datenbanken gehen aus §87ff des Urheberrechts hervor. Das wurde hier von @TS und von mir verlinkt.

            Aber zieht das Argument auch, wenn die Daten ohnehin (wenn auch mengenmäßig beschränkt) offen zugänglich sind?

            Live long and pros healthy,
             Martin

            --
            Lasst uns ins Horn brechen und aufstoßen.
            1. Hallo Der,
              hallo Alle,

              Du bringst jetzt aber nicht Urheberrecht und Sachenrecht durcheinander?

              nein, das denke ich nicht. Aber übersiehst du vielleicht die Abgrenzung zwischen Urheberrecht und Nutzungsrecht?

              Das Urheberrechtsgestz regelt unter Anderem das Nutzungsrecht geistiger Schöpfungen.

              Das Urheberrecht spielt hier nach meiner Auffassung überhaupt keine Geige. Die Informationen in der Datenbank sind vom Anbieter dazu bestimmt, von jedermann abgerufen zu werden (dass die Daten auf legalem Weg in die Datenbank gekommen sind, setzen wir mal voraus). Es geht also nur um eine anbieterseitige Einschränkung der theoretisch möglichen Nutzung.

              Ja. Die ist z.B. durch technische Maßnahmen oder durch Vertrag möglich. Und ob der (http/s-)Client die Daten der Datenbank nur selber nutzen darf, oder ob er diese auch weiterverbreiten darf, regeln die §§87(b)ff des Urheberrechtsgesetzes und/oder ein Vertrag.

              Die Rechtsansprüche bei geklauten Datenbanken gehen aus §87ff des Urheberrechts hervor. Das wurde hier von @TS und von mir verlinkt.

              Aber zieht das Argument auch, wenn die Daten ohnehin (wenn auch mengenmäßig beschränkt) offen zugänglich sind?

              Ja selbstverständlich. Hast Du schon einmal ein Telefonbuch in der Hand gehabt? Das wurde (früher) auch immer kostenlos verteilt und durfte trotzdem nicht vollumfänglich kopiert/weiterverbreitet werden.

              Aber das könntest Du alles in den beiden verlinkten Artikeln nachlesen, nebst verständlicher Beispiele.

              @Norbert:

              Was für einen Telefonanschluss habt Ihr denn?
              Für die Fritzbox gibt es einen Anrufmonitor, der Rückwärtssuche vornimmt. Der ist bereits fertig. Und über einen Kontakt zu Fritz!Labor bekommt man auch eine API für die Integration in eigene Software.

              LG + Gesundheit Localhorst

        2. Guten Morgen und Hallo an alle,

          ihr seid ja schon früh auf den Beinen 👍 Ich war überrascht, hier so schnell Antworten zu finden und bin begeistert, wie dieses Forum lebt und pulsiert. Habt alle vielen Dank!

          Es ist wirklich ein Glücksfall, dass es hier Leute gibt, die sowohl in HTML als auch in .Net zu Hause sind. Das ist selten und ich gehöre auch zu den Programmierern mit Tunnelblick😞 Aber nun habe ich die große Hoffnung, dass ihr mein Problem knacken könnt.

          Seitdem die Rückwärtssuche wieder erlaubt ist (http://www.personensuche.biz/rueckwaertssuche.php) habe ich unserer Praxissoftware einen Anrufmonitor zur Seite gestellt. Beide sind eng miteinander verzahnt. Da ich beides selbst programmiert habe, funktioniert das Zusammenspiel prima. Wenn ein Patient anruft, erscheint rechts unten ein kleines Fenster mit seinem Namen und der Adresse. Bei Bedarf kann die Mitarbeiterin mit einem Klick sofort die Patientenakte öffnen, den Anruf protokollieren, Auskunft über Befunde geben uam.

          Seit Jahren ärgert mich, dass in seltenen Fällen manche Namen nicht korrekt angezeigt werden. Da ich in HTML eine absolute Niete bin, habe ich auch nie herausgefunden, woran das liegt. Geht mal auf https://www.dasoertliche.de/ und macht mal eine Rückwärtssuche mit eurer eigenen Telefonnummer. Vielleicht seht ihr im Quellcode der Seite, dass hier auch "Blumengrüsse" angegeben sind. Ja, und manchmal kommt auch nur die Mailadresse. Aber in 90% aller Fälle wird alles korrekt angezeigt. Könnt ihr bitte nochmal über meinen Code schauen? Danke.

          Code

          1. Hallo Norbert,

            bitte antworte nicht wahllos auf irgendeinen Beitrag im Thread, sondern auf den, auf den du dich gerade beziehst. Sonst entsteht ein Zusammenhang, der gar nicht beabsichtigt ist.

            Seitdem die Rückwärtssuche wieder erlaubt ist (http://www.personensuche.biz/rueckwaertssuche.php) habe ich unserer Praxissoftware einen Anrufmonitor zur Seite gestellt.

            Damit ist auch klar, dass es dir nicht um eine Massenabfrage geht, wie schon angedeutet wurde, und Diskussionen in der Richtung im Moment zu nichts führen.

            Beide sind eng miteinander verzahnt. Da ich beides selbst programmiert habe, funktioniert das Zusammenspiel prima. Wenn ein Patient anruft, erscheint rechts unten ein kleines Fenster mit seinem Namen und der Adresse. Bei Bedarf kann die Mitarbeiterin mit einem Klick sofort die Patientenakte öffnen, den Anruf protokollieren, Auskunft über Befunde geben uam.

            Stellt sich die Frage: Warum zapfst du dafür eine öffentliche Datenbank an? Eine Abfrage mit der Telefonnummer auf die Patienten-Datenbank würde doch genügen. Und dort wüsstest du wenigstens, dass alle nötigen Daten da sind - und in der Form, wie du sie brauchst.

            Könnt ihr bitte nochmal über meinen Code schauen?

            Ich habe keine Ahnung von VB, aber der Code scheint ja "eigentlich" in Ordnung zu sein, wenn er in den meisten Fällen das gewünschte Resultat bringt. Die Frage ist nur, ob es nicht einfacher geht.

            Ich habe beim Ausprobieren leider keinen Eintrag mit Blumengrüßen gefunden; bei meinen Testkandidaten standen Name und Adresse immer brav in einem div-Element mit der ID entry_1. Aber bei einem Treffer mit Blumengrüßen muss ja irgendwas anders sein. Diesen Unterschied im Quellcode zu analysieren (Browser-Developertools, meistens mit F12 erreichbar), wäre deine Aufgabe, wenn du tatsächlich den eingeschlagenen Weg weiter gehen willst.

            Und dann musst du noch jederzeit damit rechnen, dass sich das Layout und die Struktur mal ändert, und du deinen Ansatz wieder anpassen darfst.

            Code

            Und noch eine Bitte: Code als Screenshot einzustellen, ist nicht wirklich sinnvoll, geschweige denn hilfreich. Bitte als Text, und den dann als Code auszeichenen (Button </> oberhalb des Textfelds). Danke.

            Live long and pros healthy,
             Martin

            --
            Lasst uns ins Horn brechen und aufstoßen.
            1. Vielen Dank Martin.

              bitte antworte nicht wahllos auf irgendeinen Beitrag im Thread, sondern auf den, auf den du dich gerade beziehst. Sonst entsteht ein Zusammenhang, der gar nicht beabsichtigt ist.

              Sorry, bin neu hier und habe noch nicht durchschaut, wie sich das "ordnet". Kanns ein Admin richten und die richtige Reihenfolge einrichten?

              Stellt sich die Frage: Warum zapfst du dafür eine öffentliche Datenbank an? Eine Abfrage mit der Telefonnummer auf die Patienten-Datenbank würde doch genügen. Und dort wüsstest du wenigstens, dass alle nötigen Daten da sind - und in der Form, wie du sie brauchst.

              Patienten-Datenbank genügt nicht. Es rufen auich "neue" Patienten an. Und dann kommen auch Anrufe von Krankenkassen, Institutionen, Apotheken uam.

              Ich habe beim Ausprobieren leider keinen Eintrag mit Blumengrüßen gefunden

              Probier mal 03643775947

              Code als Screenshot einzustellen, ist nicht wirklich sinnvoll

              Hab ich mir schon gedacht. Aber irgendwie sieht eingefügter Code nicht so richtig aus

              Imports System.Web
              Imports System.Net
              Imports System.Text
              Imports HtmlAgilityPack
              
              Public Class DasOertliche
                  Private Tab As Char = Convert.ToChar(9)
                  Private CrLf As String = Environment.NewLine
                  Friend Property AnruferListe As List(Of String)
              
                  Friend Function Anrufer(caller As String) As Boolean
                      Dim liste = New List(Of String)
                      Dim client As WebClient = New WebClient()
                      Dim doc As HtmlDocument = New HtmlDocument()
              
                      doc.Load(client.OpenRead("https://www.dasoertliche.de/?form_name=search_inv&buc=674&page=5&context=4&action=43&ph=" & caller), Encoding.UTF8)
                      Dim i As Integer = 1
              
                      Try
                          While i < 10
                              Dim entry = doc.GetElementbyId($"entry_{i}")
                              If entry Is Nothing Then Exit While
              
                              Dim divLeftOnHit = entry.ChildNodes.FirstOrDefault(Function(a) a.HasClass("oe_hit"))
                              Dim _Name As String = HttpUtility.HtmlDecode(divLeftOnHit.ChildNodes.FirstOrDefault(Function(a) a.Name = "a").InnerText.Replace(CrLf, ""))
                              Dim _Adresse As String = divLeftOnHit.ChildNodes.FirstOrDefault(Function(a) a.Name = "address").InnerText.Replace(CrLf, "").Replace(Tab, "")
                              liste.Add(_Name & ";" & _Adresse.Replace("&nbsp", "").Replace(",", " "))
                              i += 1
                          End While
                      Catch ex As Exception
                          Dim dummy As String = ""
                      End Try
              
                      AnruferListe = liste
                      Return liste.Count > 0
                  End Function
              End Class
              

              Da mache ich sicher etwas falsch.

              Edit Rolf B: Ja 😉 - ich habe mal ~~~ statt ` gesetzt

              Wie würdest Du es in HTML machen, um an die Werte für _Name und _Adresse zu kommen? Aber vielleicht findet sich hier im Forum auch noch jemand, der das nach .Net umsetzen kann.

              Viele Grüße Norbert

              1. Hallo,

                bitte antworte nicht wahllos auf irgendeinen Beitrag im Thread, sondern auf den, auf den du dich gerade beziehst. Sonst entsteht ein Zusammenhang, der gar nicht beabsichtigt ist.

                Sorry, bin neu hier und habe noch nicht durchschaut, wie sich das "ordnet". Kanns ein Admin richten und die richtige Reihenfolge einrichten?

                nee, das ist jetzt so, wie's ist.

                Stellt sich die Frage: Warum zapfst du dafür eine öffentliche Datenbank an? Eine Abfrage mit der Telefonnummer auf die Patienten-Datenbank würde doch genügen. Und dort wüsstest du wenigstens, dass alle nötigen Daten da sind - und in der Form, wie du sie brauchst.

                Patienten-Datenbank genügt nicht. Es rufen auich "neue" Patienten an.

                Dann wäre ich doch wieder vorsichtig, ob die gewerbliche Nutzung der Rückwärtssuche überhaupt zulässig ist.

                Ich habe beim Ausprobieren leider keinen Eintrag mit Blumengrüßen gefunden

                Probier mal 03643775947

                Ein Eintrag in Weimar im div#entry_1. Der Name steckt im h2-Nachfahrenelement, die Adresse treffenderweise in einem address-Nachfahrenelement. Die Blumengrüße in einem weiteren div dahinter.

                Gegenprobe mit der Nummer meines bevorzugten Pizza-Lieferdienstes im Nachbarort: Keine Blumengrüße, aber die Struktur ist genau dieselbe:

                <div#entry_1>
                  <div.oe_hit>
                    <div.counter/>
                    <h2>
                      <a>Name</a>
                    </h2>
                    <address>Adresse</address>
                    Sonstiger Müll: Blumengrüße, Bewertungen, Web-Link u.ä.
                  </div>
                </div>
                

                Anscheinend ist dein Codeabschnitt, der das div#entry_1 weiter aufdröselt, das Problem. Da kann ich aber nicht mehr folgen, was diese Anweisungen wirklich tun.

                Hab ich mir schon gedacht. Aber irgendwie sieht eingefügter Code nicht so richtig aus

                Das liegt wohl daran, dass du entweder am Anfang oder am Ende nicht die ganze Zeile mitmarkiert hast, sondern nur einen Teil. Dann macht die Forensoftware ein Stück "Inline-Code" draus.

                Imports System.Web
                Imports System.Net
                Imports System.Text
                Imports HtmlAgilityPack
                
                Public Class DasOertliche
                    Private Tab As Char = Convert.ToChar(9)
                    Private CrLf As String = Environment.NewLine
                    Friend Property AnruferListe As List(Of String)
                
                    Friend Function Anrufer(caller As String) As Boolean
                        Dim liste = New List(Of String)
                        Dim client As WebClient = New WebClient()
                        Dim doc As HtmlDocument = New HtmlDocument()
                
                        doc.Load(client.OpenRead("https://www.dasoertliche.de/?form_name=search_inv&buc=674&page=5&context=4&action=43&ph=" & caller), Encoding.UTF8)
                        Dim i As Integer = 1
                
                        Try
                            While i < 10
                                Dim entry = doc.GetElementbyId($"entry_{i}")
                                If entry Is Nothing Then Exit While
                
                                Dim divLeftOnHit = entry.ChildNodes.FirstOrDefault(Function(a) a.HasClass("oe_hit"))
                                Dim _Name As String = HttpUtility.HtmlDecode(divLeftOnHit.ChildNodes.FirstOrDefault(Function(a) a.Name = "a").InnerText.Replace(CrLf, ""))
                                Dim _Adresse As String = divLeftOnHit.ChildNodes.FirstOrDefault(Function(a) a.Name = "address").InnerText.Replace(CrLf, "").Replace(Tab, "")
                                liste.Add(_Name & ";" & _Adresse.Replace("&nbsp", "").Replace(",", " "))
                                i += 1
                            End While
                        Catch ex As Exception
                            Dim dummy As String = ""
                        End Try
                
                        AnruferListe = liste
                        Return liste.Count > 0
                    End Function
                End Class
                

                Wie würdest Du es in HTML machen, um an die Werte für _Name und _Adresse zu kommen?

                Gar nicht. HTML ist schließlich keine Programmiersprache, sondern eine Auszeichnungssprache. Aber in Javascript würde ich ganz ähnlich anstellen: Ich suche nach einem Element mit der ID "entry_1". Finde ich keins: Pech gehabt. Andernfalls hole ich mir dessen h2-Kindelement mit dem Namen, und das address-Kindelement mit der Adresse. Den Rest werfe ich weg.

                Live long and pros healthy,
                 Martin

                --
                Lasst uns ins Horn brechen und aufstoßen.
                1. Hallo,

                  Das liegt wohl daran, dass du entweder am Anfang oder am Ende nicht die ganze Zeile mitmarkiert hast, sondern nur einen Teil. Dann macht die Forensoftware ein Stück "Inline-Code" draus.

                  Oder es wurde folgendes durchgeführt: Nach nur einem Enter wurde auf Code einfügen gedrückt. Dann hat man zwar eine neue Zeile, aber bekommt trotzdem Inline-Code. Erst wenn man eine Leerzeile erzeugt hat und dann Code einfügen will, fragt die Forumssoftware im extra Formular nach Sprache und Code. @Christian Kruse soll das so oder kann schon bei einer neuen Zeile diese Abfrage erzeugt werden?

                  Gruß
                  Kalk

                  1. Hi,

                    Das liegt wohl daran, dass du entweder am Anfang oder am Ende nicht die ganze Zeile mitmarkiert hast, sondern nur einen Teil. Dann macht die Forensoftware ein Stück "Inline-Code" draus.

                    Oder es wurde folgendes durchgeführt: Nach nur einem Enter wurde auf Code einfügen gedrückt.

                    verstehe ich da was falsch? Ich markiere immer einen Abschnitt und klicke dann den Code-Button. Geht es auch ohne Selektion? - Tatsächlich, dann fügt die Software einen Fitzel Inline-Code ein. Wusste ich bis eben auch noch nicht.

                    Erst wenn man eine Leerzeile erzeugt hat und dann Code einfügen will, fragt die Forumssoftware im extra Formular nach Sprache und Code.

                    Tatsächlich. Kannte ich auch noch nicht. Ich bin nämlich noch nie auf die Idee gekommen, den Button anzuklicken, ohne vorher den gewünschten Code zu selektieren. Das habe ich aus der Gewohnheit von Textverarbeitungen abgeleitet, wo ich ja auch erst den zu formatierenden Text selektieren muss, um dann den gewünschten Formatierungs-Button zu klicken (oder Hotkey zu drücken).

                    Live long and pros healthy,
                     Martin

                    --
                    Lasst uns ins Horn brechen und aufstoßen.
                    1. Hallo,

                      Das habe ich aus der Gewohnheit von Textverarbeitungen abgeleitet, wo ich ja auch erst den zu formatierenden Text selektieren muss, um dann den gewünschten Formatierungs-Button zu klicken (oder Hotkey zu drücken).

                      Das kann man so machen, muss man aber nicht. Da kannst du auch andersherum arbeiten: Text schreiben, Fettschrift wählen, weiter in Fett schreiben, normal wählen und weiter normal schreiben. Geht komplett ohne zu wissen, wie man Text selektiert ;)

                      Gruß
                      Kalk

                      Lasst uns ins Horn brechen und aufstoßen.

                      Vergiss nicht, auf den Weg zu machen…

                      1. Hallo Tabellenkalk,

                        Das kann man so machen, muss man aber nicht. Da kannst du auch andersherum arbeiten: Text schreiben, Fettschrift wählen,

                        und dank Tastenkürzel sogar ohne die Finger von der Tastatur zu nehmen.

                        Bis demnächst
                        Matthias

                        --
                        Du kannst das Projekt SELFHTML unterstützen,
                        indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
              2. Hallo Norbert,

                ja, es gibt im Forum inline-Code, mit Backticks ` eingefasst.

                Und Block-Code, mit zwei Zeilen eingefasst, in denen ~~~ steht.

                Danke für den Hinweis auf das HtmlAgilityPack und das HtmlDocument Objekt darin. Das hatte ich neulich schon gefragt, aber das ist wohl untergegangen.

                Ich würde Dir gerne sagen, dass es bessere Methoden zum Selektieren von Nodes gibt als das Herumturnen auf den ChildNodes Listen. Es wäre schön, wenn HtmlAgilityPack CSS Selektoren anbieten würde. Aber das ist noch "coming soon", bzw. man braucht noch eine Library.

                Es gibt aber auch SelectNodes und SelectSingleNode, die XPath verwenden. Das ist eine andere Abfragesyntax, die nicht ganz einfach ist. Unser Wiki hat was dazu, ob man es versteht, weiß ich nicht. XPath-Dokumentationen neigen zu gruseliger Abstraktheit. Von XPath gibt's Versionen, und .net kennt nur XPath 1.0, das macht es nicht einfacher. Aber was wir brauchen, ist relativ easy:

                dim entries = doc.SelectNodes("//div[starts-with(@id, 'entry_')]")
                if entries IsNot Nothing
                   for each eintrag as HtmlNode in entries
                      dim nameElement = eintrag.SelectSingleNode(".//h2/a")
                      dim addressElement = eintrag.SelectSingleNode(".//address")
                      if nameElement IsNot Nothing
                         ...
                      end if
                
                      if addressElement IsNot Nothing
                         ...
                      end if
                   next
                end if
                

                Nehmen wir die XPathes auseinander:

                //div[starts-with(@id, 'entry_')]

                Das // bedeutet: Beginne am Wurzelknoten des Dokuments, und durchsuche den ganzen Dokumentenbaum nach dem, was hiernach steht. Da steht div, damit spezifiziert man XML (oder HTML) Elemente. Das Dokument wird also komplett nach divs durchsucht.

                Hinter dem div steht was in eckigen Klammern. Das bedeutet: Suche die divs, die eine Zusatzbedingung erfüllen. Diese Zusatzbedingung ist eine weitere XPath-Query, die relativ zu dem div ausgeführt wird, für das die Zusatzbedingung geprüft werden muss.

                Nun muss man wissen, dass man in XPath in unterschiedliche Richtungen suchen kann. Wenn man sich auf einem Element im Dokument aufhält, gibt's davon mehrere: Nach oben (Elternknoten), nach unten (Kindknoten), zur Seite (Geschwisterknoten), und dann noch die Attribute. Wir müssen das id-Attribut prüfen, also entlang der Attributachse laufen, und dafür dient das @ Kürzel.

                @id sucht also an dem div das id Attribut. Und dann gibt's noch ein paar Funktionen. starts-with prüft, ob @id mit 'entry_' beginnt.

                Also alles zusammen: //div[starts-with(@id, 'entry_')] findet alle div Elemente, deren id mit 'entry_' beginnt.

                Das Ergebnis von SelectNodes ist eine HtmlNodeCollection. Allerdings, das ist ein Blödsinn der .net XML Libraries, wenn es gar keinen Eintrag gab, kommt Nothing zurück, keine leere Collection. Deswegen die Abfrage auf IsNot Nothing, bevor man die Treffer untersucht.

                Das geschieht, indem man die entries mit For Each durchläuft.

                Innerhalb der Entries wird nun nach Name und Adresse gesucht. Der Name ist ein a Element in einem h2 Element. Das suchen wir mit dem XPath .//h2/a. .// bedeutet: Durchsuche alle Elemente unterhalb des aktuellen Elements, und auch ihre Kinder und Kindeskinder. Das aktuelle Element ist das, auf dem SelectNodes aufgerufen wird. Analog wird das address Element gesucht.

                Die Rückgabewerte müssen noch auf Nothing geprüft werden, falls nichts gefunden wird. Wie Du reagieren willst, wenn Name oder Adresse fehlen (Leerstring anzeigen, diesen Treffer überspringen) musst Du für Dich entscheiden.

                Auf diese Weise kommst Du mit relativ kompaktem Code an die richtigen Elemente heran, würde ich behaupten.

                Rolf

                --
                sumpsi - posui - obstruxi
              3. Hallo

                        Try
                            While i < 10
                                '
                                Dim _Name As String = HttpUtility.HtmlDecode(divLeftOnHit.ChildNodes.FirstOrDefault(Function(a) a.Name = "a").InnerText.Replace(CrLf, ""))
                                '
                                i += 1
                            End While
                

                Da mache ich sicher etwas falsch.

                Ja und ja, auch in deiner Logik.

                Wie von RolfB schon angemerkt, steckt der Name in einem Link (a) in einer Überschrift (h2). Du suchst aber nach „irgendeinem“ Link. Wenn vor der Überschift ein anderer Link notiert ist, bekommst du halt diesen zurück. Suche also zuerst nach der Überschrift des Eintrags (h2) und in dieser Überschrift nach dem Link.

                Ich habe keine Erfahrung mit VB neuer als MS-Office-97, geschweige denn mit VB.NET. Rolfs Ausfühungen lassen mich aber vermuten, dass die kombinierte Suche nach einem Link in einer Überschrift in einem Schritt, wie es sie beispielsweise in JavaScript gibt [1], nicht möglich ist. Wenn ich das richtig verstehe, musst du tatsächlich erst die Überschrift finden und danach dort den Link mit dem Namen herausklauben.

                Tschö, Auge

                --
                Ein echtes Alchimistenlabor musste voll mit Glasgefäßen sein, die so aussahen, als wären sie beim öffentlichen Schluckaufwettbewerb der Glasbläsergilde entstanden.
                Hohle Köpfe von Terry Pratchett

                1. Den Link mit dem Namen bekämst du in JS mit var namensElement = document.querySelector('#entry_23 .oe_hit h2 a');, wobei sich die Frage stellt, ob man die ID im Selektor wirklich braucht. ↩︎

                1. Hi,

                  Wie von RolfB schon angemerkt, steckt der Name in einem Link (a) in einer Überschrift (h2).

                  diese Struktur hatte ich ja auch schon skizziert.

                  Den Link mit dem Namen bekämst du in JS mit var namensElement = document.querySelector('#entry_23 .oe_hit h2 a');, wobei sich die Frage stellt, ob man die ID im Selektor wirklich braucht.

                  Wenn die Telefonbuchsuche mehrere Treffer liefert, stehen die in div#entry_1 bis div_entry_10. Aber bei der Rückwärtssuche kann es eigentlich nicht mehrere Treffer geben, sondern nur entweder einen oder gar keinen.

                  Dennoch würde ich im Selektor eher .oe_hit als redundant weglassen.

                  Live long and pros healthy,
                   Martin

                  --
                  Lasst uns ins Horn brechen und aufstoßen. Höchste Zeit, auf den Weg zu machen.
                  (mit freundlichem Dank an Tabellenkalk für die Ergänzung 😀)
                  1. Hallo

                    Wie von RolfB schon angemerkt, steckt der Name in einem Link (a) in einer Überschrift (h2).

                    diese Struktur hatte ich ja auch schon skizziert.

                    Ja, das hatte ich gesehen.

                    Den Link mit dem Namen bekämst du in JS mit var namensElement = document.querySelector('#entry_23 .oe_hit h2 a');, wobei sich die Frage stellt, ob man die ID im Selektor wirklich braucht.

                    Wenn die Telefonbuchsuche mehrere Treffer liefert, stehen die in div#entry_1 bis div_entry_10. Aber bei der Rückwärtssuche kann es eigentlich nicht mehrere Treffer geben, sondern nur entweder einen oder gar keinen.

                    Das sollte zutreffen, aber man weiß ja nie.

                    Dennoch würde ich im Selektor eher .oe_hit als redundant weglassen.

                    In Sachen HTML-Struktur ist der Container tatsächlich redundant. Wenn ich das richtig gesehen habe, enthält jeder Container für einen Suchtreffer (#entry_N) ein div.oe_hit als einziges direktes Kindelement. Erst darin befindet sich der gefundene Eintrag.

                    Vorausgesetzt, man will die ganze Suchtrefferliste durchgehen, weil man nicht sicher ist, dass es wirklich nur einen Treffer gibt, dann ersparte einem der Query .oe_hit h2 a statt #entry_23 h2 a das Handling der Zähler in der ID. Dann würde man in der Konsequenz aber auch querySelectorAll benutzen und über das Ergebnis iterieren.

                    Mir ging es hier aber nur darum, ein Beispiel für das Auffinden eines auf bekannte Art geschachtelten Elements in einem Schritt anführen, was wohl in VB so nicht funktioniert.

                    Tschö, Auge

                    --
                    Ein echtes Alchimistenlabor musste voll mit Glasgefäßen sein, die so aussahen, als wären sie beim öffentlichen Schluckaufwettbewerb der Glasbläsergilde entstanden.
                    Hohle Köpfe von Terry Pratchett
                2. Hallo Auge,

                  der Blumengruß-Link befindet sich im DOM hinter dem Link mit dem Namen. Aber der Name steckt immer im h2 und die Suche von Norbert geht nie rekursiv vor, darum verstehe ich nicht, warum sein Code überhaupt mal einen Namen findet. Er iteriert ja nur über die ChildNode Liste des div.oe-hit.

                  Meine von Dir verlinkten Ausführungen sollten belegen, dass eine bessere Suche durchaus möglich ist. Nur eben nicht mit CSS Selektoren und QuerySelector, sondern mit SelectNodes und einer XPath Query (ich habe allerdings erstmal zwei Flaschen Ballistol[1] trinken müssen, bis ich meine XPath-Kenntnisse entrostet hatte). Mein Beispiel ist deshalb zweistufig, um zu einem Entry immer Name und Adresse beisammen zu haben.

                  Rolf

                  --
                  sumpsi - posui - obstruxi

                  1. WD-40 wäre besser gewesen, ist aber zum Trinken nicht zu empfehlen 🤮. Ballistol macht nur 😰 und 🤢… ↩︎