Anfängerfrage zu re.compile

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.
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

Hallo zusammen,

Ich habe vor kurzem begonnen mich mit Python zu beschäftigen.
Bisher habe ich nur vorhandene Scripte auf meine Bedürfnisse angepasst nund möchte
ich mein erstes eigenes Script schreiben...

Ich frage dabei die Inhalte einer Webseite mit re.compile ab was auch schon funktioniert...

Code: Alles auswählen

episode = re.compile('title="Episode">(.+?)</span>', re.DOTALL).findall(entry)[0]
nun kommt es vor, das der Ausdruck title="Episode"> mal nicht vorhanden ist, dann läuft das script in einen Fehler

IndexError: list indes out of range...

gibt es eine möglichkeit das mit if/else abzufangen? Also wenn die Abfrage scheitert dann Episode = " ", wenn nicht dann
Episode = episode
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@sveni_lee: statt HTML mit regulären Ausdrücken zu quälen, solltest Du einen dafür geeignete HTML-Parser verwenden. Beautifulsoup wird in diesem Zusammenhang oft genannt.
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

damit habe ich mich noch nicht weiter beschäftigt...
hört sich interessant an...

aber trotzdem, gibt es eine Möglichkeit das auch mit regulären Ausdrücken in den Griff zu bekommen?

Ich habe beutifulsoup nicht installiert... bei mir ist Python auf einem USB Stick installiert damit ich das
immer dabei habe und auch hier auf arbeit einiges testen kann...
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

sveni_lee hat geschrieben:aber trotzdem, gibt es eine Möglichkeit das auch mit regulären Ausdrücken in den Griff zu bekommen?
Dein Problem ist nicht der reguläre Ausdruck, sondern dass du den Rückgabewert von findall() im Grunde ignorierst.
the more they change the more they stay the same
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@sveni_lee: HTML kann man im Allgemeinen nicht mit regulären Ausdrücken parsen. Weil die meisten heutzutage verwendeten regex-Parser Erweiterungen haben, die über reguläre Grammatiken hinausgehen, ist es sicherlich möglich, mit viel Aufwand irgendwas zusammenbasteln. Die Frage ist aber, ob man das will. BeautifulSoup ist deutlich einfacher zu verwenden und kommt auch oft mit fehlerhaftem HTML zurecht. Mach dir also dein Leben nicht schwerer, als nötig, und verwende BeautifulSoup.
In specifications, Murphy's Law supersedes Ohm's.
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

@Dav1d:
Aber es gibt doch keinen Rückgabewert wenn title="Episode"> gar nicht im quelltext vorhanden ist
dann gibts es einen Error und genau den Fall möchte ich abfangen...

oder habe ich das falsch verstanden...?

@pillmuncher:
okay, verstehe...

Ich habe mit BS noch nicht gearbeitet... und bekomme es noch nichtmal irgendwie nachinstalliert...
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

BeautifulSoup habe ich jetzt installiert bekommen... war gar nicht so kompliziert... :-)
Aber einen Rückgabewert bekomme ich noch nicht...

Code: Alles auswählen

def get_SerienSendetermine(soup1):
	address = ('http://www.wunschliste.de/serienplaner/0/0')
	req_page = getURL(address)
	soup = BeautifulSoup(req_page)
	self.get_title(soup)
	for all in soup1.findAll("li", {"id" : "e_[^\s]*"}):
		for title in all.findAll("td", {"class" : "sendung b[^\s]*"}):
			title = title.getText()	
				
		print "===========TESTSERIE START============="
		print "Title :"+title	
		print "===========TESTSERIE END============="	
get_SerienSendetermine()		
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich empfehle erstmal ein Python Tutorial durchzuarbeiten, da dein Problem hier schlicht ist, dass dein Verstaendnis von Funktionen und deren Rueckgabewerten falsch ist.
Benutzeravatar
miracle173
User
Beiträge: 127
Registriert: Samstag 6. Februar 2016, 00:28

sveni_lee hat geschrieben:(...)

Code: Alles auswählen

episode = re.compile('title="Episode">(.+?)</span>', re.DOTALL).findall(entry)[0]
(...)
IndexError: list index out of range...
(...)
gibt es eine möglichkeit das mit if/else abzufangen?
(...)
Wenn schon der Index "out of range ist" dann ist die Liste leer.Also

