html untersuchen

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.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Ich wollte mal ein bisschen etwas mit beautiful soup machen, da kahm mir die idee ein paar infos vom Forum hier auszugeben.
Also habe ich mit den "DevTools" von chromium den xpath von dem objekt ausgegeben und versucht das mit python umzusetzen.

aber leider bekomme ich immer ein Fehler der sagt dass das li nicht existiert
Fehler

Code: Alles auswählen

Traceback (most recent call last):
  File "/media/usb/.Projekte/Python3/Python Forum Info.py", line 12, in <module>
    print(soup.body.div.contents[3].div.ul.contents[2].li.contents[2].div.p)
  File "/home/pi/.local/lib/python3.7/site-packages/bs4/element.py", line 652, in __getattr__
    self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'li'


Aktueller Code

Code: Alles auswählen

from bs4 import BeautifulSoup
from urllib.request import urlopen

print("webseite wird geöffnet")
webseite = urlopen("https://www.python-forum.de/index.php")
print("wird gelesen")
html = webseite.read().decode()
print("fertig")
soup = BeautifulSoup(html, "html.parser")
werte = [["Themen insgesamt"], ["Mitglieder insgesamt"], ["neuestes Mitglied"]]

print(soup.body.div.contents[3].div.ul.contents[2].li.contents[2].div.p)
#/body/div[3]/div/ul[2]/li[2]/div/p

Xpath

Code: Alles auswählen

/body/div[3]/div/ul[2]/li[2]/div/p
Könnt ihr mir helfen?
Ich benutze beautiful soup zum ersten mal.
Ich freue mich auf antworten.
nezzcarth
User
Beiträge: 1638
Registriert: Samstag 16. April 2011, 12:47

Fire Spike hat geschrieben: Sonntag 20. Oktober 2019, 07:29 Könnt ihr mir helfen?
Ich benutze beautiful soup zum ersten mal.
Ich freue mich auf antworten.
Ich vermute, dass der Ausdruck nicht ganz korrekt ist. Am einfachsten kann man das im interaktiven Python-Interpret/ipython ausprobieren. Dort kannst du den Ausdruck schrittweise von Links nach Rechts durchgehen und schauen, ob irgendwo ein Fehler kommt. Du solltest auch berücksichtigen, dass BeautifulSoup kein JavaScript interpretiert. JavaScript Code kann die Webseite modifizieren und diese Modifikationen sind normalerweise in den DOM, den man sich in Entwicklertools anzeigen lassen kann, schon eingeflossen. Wenn du also den Eindruck hast, dass die Elementstrukturen irgendwie nicht passen, schau mal im "rohen" Seitenquelltext (strg + U) nach.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

bei

Code: Alles auswählen

print(soup.body.div.contents[3].div.ul.contents[2])
komme ich nur noch eine leerzeile ausgegeben. :? :shock:
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Fire Spike: Man kann XPath nicht einfach so in BeautifulSoup übersetzen, die Semantik ist zwar ähnlich, aber nicht gleich.

Bei der Entwicklung von so etwas ist es echt hilfreich, dass es interaktive Python-Shells gibt, wo man solche Ausdrücke Schritt für Schritt entwickeln und ausprobieren kann. Da kann man Deinen Ausdruck einfach mal ausprobieren in dem man, von `soup` ausgehend, immer einen Schritt hinzufügt, und schaut was dabei heraus kommt. Also `soup.body`, `soup.body.div`, `soup.body.div.contents[3]`, und so weiter. Da sieht man dann, dass man bei `soup.body.div.contents[3].div.ul.contents[2]` eine Zeichenkette heraus bekommt, beziehungsweise ein Objekt vom Typ `NavigableString`, der von `str` abgeleitet ist:

Code: Alles auswählen

