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:

@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.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Jetzt weiß ich was du meinst.

Ich nutzte immer mehr externe Module.
Auf http://pylucid.org/_goto/139/package-in ... ject-infos ist eine Liste für v0.8... In v0.9 sind noch einige Dinge dazu gekommen, siehe: http://pylucid.org/_goto/184/v0-9-package-info/

Wenn externe Libs das leben einfacher machen, bin ich nicht abgeneigt!

Mag sein, das für diese "Wörter ersetzten" Problem, BeautifulSoup oder html5lib + ElementTree besser geeignet sind, aber zum einen habe ich mit den Libs noch nie gearbeitet und zum anderen hab ich nun eine einfache Lösung, die erstmal funktioniert. Es gibt wichtigere Dinge, die noch in v0.9 erledigt werden müßen.

Wenn jemand Code beisteuert der besser ist und/oder mit extere Libs arbeitet, immer her damit!

btw. ich hab eine eigene Suche implementiert: http://pylucid.de/?search=test
Aber der größte Spaß macht es halt auch plugins zu schreiben. Also ein eigenes Blog/Suche zu implementieren...

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

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/jedie/PyLucid09/PyLucid_env/src/pylucid/pylucid_project/pylucid_plugins/search/views.py", line 207, in call_searchs
    plugin_instance.call_plugin_view(self.request, filename, view_name, method_kwargs)
  File "/home/jedie/PyLucid09/PyLucid_env/src/pylucid/pylucid_project/system/pylucid_plugins.py", line 87, in call_plugin_view
    response = callable(request, **method_kwargs)
  File "/home/jedie/PyLucid09/PyLucid_env/src/pylucid/pylucid_project/pylucid_plugins/blog/search.py", line 26, in get_search_results
    for item in queryset:
  File "/home/jedie/PyLucid09/PyLucid_env/src/django/django/db/models/query.py", line 106, in _result_iter
    self._fill_cache()
  File "/home/jedie/PyLucid09/PyLucid_env/src/django/django/db/models/query.py", line 692, in _fill_cache
    self._result_cache.append(self._iter.next())
  File "/home/jedie/PyLucid09/PyLucid_env/src/django/django/db/models/query.py", line 238, in iterator
    for row in self.query.results_iter():
  File "/home/jedie/PyLucid09/PyLucid_env/src/django/django/db/models/sql/query.py", line 287, in results_iter
    for rows in self.execute_sql(MULTI):
  File "/home/jedie/PyLucid09/PyLucid_env/src/django/django/db/models/sql/query.py", line 2369, in execute_sql
    cursor.execute(sql, params)
  File "/home/jedie/PyLucid09/PyLucid_env/src/django/django/db/backends/util.py", line 19, in execute
    return self.cursor.execute(sql, params)
  File "/home/jedie/PyLucid09/PyLucid_env/src/django/django/db/backends/sqlite3/base.py", line 193, in execute
    return Database.Cursor.execute(self, query, params)
OperationalError: no such table: blog_blogentry_sites
Das zur Suche ;)

Na gut, wenn ich die Tage mal Zeit hab, und meine Erkältung deutet darauf hin, werd ich mal ne Alternative schreiben.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

audax hat geschrieben:

Code: Alles auswählen

OperationalError: no such table: blog_blogentry_sites
Das zur Suche ;)
Ja, ich weiß. Ist halt noch Alpha, die Tabellen haben sich zwischenzeitlich geändert, aber nicht auf der Test Seite ;)

Wäre schön, wenn du Code einreichen könntest...


Edit (jens): Abgetrennt: PyLucid Kritik...

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:

Würde gern nochmal das Thema ansprechen.

Denn mit der aktuellen Implementierung gibt es ein Problem: Es werden nur immer ein Wort gefunden. Aber das ist im Nachhinein doof, denn Lexikon Einträge können durchaus auch aus zwei Wörtern bestehen.

Welche html lib ist denn nun die beste vor sowas?

genannt wurden: lxml, BeautifulSoup, html5lib + ElementTree

Ich werde mir erstmal lxml ansehen...
EDIT: Ach, nee, lxml ist ja kein pure Python. Das dürfte für manche schwer zu installieren sein :(
Dann versuche ich es doch erstmal mit HTMLParser, der auch gar nicht mal so schlecht von der Performance sein soll, siehe: http://blog.ianbicking.org/2008/03/30/p ... rformance/

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:

Hab jetzt sowas gebastelt:

Code: Alles auswählen

class NoneHTMLParser(object, HTMLParser.HTMLParser):
    def __init__(self):
        # Note: HTMLPaser is a oldstyle class!
        self.reset() # Initialize and reset this HTMLParser instance.

        self.html = ""

    def _add_attrs(self, attrs):
        if attrs:
            self.html += " " + " ".join(['%s="%s"' % (attr, escape(value)) for attr, value in attrs])

    def handle_startendtag(self, tag, attrs):
        self.html += "<" + tag
        self._add_attrs(attrs)
        self.html += " />"

    def handle_starttag(self, tag, attrs):
        self.html += "<" + tag
        self._add_attrs(attrs)
        self.html += ">"

    def handle_endtag(self, tag):
        self.html += "</%s>" % tag

    def handle_charref(self, name):
        self.html += "&#%s;" % name

    def handle_entityref(self, name):
        self.html += "&%s;" % name

    def handle_data(self, data):
        self.html += data

    def handle_comment(self, data):
        self.html += "<!-- %s -->" % data

    def handle_decl(self, decl):
        self.html += "<!%s>" % decl

    def handle_pi(self, data):
        print "handle processing instruction:", data

    def unknown_decl(self, data):
        self.error("unknown declaration: %r" % (data,))
Macht eingentlich nix, außer einen HTML Code durch zu parsen und alles in self.html wieder zusammen zu setzten.
Man könnte nun also von dieser Klasse erben und in handle_data irgendwas machen, bsp:

Code: Alles auswählen

class SubHtml(NoneHTMLParser):
    def handle_data(self, data):
        data = data.replace(..., ...) # XXX
        self.html += data
Wie das eigentliche ersetzten in handle_data aussehen kann, überlege ich hier: http://www.python-forum.de/topic-22102.html

Aber ich weiß nicht ob NoneHTMLParser so das richtige ist...

Weiß auch nicht recht, was ich bei handle_pi und unknown_decl machen sollte.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Zu meinen PHP-Zeiten habe ich folgende Regex-Variante gerne eingesetzt (Achtung - PHP-Code):

Wie ersetze ich in einem Text, jedoch nicht innerhalb von HTML-Tags?
http://www.php-faq.de/q-regexp-ersetzen.html

Vielleicht kann das ja auch noch ein Ansatzpunkt sein.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, aktueller Stand mit HTMLParser: http://trac.pylucid.net/changeset/2559

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten