@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&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&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&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&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&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&u=22911&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'