In [159]: soup.body.div.contents[3].div.ul                                      
Out[159]: 
<ul class="nav-main linklist" id="nav-main" role="menubar">
<li class="quick-links dropdown-container responsive-menu" data-skip-responsive="true" id="quick-links">
<a class="dropdown-trigger" href="#">
<i aria-hidden="true" class="icon fa-bars fa-fw"></i><span>Schnellzugriff</span>
</a>
<div class="dropdown">
<div class="pointer"><div class="pointer-inner"></div></div>
<ul class="dropdown-contents" role="menu">
<li class="separator"></li>
<li>
<a href="./search.php?search_id=unanswered&amp;sid=4bb00297354aa2ba933809b3a2077e60" role="menuitem">
<i aria-hidden="true" class="icon fa-file-o fa-fw"></i><span>Unbeantwortete Themen</span>
</a>
</li>
<li>
<a href="./search.php?search_id=active_topics&amp;sid=4bb00297354aa2ba933809b3a2077e60" role="menuitem">
<i aria-hidden="true" class="icon fa-file-o fa-fw"></i><span>Aktive Themen</span>
</a>
</li>
<li class="separator"></li>
<li>
<a href="./search.php?sid=4bb00297354aa2ba933809b3a2077e60" role="menuitem">
<i aria-hidden="true" class="icon fa-search fa-fw"></i><span>Suche</span>
</a>
</li>
<li class="separator"></li>
</ul>
</div>
</li>
<li data-skip-responsive="true">
<a href="/help/faq?sid=4bb00297354aa2ba933809b3a2077e60" rel="help" role="menuitem" title="Häufig gestellte Fragen">
<i aria-hidden="true" class="icon fa-question-circle fa-fw"></i><span>FAQ</span>
</a>
</li>
<li class="rightside" data-skip-responsive="true">
<a accesskey="x" href="./ucp.php?mode=login&amp;sid=4bb00297354aa2ba933809b3a2077e60" role="menuitem" title="Anmelden">
<i aria-hidden="true" class="icon fa-power-off fa-fw"></i><span>Anmelden</span>
</a>
</li>
<li class="rightside" data-skip-responsive="true">
<a href="./ucp.php?mode=register&amp;sid=4bb00297354aa2ba933809b3a2077e60" role="menuitem">
<i aria-hidden="true" class="icon fa-pencil-square-o fa-fw"></i><span>Registrieren</span>
</a>
</li>
</ul>

In [160]: soup.body.div.contents[3].div.ul.contents[2]                          
Out[160]: '\n'
Und im nächsten Schritt bekommt man dann die Ausnahme, weil dieses Objekt natürlich kein `li`-Attribut mehr hat.

`lxml.html` kann im Gegensatz zu BeautifulSoup XPath. aber auch da kommt nichts bei herum:

Code: Alles auswählen

In [174]: import lxml.html                                                      

In [175]: element = lxml.html.fromstring(html)                                  

In [176]: element.xpath("/body/div[3]/div/ul[2]/li[2]/div/p")                   
Out[176]: []

In [177]: element.xpath("body")                                                 
Out[177]: [<Element body at 0x7fe73270b138>]

In [178]: element.xpath("body/div[3]")                                          
Out[178]: []

In [179]: element.xpath("body/div")                                             
Out[179]: [<Element div at 0x7fe73270b818>, <Element div at 0x7fe73270b408>]
Es gibt direkt unter <body> nur zwei <div>-Elemente, Du versuchst aber auf das nicht vorhandene dritte zuzugreifen.

Man würde aber so auch nicht vorgehen weil das recht fragil ist und schnell kaputt geht sollte sich auch nur eine Kleinigkeit an so einem kompletten Pfad ändern. Zum Beispiel weil zu Weihnachten (oder anderen Feiertagen) etwas am Layout geändert wird und auf oberster Ebene ein <div> dazu kommt und damit schon ziemlich früh in den falschen Zweig des Dokuments abgebogen wird. Man versucht sich in solchen Fällen mehr an semantischen Informationen zu orientieren statt an der eher nichtssagenden Struktur. Die ist auch wichtig, aber eben nicht so zuverlässig. Solche Information steckt (hoffentlich) in IDs und CSS-Klassennamen.

Das Forum enttäuscht in dieser Hinsicht nicht, denn wenn ich die `werte`-Liste richtig interpretiere, dann suchst Du die Information die in diesem <div> steht:

Code: Alles auswählen

<div class="stat-block statistics">
  <h3>Statistik</h3>
  <p>
    Beiträge insgesamt <strong>346340</strong>
    • Themen insgesamt <strong>42143</strong>
    • Mitglieder insgesamt <strong>17859</strong>
    • Unser neuestes Mitglied:
    <strong><a href="./memberlist.php?mode=viewprofile&amp;u=22911" class="username">MiHe</a></strong>
  </p>
