Evolis2k2
Anmeldungsdatum: 5. August 2007
Beiträge: 69
|
N'abend 😢 ich bin hier gerade etwas am verzweifeln: Ich Programmiere gerade mit Django eine Webseite. Der Clou an der Sache ist ein Stichwortverzeichniss, welches in den Artikeln die jeweiligen Stichwörter automatisch durch einen Link zur "Stichwortbeschreibung" ersetzen soll. string = '<a href="/test/foo123.html">foo bild</a>foo fooo <img src="/foo.gif" />' In diesem String soll jetzt zum beispiel nur das eine foo durch einen Link ersetzt werden, alle anderen logischerweise nicht. Ich hänge nun schon mehrere Stunden an diesem Problem, und komme nicht wirklich weiter. Ich hab jetzt erstmal probiert, nur Link-Tags auszuschließen... | string = '<a href="/test/foo123.html">foo</a>foo fooo <img src="/foo.gif"/>'
string_neu = re.sub('(?!<a href[A-Za-z0-9[:punct:]]*)foo(?![A-Za-z0-9[:punct:]]*</a>)', 'bar', string)
|
Ausgabe:
| <a href="/test/bar123.html">bar</a>bar baro <img src="/bar.gif"/>
|
😢 Wär von euch jemand bereit, mich auf den richtigen Weg zu leiten? Diese Regex-Geschichte sind für mich das totale Grauen & die Dokus machen mich nicht wirklich schlauer... Danke schonmal, Evo
|
Hello_World
Anmeldungsdatum: 13. Juni 2006
Beiträge: 3620
|
Versuche nicht, XML mit regulären Ausdrücken zu parsen, das ist kompliziert und fehleranfällig. Es gibt mit XPath eine einfache Möglichkeit, Knoten in einem XPath-Dokument auszuwählen. Beispielsweise kannst Du mit dem XPath-Ausdruck //text()[not(ancestor::a)] alle Textknoten im Dokument auswählen, die nicht in einem a-Element enthalten sind. Darin ersetzt Du dann einfach die jeweiligen Stichworte durch Links und fertig.
|
Evolis2k2
(Themenstarter)
Anmeldungsdatum: 5. August 2007
Beiträge: 69
|
danke für den Tipp, bin schon etwas weiter damit gekommen
|
Hello_World
Anmeldungsdatum: 13. Juni 2006
Beiträge: 3620
|
Mit etree.parse und der StringIO-Klasse kannst Du einen String als XML parsen. Was ich mit meinen eher bescheidenen Python-Kenntnissen aber noch nicht herausgefunden habe, ist, wie man mit Hilfe der Liste der Textknoten, die der XPath-Ausdruck beschreibt, das Originaldokument verändern kann. Aber hier laufen ein paar Leute herum, die die eine oder andere Sache von Python verstehen, daher denke ich, dass sich das schon klären wird.
|
Evolis2k2
(Themenstarter)
Anmeldungsdatum: 5. August 2007
Beiträge: 69
|
genau das ist nämlich jetzt mein problem... ich hab ne schöne liste mit den zu ändernden objekten, aber kann sie nicht einfach wieder im string richtig positionieren.
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4658
Wohnort: Berlin
|
@Hello World: Mit etree.fromstring() kann man sich StringIO sparen. @Evolis2k2: Element-Exemplare haben die Attribute text und tail. text enthält den Text innerhalb des Tags bis zum nächsten Tag, falls da eines verschachtelt enthalten ist, und tail enthält den Text nach einem Tag. Die originale ElementTree-API ist für Dein Vorhaben aber wohl nicht so gut geeignet, weil man für den tail-Text eventuell neue Knoten in das Elternelement einfügen muss und an das kommt man mit der originalen API nicht heran. Das müsste mit lxml.etree gehen.
|
snafu1
Anmeldungsdatum: 5. September 2007
Beiträge: 2128
Wohnort: Gelsenkirchen
|
Mit einem anderen Parser - nämlich BeautifulSoup - würde ich es so lösen: | >>> from BeautifulSoup import BeautifulSoup
>>> string = '<a href="/test/foo123.html">foo</a>foo fooo <img src="/foo.gif"/>'
>>> soup = BeautifulSoup(string)
>>> print soup(text=True)
[u'foo', u'foo fooo ']
>>> link = '<a href="/murks/bla.html">foo</a> fooo '
>>> soup(text=True)[1].replaceWith(link)
>>> print soup
<a href="/test/foo123.html">foo</a><a href="/murks/bla.html">foo</a> fooo <img src="/foo.gif" />
|
Sicherlich nicht optimal. Aber ich wüsste nicht, dass BeautifulSoup das splitten eines Strings unterstützt. //edit: Man könnte sich aber natürlich ne Funktion wie diese schreiben: | def replace_splitted(soup, n, repl):
'''replace_splitted(soup, n, repl)
Split the soup using space chars as seperator, replace the nth element and
return the new soup.
'''
split = soup.split(' ')
split[n] = repl
return soup.replaceWith(' '.join(split))
|
Anwendung: | >>> import souptools
>>> from BeautifulSoup import BeautifulSoup
>>> string = '<a href="/test/foo123.html">foo</a>foo fooo <img src="/foo.gif"/>'
>>> soup = BeautifulSoup(string)
>>> link = '<a href="/murks/bla.html">foo</a>'
>>> souptools.replace_splitted(soup(text=True)[1], 0, link)
>>> soup
<a href="/test/foo123.html">foo</a><a href="/murks/bla.html">foo</a> fooo <img src="/foo.gif" />
|
|
Evolis2k2
(Themenstarter)
Anmeldungsdatum: 5. August 2007
Beiträge: 69
|
Ich habe jetzt etwas mit lxml.html, lxml.etree und beautifulsoup rumprobiert... mit beautifulsoup bin ich bisher am nächsten herrangekommen (auch wenn's wirklich dreckig von mir programmiert ist) Das ist bisher dabei rausgekommen: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | tagliste = ['link1', 'link2']
from BeautifulSoup import BeautifulSoup
string = '<p><a href="link1.html">link1</a><a href=/bar/bar.html>bar</a>link2 <a href=/bar/bar.html>bar</a></p>'
soup = BeautifulSoup(string)
for tag in tagliste:
soup = BeautifulSoup(string)
if find(string, tag) > -1:
i = 0
link_soup = soup.findAll('a')
for item in soup.findAll('a'):
soup.find('a').replaceWith('[[[LINK]]]')
i = i+1
i_2 = 0
for item in soup(text=True):
text = replace(item, tag, '<a href=/bar/bar.html>bar</a>')
soup(text=True)[i_2].replaceWith(text)
i_2 = i_2+1
#print link_soup
i_3 = 0
soup = str(soup)
for item in link_soup:
soup = replace(soup, '[[[LINK]]]', str(item), 1)
i_3 = i_3+1
string = soup
else :
print 'nicht gefunden'
print str(soup)
|
Soweit läuft es erstmal... danke für die ganzen Hinweise ☺ (Verbessungsvorschläge werden dankend angenommen) 😬
|
snafu1
Anmeldungsdatum: 5. September 2007
Beiträge: 2128
Wohnort: Gelsenkirchen
|
Dein Code funkioniert bei mir nicht: | >>> import testding
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
import testding
File "C:\Python25\testding.py", line 8, in <module>
if find(string, tag) > -1:
NameError: name 'find' is not defined
|
Ich finde ihn ohne etwas komisch. Was genau willst du denn erreichen? Sag doch mal wie dein String am Ende aussehen soll.
|
Evolis2k2
(Themenstarter)
Anmeldungsdatum: 5. August 2007
Beiträge: 69
|
hier der komplette Code mit import... 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | from string import find, replace
tagliste = ['link1', 'foo']
from BeautifulSoup import BeautifulSoup
string = '<p><a href="link1.html">link1</a><a href=/bar/bar.html>bar</a>foo <a href=/bar/foo.html>bar</a></p>'
soup = BeautifulSoup(string)
for tag in tagliste:
soup = BeautifulSoup(string)
if find(string, tag) > -1:
i = 0
link_soup = soup.findAll('a')
for item in soup.findAll('a'):
soup.find('a').replaceWith('[[[LINK]]]')
i = i+1
i_2 = 0
for item in soup(text=True):
text = replace(item, tag, '<a href=/bar/bar.html>bar</a>')
soup(text=True)[i_2].replaceWith(text)
i_2 = i_2+1
#print link_soup
i_3 = 0
soup = str(soup)
for item in link_soup:
soup = replace(soup, '[[[LINK]]]', str(item), 1)
i_3 = i_3+1
string = soup
print str(soup)
|
Hiermit erreiche ich, dass alle Einträge der Tagliste im String durch einen Link '<a href=/bar/bar.html>bar</a>' ersetzt werden, wenn sie nicht in einem <a>-Tag oder <img>-Tag liegen. Dadurch wird aus:
<p><a href="link1.html">link1</a><a href=/bar/bar.html>bar</a>foo <a href=/bar/foo.html>bar</a></p>
Ein:
<p><a href="link1.html">link1</a><a href="/bar/bar.html">bar</a><a href=/bar/bar.html>bar</a> <a href="/bar/foo.html">bar</a></p>
|
DasIch
Anmeldungsdatum: 2. November 2005
Beiträge: 1130
|
Normalerweise benutzt man nicht string.find oder string.replace sondern str.find bzw. str.replace. In Zeile 10 wäre das z.B. | if string.find(tag) > -1:
|
|
Evolis2k2
(Themenstarter)
Anmeldungsdatum: 5. August 2007
Beiträge: 69
|
danke, ich habs umgeändert ☺
|
snafu1
Anmeldungsdatum: 5. September 2007
Beiträge: 2128
Wohnort: Gelsenkirchen
|
Evolis2k2 schrieb: Hiermit erreiche ich, dass alle Einträge der Tagliste im String durch einen Link '<a href=/bar/bar.html>bar</a>' ersetzt werden, wenn sie nicht in einem <a>-Tag oder <img>-Tag liegen. Dadurch wird aus:
<p><a href="link1.html">link1</a><a href=/bar/bar.html>bar</a>foo <a href=/bar/foo.html>bar</a></p>
Ein:
<p><a href="link1.html">link1</a><a href="/bar/bar.html">bar</a><a href=/bar/bar.html>bar</a> <a href="/bar/foo.html">bar</a></p>
So auch 😉 : 1
2
3
4
5
6
7
8
9
10
11
12 | def set_anchor(head, taglist):
for elem in head.contents:
if not str(elem).startswith(('<a', '<img')):
split = str(elem).split(' ')
i = 0
for word in split:
for entry in taglist:
if word == entry:
split[i] = '<a href=/bar/bar.html>bar</a>'
i += 1
elem.replaceWith(' '.join(split))
return head
|
| >>> from BeautifulSoup import BeautifulSoup
>>> import testding
>>> tagliste = ['link1', 'foo']
>>> tagliste = ['link1', 'foo', 'katze']
>>> string = '<p><a href="link1.html">link1</a><a href=/bar/bar.html>bar</a>foo <a href=/bar/foo.html>bar</a>die katze faucht</p>'
>>> soup = BeautifulSoup(string)
>>> head = soup.p
>>> testding.set_anchor(head, tagliste)
<p><a href="link1.html">link1</a><a href="/bar/bar.html">bar</a><a href=/bar/bar.html>bar</a> <a href="/bar/foo.html">bar</a>die <a href=/bar/bar.html>bar</a> faucht</p>
|
Es müsste nur noch implementiert werden, wenn auf dem Wort ein Satzzeichen folgt. Denn dann ist es ja nicht mehr identisch mit dem Eintrag in deiner Liste.
|
Evolis2k2
(Themenstarter)
Anmeldungsdatum: 5. August 2007
Beiträge: 69
|
Danke, ich werds mal mit deinem Ansatz ausprobieren ☺
|
snafu1
Anmeldungsdatum: 5. September 2007
Beiträge: 2128
Wohnort: Gelsenkirchen
|
Habe das ganze noch ein wenig erweitert: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 | import string
def set_anchors(soup, termlist):
'''set_anchors(soup, termlist)
Inspect all elements of a BeautifulSoup instance whether they are not
included by an a- or img-tag. In that case compare each word of the element
with the entries in termlist. Change each matching word to an anchor where
URL is /description/%s.html (%s = word).
'''
for elem in soup.contents:
if not str(elem).startswith(('<a', '<img')):
elem.replaceWith(_anchors(elem, termlist))
return soup
def _anchors(elem, termlist):
split = str(elem).split(' ')
i = 0
for word in split:
adj_word = _adjust(word)
for entry in termlist:
if adj_word == entry.lower():
split[i] = '<a href="/description/%s.html">%s</a>' % (adj_word,
word)
i += 1
return ' '.join(split)
def _adjust(word):
chars = [char for char in word.lower()]
for char in chars:
if char in string.punctuation:
chars.remove(char)
return ''.join(chars)
|
Beispiel, an dem hoffentlich die neue Funktionsweise deutlich wird: | >>> import bla
>>> from BeautifulSoup import BeautifulSoup
>>> string = '<p><a href="link1.html">link1</a><a href=/bar/bar.html>bar</a>foo <a href=/bar/foo.html>bar</a>die Katze, die im Garten ist, faucht</p>'
>>> soup = BeautifulSoup(string)
>>> termlist = ['foo', 'bar', 'Katze', 'garten']
>>> bla.set_anchors(soup, termlist)
<p><a href="link1.html">link1</a><a href="/bar/bar.html">bar</a><a href="/description/foo.html">foo</a> <a href="/bar/foo.html">bar</a>die <a href="/description/katze.html">Katze,</a> die im <a href="/description/garten.html">Garten</a> ist, faucht</p>
|
Jetzt fehlt eigentlich nur noch, dass das Komma hinter Katze nicht von Anker eingeschlossen wird. Entspricht dies in etwa deinen Vorstellungen (um mal weg von den ganzen foobars zu kommen)?
|