2013-03-16 7 views
6

perche la seguente sessione di pitone:find() dopo ReplaceWith() non funziona (usando 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> 

Si prega di notare l'uscita mancante di s.find ("i"), dopo la linea 4!

Qual è il motivo? C'è una soluzione?

EDIT: In realtà, l'esempio non dimostra il caso d'uso, che è:

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

Ogni volta che la parte inserita in sé contiene il codice html non banale, non vedo come si potrebbe sostituire questa sintassi con qualcosa altro. Il solo

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

sostituirà i caratteri speciali html per entità.

+0

Perché è necessario sostituire con 'sometag.renderContents()' invece di sostituire con 'someTag'? – BrenBarn

+0

Ok, siamo più concreti aggiungendo un altro esempio ... (vedi sopra, ho modificato di nuovo) – thomas

risposta

5

Più semplice: dopo la vostra chiamata a replaceWith, rigenerare e pulita s chiamando s = BeautifulSoup(s.renderContents()). Quindi puoi nuovamente find.

3

Il problema sembra essere che un oggetto BeautifulSoup è considerato un intero documento. find itera attraverso il documento chiedendo ad ogni elemento il successivo elemento dopo di esso. Ma quando arriva al tuo BeautifulSoup("was"), quell'oggetto pensa che sia l'intero documento, quindi dice che non c'è niente dopo di esso. Questo interrompe la ricerca troppo presto.

Non penso che BeautifulSoup sia progettato per contenere oggetti BeautifulSoup all'interno di altri oggetti BeautifulSoup. La soluzione alternativa è non farlo. Perché senti di dover utilizzare il primo modulo invece del secondo, che funziona già? Se si desidera sostituire un elemento con un po 'di HTML, utilizzare uno Tag per la sostituzione, non un oggetto BeautifulSoup.

+0

Ammetto che il mio esempio non chiarisce, perché ho bisogno di questa strana costruzione, ho aggiunto ulteriori spiegazioni sopra. – thomas

+0

Tuttavia, la tua spiegazione è completamente corretta, grazie! Sarebbe bello avere una soluzione alternativa però. (Quindi non arrabbiarti con me perché non segna la tua risposta come soluzione.) – thomas

+0

@thomas: c'è una segnalazione di bug su un problema simile [qui] (https://bugs.launchpad.net/beautifulsoup/+ bug/1.105.148). Un commento dice che è stato risolto ma mi sembra ancora rotto e non riesco a capire la spiegazione. Potresti voler commentare quel bug e mostrare il tuo esempio e vedere cosa dicono. – BrenBarn

2

Penso, ho trovato una soluzione alternativa, che risolve il problema per me. Ripeto l'intero codice di nuovo come uno script Python per dare un esempio completo:

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() 

Si prega di notare, che questo non funzionerà senza reversed(). Se lo salti, non cambi solo l'ordine degli elementi. Se si vuole veramente la fine di essere cambiato, si dovrà scrivere il seguente:

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

Qualcuno può spiegare, perché saltare list() ometterà <b>s</b>? (Si prega di rispondere in un commento, perché questo non è il problema principale qui.) Risposta

+0

Il motivo per cui è necessario 'list' è a causa di ciò che dice [qui] (http: //www.crummy.com/software/BeautifulSoup/bs3/documentation.html # Aggiunta di% 20a% 20Brand% 20New% 20Element): un elemento può verificarsi in un solo punto del documento. Quando fai il comando 'insert', rimuove il primo elemento da' s2.contents' per inserirlo altrove. In questo modo stai modificando 's2' mentre esegui iterazioni su di esso. – BrenBarn