</div>
Und das <div> hat aussagekräftige Klassen.

Code: Alles auswählen

In [182]: soup.find("div", "statistics")                                        
Out[182]: 
<div class="stat-block statistics">
<h3>Statistik</h3>
<p>Beiträge insgesamt <strong>346339</strong> • Themen insgesamt <strong>42143</strong>
• Mitglieder insgesamt <strong>17859</strong> • Unser neuestes Mitglied:
<strong><a class="username" href="./memberlist.php?mode=viewprofile&amp;u=22911&amp;sid=4bb00297354aa2ba933809b3a2077e60">MiHe</a></strong>
</p>
</div>
Von da aus dann robust weiter wäre die richtige Zeichenkette vor dem gewünschten <strong> zu identifizieren, von da zum nächsten Element zu gehen, also dem <strong>, und sich dessen Inhalt geben zu lassen:

Code: Alles auswählen

In [191]: soup.find("div", "statistics").p.find(text=re.compile("Themen insgesamt")).next.text
Out[191]: '42143'
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

danke __blackjack__ ! :D
damit mir klar wird wie ich das verwenden kann, würde ich mich freuen wenn du mir ein tipp geben kannst damit ich die user die online sind anzeigen kann.
Ich brauche kein code nur tipps :wink:
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Fire Spike: Dafür dürfte der entscheidende Tipp wohl sein das man sich dafür anmelden muss. Unangemeldet sieht man nur wie viele Benutzer Online sind, aber nicht welche.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