Code: Alles auswählen

findings= re.compile('title="Episode">(.+?)</span>', re.DOTALL).findall(entry)
if findings:
    episode = findings[0]
else:
    episode=''
ist vermutlich das was du meinst.
BlackJack

@sveni_lee: Die Einrücktiefe sollte vier Leerzeichen pro Ebene betragen und nicht nur drei. Die Namensschreibweise entspricht auch nicht dem Style Guide for Python Code. Dem folgend sollten auch bei BeautifulSoup die neuen Namen verwendet werden, also `find_all()` statt `findAll()`. Letzteres wird irgendwann ausgemustert.

Wenn man anfängt Namen durchzunummerieren macht man mit ziemlicher Sicherheit etwas falsch. Entweder war man zu faul sich einen vernünftigen Namen auszudenken, oder man wollte eigentlich eine Datenstruktur, meistens eine Liste, verwenden. Im Fall von `soup1` ist das aber einfach nur falsch. Das Argument wird beim Aufruf gar nicht übergeben und dort wo es verwendet wird, sollte wohl eher das nicht verwendendete, lokale `soup` benutzt werden.

Warum ist die URL unnötig in Klammern gesetzt?

`self.get_title()` ”riecht” komisch. Warum ist das eine Methode? Auf welchen Zustand des Objekts wird darin zugegriffen? `get_SerienSendetermine()` müsste dann ja auch eine Methode sein, da wird das Objekt aber auch überhaupt nicht benutzt.

`all` ist der Name einer eingebauten Funktion, den sollte man nicht an etwas anderes binden.

Wenn man bei BeautifulSoup reguläre Ausdrücke verwendet, dann müssen das kompilierte Ausdrücke sein, sonst kann die Bibliothek reguläre Ausdrücke von normalen Zeichenketten nicht unterscheiden. Bei 'class' könnte es ein Problem mit dem Ausdruck geben weil 'class' nicht als *ein* Wert behandelt wird, also weder von HTML noch von BeautifulSoup.

`title` wird an zwei verschiedene Werte gebunden, also eine doppelverwendung des gleichen Namens für unterschiedliche Bedeutungen.

Die ``print``-Zeilen scheinen mir nicht weit genug eingerückt‽ Es wird auf jeden Fall nicht der Fall behandelt, dass kein Titel gefunden wird in der Schleife, was im ersten Durchlauf der äusseren Schleife zu einer Ausnahme führen würde weil `title` nicht definiert ist, oder zu Ausgaben von `title`-Werten aus vorherigen Durchläufen der äusseren Schleife.

Bei solchen Aufgaben bietet es sich an das in einer interaktiven Python-Shell einfach mal Schritt für Schritt durchzuführen, sich die Zwischenergebnisse anzuschauen und die Aufrufe und Argumente ”live” zu entwicklen.

Die Funktion mit den oben genannten Änderungen (ungetestet):

Code: Alles auswählen

def get_seriensendetermine():
    soup = BeautifulSoup(
        get_content('http://www.wunschliste.de/serienplaner/0/0')
    )
    for li_node in soup.find_all('li', {'id': re.compile(r'e_[^\s]*')}):
        for title_node in li_node.find_all(
            'td', {'class': re.compile(r'sendung b[^\s]*')}
        ):
            title = title_node.get_text()   
            
            print('===========TESTSERIE START=============')
            print('Title :', title)
            print('===========TESTSERIE END===============')
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

miracle173 hat geschrieben:
sveni_lee hat geschrieben:(...)
Wenn schon der Index "out of range ist" dann ist die Liste leer.Also

Code: Alles auswählen

findings= re.compile('title="Episode">(.+?)</span>', re.DOTALL).findall(entry)
if findings:
    episode = findings[0]
else:
    episode=''
 
ist vermutlich das was du meinst.
ja genau, das hat funktioniert... Danke

BlackJack hat geschrieben:Die Funktion mit den oben genannten Änderungen (ungetestet):

Code: Alles auswählen

