2013-03-16 16 views
6

Proszę rozważyć następującą sesji Pythona:find() po replaceWith() nie działa (używając BeautifulSoup)

>>> from BeautifulSoup import BeautifulSoup 
>>> s = BeautifulSoup("<p>This <i>is</i> a <i>test</i>.</p>"); myi = s.find("i") 
>>> myi.replaceWith(BeautifulSoup("was")) 
>>> s.find("i") 
>>> s = BeautifulSoup("<p>This <i>is</i> a <i>test</i>.</p>"); myi = s.find("i") 
>>> myi.replaceWith("was") 
>>> s.find("i") 
<i>test</i> 

Uwaga brakującą moc s.find ("I") po wierszu 4!

Jaki jest tego powód? Czy jest w pobliżu praca?

EDIT: Faktycznie, przykład nie wykazują USECASE, czyli:

myi.replaceWith(BeautifulSoup("wa<b>s</b>")) 

Ilekroć włożona część zawiera sobie nietrywialne kod HTML, nie widzę w jaki sposób można zastąpić tę składnię z czymś jeszcze. Wystarczy zamienić specjalne znaki html na encje, zastępując je specjalnymi znacznikami.

+0

Dlaczego musisz zastąpić 'sometag.renderContents()' zamiast po prostu zastąpić 'someTag'? – BrenBarn

+0

Okay, bądźmy bardziej konkretni, dodając inny przykład ... (patrz wyżej, ponownie zredagowałem) – thomas

Odpowiedz

5

Prostsza odpowiedź: po rozmowie z numerem replaceWith regeneruj i czyść s, dzwoniąc pod numer s = BeautifulSoup(s.renderContents()). Następnie ponownie możesz find.

3

Problem polega na tym, że obiekt BeautifulSoup jest uznawany za cały dokument. find iteruje po dokumencie, pytając każdy element o następny element po nim. Ale kiedy dociera do twojego obiektu BeautifulSoup("was"), obiekt ten myśli, że jest to cały dokument, więc mówi, że nie ma po nim nic. Przerywa to wyszukiwanie zbyt wcześnie.

Nie sądzę, że BeautifulSoup ma mieć obiekty BeautifulSoup wewnątrz innych obiektów BeautifulSoup. Rozwiązaniem jest nie rób tego. Dlaczego uważasz, że musisz użyć pierwszego formularza zamiast drugiego, który już działa? Jeśli chcesz zastąpić element jakimś fragmentem HTML, zmień obiekt na Tag, a nie na obiekt BeautifulSoup.

+0

Przyznaję, że mój przykład nie wyjaśnia, dlaczego potrzebuję tej dziwnej konstrukcji, dodałem dalsze wyjaśnienia powyżej. – thomas

+0

Jednak twoje wyjaśnienie jest całkowicie poprawne, dzięki! Byłoby wspaniale, gdyby udało się obejść. (Więc nie złość się na mnie, ponieważ nie zaznaczam twojej odpowiedzi jako rozwiązania.) – thomas

+0

@thomas: Istnieje zgłoszenie błędu dotyczące podobnego problemu [tutaj] (https://bugs.launchpad.net/beautifulsoup/+ bug/1105148). Komentarz mówi, że został rozwiązany, ale nadal wydaje mi się, że został złamany i nie rozumiem tego wyjaśnienia. Możesz skomentować ten błąd i pokazać swój przykład i zobaczyć, co mówią. – BrenBarn

2

Myślę, znalazłem obejście, które rozwiązuje problem dla mnie. Powtarzam cały kod ponownie jako skrypt Pythona aby uzyskać pełny przykład:

from BeautifulSoup import BeautifulSoup 
s = BeautifulSoup("<p>This <i>is</i> a <i>test</i>.</p>") 
myi = s.find("i") 
s2 = BeautifulSoup("wa<b>s</b>") 
myi_id = myi.parent.contents.index(myi) 
for c in reversed(s2.contents): 
    myi.parent.insert(myi_id + 1, c) 
myi.extract() 

Należy pamiętać, że to nie będzie działać bez reversed(). Jeśli go pominiesz, zmienisz nie tylko kolejność elementów. Jeśli naprawdę chcesz, aby zmienić, trzeba będzie napisać następujące:

for c in list(s2.contents): 
    myi.parent.insert(myi_id + 1, c) 

Może ktoś proszę wyjaśnić, dlaczego omijając list() będzie pomijać <b>s</b>? (Proszę odpowiedzieć w komentarzu, ponieważ nie jest to główne pytanie.)

+0

Powód, dla którego potrzebujesz 'list', wynika z tego, co mówi [tutaj] (http: //www.crummy.com/software/BeautifulSoup/bs3/documentation.html # Dodawanie% 20a% 20Brand% 20Nowy% 20Element): element może występować tylko w jednym miejscu w dokumencie. Kiedy wykonasz 'insert', usuwa on pierwszy element z' s2.contents' w celu wstawienia go gdzie indziej. W ten sposób modyfikujesz 's2' podczas iteracji. – BrenBarn

Powiązane problemy