UPS!!! daran hatte ich nicht gedacht. kann ich mich anmelden per python und dann auswerten?
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ja, man kann sich per Python anmelden. Ich würde dazu die `requests`-Bibliothek empfehlen, und Du brauchst daraus ein `Session`-Objekt, weil die Forensoftware Cookies verwendet um sich die Anmeldung zu merken. Also theoretisch sollte die Indexseite direkt nach dem Anmelden auch ohne `Session` gehen, aber spätestens wenn man danach weitere Abfragen machen möchte, muss man die Cookies behalten um angemeldet zu bleiben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Danke __blackjack__ .
ich habe jetzt einen code gemacht aber nachdem ich mich angemeldet habe und die webseite anfordere die ich brauche bin ich nicht mehr eingeloggt. :(
Ich bekomme immer die seite wo verhindert dass ich auf die online-user seite komme.
Ich habe schon hunderte beispiele durchgeschaut aber es geht nichts. :cry:
Was mache ich falsch? :?:

Code: Alles auswählen

from bs4 import BeautifulSoup
from urllib.request import urlopen
import requests
import re

def suche1():
    webseite = urlopen("https://www.python-forum.de/index.php")
    html = webseite.read().decode()
    soup = BeautifulSoup(html, "html.parser")

    werte = ["Themen insgesamt", "Beiträge insgesamt", "Mitglieder insgesamt", "neuestes Mitglied"]

    ausgabetext = ""

    for text in werte:
        ausgabetext += text + ": " + soup.find("div", "statistics").p.find(text=re.compile(text)).next.text + "\n"
    
    print(ausgabetext)

def suche2():
    with requests.Session() as s:
        benutzerdaten = {"benutzername" : "Fire Spike",
                         "passwort" : "schreibe-ich-nicht"}
        s.post("https://www.python-forum.de/ucp.php?mode=login", data=benutzerdaten)
        html = s.get("https://www.python-forum.de/viewonline.php").text
        print(html)
#     html = 
#     soup = BeautifulSoup(html, "html.parser")
#     e = soup.find("div", "forumbg forumbg-table")


suche1()
suche2()
Sirius3
User
Beiträge: 17758
Registriert: Sonntag 21. Oktober 2012, 17:20

suche1 und suche2 sind zwei bescheuerte Funktionsnamen, weil ersten durchnummeriert und zweitens beide nichts mit einer Suche zu tun haben.
Strings stückelt man nicht mit + zusammen, erst recht nicht, wenn da ein langer Ausdruck drin vorkommt. ´s` ist ein schlechter Name und insbesondere für eine Session.
Zum eigentlichen Problem: Du mußt natürlich alle Anfragen mit der Session machen, in der Du Dich eingeloggt hast.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Sirius3 hat geschrieben: Sonntag 20. Oktober 2019, 19:44 suche1 und suche2 sind zwei bescheuerte Funktionsnamen, weil ersten durchnummeriert und zweitens beide nichts mit einer Suche zu tun haben.
Strings stückelt man nicht mit + zusammen, erst recht nicht, wenn da ein langer Ausdruck drin vorkommt. ´s` ist ein schlechter Name und insbesondere für eine Session.
Zum eigentlichen Problem: Du mußt natürlich alle Anfragen mit der Session machen, in der Du Dich eingeloggt hast.
ich gebe dir recht, die namen sind bescheuert. :?
wie alle mit der session machen in der ich mich eingeloggt habe? :?: ich habe gedacht "s" ist immer die gleiche session. :oops:
Sirius3
User
Beiträge: 17758
Registriert: Sonntag 21. Oktober 2012, 17:20

Für `suche2`, aber `suche1` hat keine Session.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

ich will ja auch nur eine session in suche2
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

namen verbessert, könnt ihr mir jetzt helfen?

Code: Alles auswählen

from bs4 import BeautifulSoup
from urllib.request import urlopen
import requests
import re

def status_parser():
    webseite = urlopen("https://www.python-forum.de/index.php")
    html = webseite.read().decode()
    soup = BeautifulSoup(html, "html.parser")

    werte = ["Themen insgesamt", "Beiträge insgesamt", "Mitglieder insgesamt", "neuestes Mitglied"]

    ausgabetext = ""

    for text in werte:
        ausgabetext += text + ": " + soup.find("div", "statistics").p.find(text=re.compile(text)).next.text + "\n"
    
    print(ausgabetext)

def online_benutzer_parser():
    with requests.Session() as session:
        benutzerdaten = {"benutzername" : "Fire Spike",
                         "passwort" : "Editiert durch Jankie"}
        session.post("https://www.python-forum.de/ucp.php?mode=login", data=benutzerdaten)
        html = session.get("https://www.python-forum.de/viewonline.php").text
        print(html)
#     html = 
#     soup = BeautifulSoup(html, "html.parser")
#     e = soup.find("div", "forumbg forumbg-table")


status_parser()
online_benutzer_parser()
Zuletzt geändert von Fire Spike am Montag 21. Oktober 2019, 15:22, insgesamt 1-mal geändert.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Aufjedefall solltest du dir ein neues Passwort suchen. Das hast du nämlich vergessen aus dem Code zu nehmen.

#edit: Nimms mir nicht böse, aber habe mich kurz in deinen Account eingeloggt um das Passwort aus dem Code zu entfernen bevor es jemand falsches in die Hände bekommt.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

danke jankie.
ich werde es ändern!
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Ich habe jetzt schon hunderte beispiele angesehen.
Die meisten sind wie meins nur dass sie bei den personen die es geschrieben haben funktionierten
WAS MACHE ICH FALSCH?
ICH KOMME MIR DUMM VOR(bin ich vielleicht auch :lol: :lol: :lol: :lol:)
Sirius3
User
Beiträge: 17758
Registriert: Sonntag 21. Oktober 2012, 17:20

@Fire Spike: schau mal, was wirklich an Parametern übertragen wird, alle. Dazu mußt Du erst mit einen Request Dir die Parameter erzeugen lassen und die Session-Cookies abfragen. Und dann mußt Du noch die Bot-Erkennung austricksen.
Fire Spike
User
Beiträge: 329
Registriert: Montag 13. Mai 2019, 16:05
Wohnort: Erde

Code: Alles auswählen

Dazu mußt Du erst mit einen Request Dir die Parameter erzeugen lassen und die Session-Cookies abfragen. 
Das werde ich mal probieren.

Code: Alles auswählen

Und dann mußt Du noch die Bot-Erkennung austricksen.
Wie funktoniert den die? weisst du das oder soll ich damaskus fragen?
dass muss ich wissen dass ich weiss was es für wege gäbe.
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Also bei mir funktioniert das. Die Bot-Erkennung greift anscheinend erst wenn man zu viele Fehlversuche gemacht hat. Keine Ahnung wann die wieder aufhört. Wenn die nach normalem Ab- und wieder Anmelden nicht wieder weg ist, dann ist es wahrscheinlich zeitgesteuert, und Du musst halt warten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten