Wörter in html text ersetzten...

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

In PyLucid v0.9 gibt es ein neues Lexikon Plugin. Es ersetzt alle vorhandenen Lexikon Begriffe in der erzeugten html Seite mit einem Link in das Lexikon und einer title information im link...

Das Problem: Ich verwende z.Z. eine sehr einfache funktion um die Wörter zu ersetzten. Ich mache einfach sowas in der Art: content = content.replace(" %s ", lexicon_info).

Das ersetzt aber auch die Wörter, wo ein Link überhaupt nicht erlaubt ist. Ich muß also viel mehr den html code analysieren und nur im eigentliche Text die Wörter ersetzten. Jemand eine Idee wie man das mit relativ wenig Aufwand machen kann?

EDIT1: Eine variante die mit einfallen würde: Das ganze nicht auf dem Server machen, sondern per jQuery die Sachen erledigen. Dort gibt es sicherlich eine Funktion dafür. Aber das wäre dumm, weil es dann nur mit JS gehen würde...

EDIT2: Ob es evtl. mit RE gehen würde? Nach dem Prinzip: ">(.*?)<" (Also nur in dem Bereich suchen)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Normaler Fliess-Text oder ein Absatz sollte doch in HTML immer in <p>-Tags sein?

EDIT: Du hast wohl auch editiert, jetzt verstehe ich, was du meinst... Ich würde halt einfach überprüfen, ob die übergeordneten Tags ein <a> beinhalten dürfen oder nicht...
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ne, So einfach geht das nicht. Eine RE nach dem Motto "<p>(.*?)</p>" würde ja auch Links im Text treffen.

Mal ein Beispiel:
Quellen Text:

Code: Alles auswählen

<p>Bla bla <a href="/en/blog/" title="The PyLucid developer blog" class="level_0">blog</a> foobar</p>
Wenn im Lexikon "PyLucid" vorhanden wäre, würde auch im Link Titel das Wort ersetzt.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Warum parst du das HTML nicht einfach mit lxml oder so?
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Das wäre auch mein Vorschlag gewesen... Dann ersparst du dir komplizierte re's, die vllt. gar nicht immer funktionieren.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ja, aber dann brauche ich noch eine externe lib :(

Könnte man auch ein internes Modul her nehmen? Ich meine ich hab sowas ähnliches mal mit HTMLParser und sgmllib probiert. Aber das klappte nicht so doll...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

jens hat geschrieben:Ja, aber dann brauche ich noch eine externe lib :(

Könnte man auch ein internes Modul her nehmen? Ich meine ich hab sowas ähnliches mal mit HTMLParser und sgmllib probiert. Aber das klappte nicht so doll...
Fällt denn lxml bei der Abhängigkeit "Django" noch so sehr ins Gewicht?
lunar

Außerdem kann man ja auch reine Python-Module wie BeautifulSoup oder html5lib + ElementTree nutzen, die man notfalls mit der eigenen Anwendung mitliefern kann.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich würde mit Regexp 1 Tags von Text trennen und dann in dem Text, getrennt an Leerschritt, Komma, Punkt und ein paar anderen Zeichen, in dem Lexikon suchen, ggf. Links erzeugen und alles wieder zusammensetzen. Sind maximal 5 Zeilen Code. XML-Parser (außer man will auch mit Entities klarkommen, also wenn jemand ein antikes &auml; statt ä benutzen will) nicht nötig.

Stefan
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ja, ich werde wohl erstmal zu einer RE Lösung greifen. Das Lexikon ist ja nur ein nettes Zusatzfeature. Zusätzliche Abhängigkeiten sind IMHO nicht gerechtfertigt, wenn es auch ohne geht...

@sma: Vorschläge wie eine RE Lösung aussehen kann?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wie wäre es dann, das Lexikon-Feature optional anzubieten? Es wird dann halt freigeschaltet, wenn es den gewünschten Parser findet.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Oder du baust dir nen eigenen HTML-Parser! \o/
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

snafu hat geschrieben:Wie wäre es dann, das Lexikon-Feature optional anzubieten? Es wird dann halt freigeschaltet, wenn es den gewünschten Parser findet.
Ja, das wäre eine "Not Lösung". Wäre aber schöne, wenn es immer geht ;)

Hab die öffentliche Test Seite http://www.pylucid.de aktualisiert. Dort kann man sich das mal anschauen.
Im Lexicon ist allerdings bisher nur der Begriff "PyLucid" in englisch und deutsch. Auf der Hauptseite kann man sehen, das jedes "PyLucid" Wort ersetzt wird mit einem Link zu http://www.pylucid.de/?lexicon=PyLucid