def get_seriensendetermine():
    soup = BeautifulSoup(
        get_content('http://www.wunschliste.de/serienplaner/0/0')
    )
    for li_node in soup.find_all('li', {'id': re.compile(r'e_[^\s]*')}):
        for title_node in li_node.find_all(
            'td', {'class': re.compile(r'sendung b[^\s]*')}
        ):
            title = title_node.get_text()   
           
            print('===========TESTSERIE START=============')
            print('Title :', title)
            print('===========TESTSERIE END===============')
das Script gibt zwar keine Fehlermeldung aus aber auch kein Ergebnis... Das wird dann wohl an den Argumenten liegen...
BlackJack

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from pprint import pprint

import requests
from bs4 import BeautifulSoup


def parse_number(node):
    result = None
    if node:
        try:
            result = int(node.text)
        except ValueError:
            pass  # Intentionally ignored.
    return result


def parse_episode_node(node):
    print(node)
    time = node.find('td', 'zeit').text
    if not time.endswith('Uhr'):
        raise ValueError('time does not end with "Uhr"')
    episode_title_node = node.find('a', 'na')
    result = {
        'episode': parse_number(node.find('span', 'epg_ep')),
        'episode_title': episode_title_node.text if episode_title_node else None,
        'season': parse_number(node.find('span', 'epg_st')),
        'station': node.find('img', 'stationslogo')['title'],
        'time': time,
        'title': node.find('a', 'sendung').text,
    }
    return result


def is_episode_id(string):
    return string and string.startswith('e_')


def main():
    source = requests.get('http://www.wunschliste.de/serienplaner/0/0').text
    soup = BeautifulSoup(source)
    ul_node = soup.find('ul', 'my_epgliste')
    pprint(map(parse_episode_node, ul_node('li', {'id': is_episode_id})))


if __name__ == '__main__':
    main()
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

cool... Danke das klappt....

allerding ist mir aufgefallen, dass die Methode mit BS weniger Treffer als mit der "unschönen" Methode...

und stop das BS script wenn bei einer Serie keinen 'episode_title' hat.

wenn ich mich jetzt richtig belesen habe, liegt das dann hieran:

Code: Alles auswählen

'episode_title': episode_title_node.text if episode_title_node else None,
BlackJack

@sveni_lee: Nein die Zeile stoppt das Programm nicht wenn es keinen Episodentitel gibt. Und wo der zahlenmässige Unterschied liegt, da musst Du halt mal schauen worin sich nicht enthaltene Datensätze von den Erkannten unterscheiden.
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

@BlackJack:

ja, da gibt es Unterschiede... Es gibt keine "Einträge" für season, episode und episoden_title...

Code: Alles auswählen

<li id="e_120" style="display:none;" style="display:none;"><table><colgroup><col width="65"><col width="12"><col width="475"><col width="70"></colgroup>
<tr><td rowspan="3" class="zeit">16.55 Uhr</td><td colspan="2" class="entry"><a href="/serie/two-and-a-half-men" class="sendung b2">Two and a Half Men</a><a href="http://www.wunschliste.de/kalender.pl?s=8164&ktermin_start=20160315T165500&ktermin_ende=20160315T172500&kid=201603151655077&sz=0&sender=&titel=" class="episode_kalender"><img src="/gfx/kal.gif" width="10" height="9" border="0" title="Zum Kalender hinzufügen" class="kalsprite"></a></td><td rowspan="2" style="background-color:#ffffff;"><img src="/gfx/logos/77.gif" class="stationslogo" alt="ATV (Österreich)" title="ATV (Österreich)"><img src="/gfx/flaggen/flagge_au.png" alt="Österreich" title="Österreich" class="linkflag"></td></tr>
<tr><td rowspan="2"></td><td><span class="epg_st" style="background-color:#053357" title="Staffel">1</span><span class="epg_ep" title="Episode">13</span>&nbsp;<a href="/episode/79298" class="na" target="_new">Im Bett mit Angina</a> (Wdh.)</td></tr>
</table>
</li>
<script type="text/javascript">
s_regional[121]=1;
s_paytv[121]=0;
s_neu[121]=0;
s_prime[121]=0;
</script>
<li id="e_121" style="display:none;" style="display:none;"><table><colgroup><col width="65"><col width="12"><col width="475"><col width="70"></colgroup>
<tr><td rowspan="3" class="zeit">16.55 Uhr</td><td colspan="2" class="entry"><a href="/serie/funnymals" class="sendung b2">Funnymals</a><a href="http://www.wunschliste.de/kalender.pl?s=24884&ktermin_start=20160315T165500&ktermin_ende=20160315T173000&kid=201603151655014&sz=0&sender=&titel=" class="episode_kalender"><img src="/gfx/kal.gif" width="10" height="9" border="0" title="Zum Kalender hinzufügen" class="kalsprite"></a></td><td rowspan="2" style="background-color:#ffffff;"><img src="/gfx/logos/14.gif" class="stationslogo" alt="SRF 1 (Schweiz)" title="SRF 1 (Schweiz)"><img src="/gfx/flaggen/flagge_ch.png" alt="Schweiz" title="Schweiz" class="linkflag"></td></tr>
<tr><td rowspan="2"></td><td>FUNNYMALS</a></td></tr>
</table>
</li>
und das ist die dazugehörge Ausgabe:

