Seite 1 von 2

Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 10:46
von sveni_lee
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

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 10:59
von Sirius3
@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.

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 12:29
von sveni_lee
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...

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 12:55
von Dav1d
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.

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 13:02
von pillmuncher
@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.

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 13:07
von sveni_lee
@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...

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 13:42
von sveni_lee
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()		

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 14:07
von cofi
Ich empfehle erstmal ein Python Tutorial durchzuarbeiten, da dein Problem hier schlicht ist, dass dein Verstaendnis von Funktionen und deren Rueckgabewerten falsch ist.

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 14:23
von miracle173
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.

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 14:34
von 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===============')

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 15:34
von sveni_lee
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...

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 20:36
von 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()

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 22:06
von sveni_lee
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,

Re: Anfängerfrage zu re.compile

Verfasst: Montag 14. März 2016, 22:13
von 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.

Re: Anfängerfrage zu re.compile

Verfasst: Dienstag 15. März 2016, 11:35
von sveni_lee
@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...

Re: Anfängerfrage zu re.compile

Verfasst: Dienstag 15. März 2016, 12:00
von 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.

Re: Anfängerfrage zu re.compile

Verfasst: Dienstag 15. März 2016, 12:48
von sveni_lee
ja, das hatte ich schon verstanden... (glaube ich zumindest)

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

Re: Anfängerfrage zu re.compile

Verfasst: Dienstag 15. März 2016, 14:09
von 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.

Re: Anfängerfrage zu re.compile

Verfasst: Dienstag 15. März 2016, 14:54
von sveni_lee
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>

Re: Anfängerfrage zu re.compile

Verfasst: Dienstag 15. März 2016, 15:06
von 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‽