In der "Update" liste, auf der Hauptseite oder im http://www.pylucid.de/en/SiteMap/ kann man auch sehen, das die jetzige Implementierung schaden anrichtet ;)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Code: Alles auswählen

import re

def sub_text(html, f): 
    def r(m): 
        s = m.group(1)
        return s and (f(s) or s) or m.group()
    return re.sub(r"(?u)<[^>]*>|(\w+)", r, html) 

def f(w):
    if w == u'weiter':
        return u'<b>WEITER</b>'

print sub_text(u'<html><a href="foo.bar/?a=x">dingens</a>und so weiter</html>', f)
Update: Meine erste Version war zu kompliziert gedacht.

Stefan
Zuletzt geändert von sma am Sonntag 23. August 2009, 09:03, insgesamt 1-mal geändert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das sieht schon mal nach einer Lösung aus, die besser ist als meine ;)
Werde ich nächste Woche mal testen. Danke!

Daneben sollte ich mir noch Gedanken dazu machen, wie man die Datenbank Zugriffe im Zaum hält. Denn bei langen Texten hätten wir sehr viele, wenn man einfach bei jedem Wort prüfen würde, ob es in der DB ist.
Aber man könnte es auch so machen, das man sich erstmal alle Wörter (mit Alias, den gibt es auch) vorab beschafft.
Außerdem gibt es irgendwann wieder den Page cache ;)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

@sma: Mit http://trac.pylucid.net/changeset/2320 habe ich eine Abgewandelte Form deines Beispiels in PyLucid eingebaut.

In meiner Variante kann man Tags angeben, in denen nichts geändert werden darf. Das ist wichtig, denn in <a href="foo">bar</a> darf "bar" nicht ersetzt werden. Denn schließlich füge ich ja links zum Lexicon ein.

Hier ein Beispiel:

Code: Alles auswählen

class LexiconData(dict):
    def __call__(self, word_lower, word):
        return "*%s*" % word.upper()

lexicon_data = LexiconData({"foo": None, "bar": None})

s = SubHtml(lexicon_data, skip_tags=["a"])
s.process('<html><p><a href="Foo=Bar"><strong>Foo</strong> Bar</a>Foo Bar</p></html>')
Raus kommt, das:
'<html><p><a href="Foo=Bar"><strong>Foo</strong> Bar</a>*FOO* *BAR*</p></html>'

Sourcecode von SubHtml ist hier: http://trac.pylucid.net/browser/branche ... ub_html.py

Code: Alles auswählen

import re

SUB_HTML_RE = re.compile(r"(?u)<(\w+)[^>]*>|(\w+)|</(\w+)>")

class SubHtml(object):
    def __init__(self, lexicon_data, skip_tags=[]):
        # forming a dict from the skip_tags list, for faster lookup
        self.skip_tags = dict([(tag.lower(), None) for tag in skip_tags])
        self.lexicon_data = lexicon_data
        self.in_skip_tag = None

    def sub(self, m):
        if self.in_skip_tag: # We are in a skip_tags
            close_tag = m.group(3)
            if close_tag == self.in_skip_tag: # The last skip_tag was closed
                self.in_skip_tag = None
            return m.group()

        tag = m.group(1) # Open html tag
        if tag:
            if tag.lower() in self.skip_tags:
                self.in_skip_tag = tag
            return m.group()

        word = m.group(2) # A word from the text
        if word:
            word_lower = word.lower()
            if word_lower in self.lexicon_data:
                return self.lexicon_data(word_lower, word)

        return m.group()

    def process(self, html):
        return SUB_HTML_RE.sub(self.sub, html)
Noch was zu verbessern/optimieren?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Du baust dir lieber einen Ad hoc HTML-Parser als nen fertiges Modul dafür zu nutzen? Wtf. Aber mal volle Möhre.
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

audax hat geschrieben:Du baust dir lieber einen Ad hoc HTML-Parser als nen fertiges Modul dafür zu nutzen? Wtf. Aber mal volle Möhre.
DAS denk ich mir bei pylucid auch schon länger
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Was soll das denn jetzt wieder heißen?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

jens hat geschrieben:Was soll das denn jetzt wieder heißen?
Ich frage mich nur, wieviele Räder du schon erfunden hast.
Ständig, und diesmal übertreibe ich nicht, baust du Dinge für PyLucid neu, die es schon längst gibt. Du würdest vermutlich auch eher ne Suchmaschine selbst baun bevor du z.B. Xapian einbindest.
Abhängigkeiten mögen zwar manchmal etwas nervig sein, aber _bitte_ nutz doch etwas mehr fertigen und getesteten Code! Wenn du schon Django als Abhängigkeit hast, dann mach Beatiful Soup den Braten echt nicht fett.
Ich würd gern PyLucid gut finden, aber diese Einstellung von dir verdirbt es mir.
Antworten