Code: Alles auswählen

 {'episode': 13,
  'episode_title': u'Im Bett mit Angina',
  'season': 1,
  'station': u'ATV (\xd6sterreich)',
  'time': u'16.55 Uhr',
  'title': u'Two and a Half Men'},
 {'episode': None,
  'episode_title': None,
  'season': None,
  'station': u'SRF 1 (Schweiz)',
  'time': u'16.55 Uhr',
  'title': u'Funnymals'}]
und danach stopt dann das script...
BlackJack

@sveni_lee: Ich meine Unterschiede zwischen den Einträgen die erfasst werden und denen die nicht erfasst werden. Das Programm stoppt *nicht* wegen den fehlenden Einträgen für Episode, Staffel, und Episodentitel.
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

ja, das hatte ich schon verstanden... (glaube ich zumindest)

Aber es werden alle nachfolgenden Einträge nicht mehr ausgelesen....
BlackJack

@sveni_lee: Ja aber *warum* nicht? Worin unterscheiden die sich von denen die ausgelesen werden? Und ich kann das auch nicht nachvollziehen, bei mir wird bis zum letzten <li>-Eintrag alles erfasst.
sveni_lee
User
Beiträge: 92
Registriert: Montag 14. März 2016, 09:50

der Unterschied ist,

'episode': parse_number(node.find('span', 'epg_ep')),
'episode_title': episode_title_node.text if episode_title_node else None,
'season': parse_number(node.find('span', 'epg_st')),

nicht vorhanden sind und mit None ausgegeben werden....

Code: Alles auswählen

<li id="e_121" style="display:none;" style="display:none;"><table><colgroup><col width="65"><col width="12"><col width="475"><col width="70"></colgroup>
<tr><td rowspan="3" class="zeit">16.55 Uhr</td><td colspan="2" class="entry"><a href="/serie/funnymals" class="sendung b2">Funnymals</a><a href="http://www.wunschliste.de/kalender.pl?s=24884&ktermin_start=20160315T165500&ktermin_ende=20160315T173000&kid=201603151655014&sz=0&sender=&titel=" class="episode_kalender"><img src="/gfx/kal.gif" width="10" height="9" border="0" title="Zum Kalender hinzufügen" class="kalsprite"></a></td><td rowspan="2" style="background-color:#ffffff;"><img src="/gfx/logos/14.gif" class="stationslogo" alt="SRF 1 (Schweiz)" title="SRF 1 (Schweiz)"><img src="/gfx/flaggen/flagge_ch.png" alt="Schweiz" title="Schweiz" class="linkflag"></td></tr>
<tr><td rowspan="2"></td><td>FUNNYMALS</a></td></tr>
BlackJack

@sveni_lee: Noch mal: Interessant ist der Unterschied zwischen Daten die erfasst werden und welchen die nicht erfasst werden. Wobei das mit den Episodendaten kein wichtiger Unterschied ist, denn es werden ja ganz offensichtlich Datensätze mit und ohne Episodeninformationen gefunden, das kann also nicht der Unterschied sein der dazu führt das bei Dir nicht alles gefunden wird. Wenn die Episodendaten nicht vorhanden sind, dann wird halt `None` als Wert eingetragen von der Funktion. Das bewirkt in keinster Weise das danach nicht weitergesucht wird. Warum sollte es auch‽
Antworten