OVH Cloud OVH Cloud

SAX et python

6 réponses
Avatar
remi
Bonjour,

J'arrive à bien (?) faire fonctioner l'API SAX dès qu'il s'agit de
traiter des attributs d'un élement mais j'ai du mal lorsqu'il s'agit
d'acceder au texte même.
Par exemple :
<section id="1", titre="titre">
<texte>
...texte que j'ai du mal à atteindre...
</texte>
</section>
Comment faire ?
Merci.
Rémi (newbie)

6 réponses

Avatar
Bruno Desthuilliers
Bonjour,

J'arrive à bien (?) faire fonctioner l'API SAX dès qu'il s'agit de
traiter des attributs d'un élement mais j'ai du mal lorsqu'il s'agit
d'acceder au texte même.
Par exemple :
<section id="1", titre="titre">
<texte>
...texte que j'ai du mal à atteindre...
</texte>
</section>
Comment faire ?


xml.sax.contentHandler.characters(content)
"""
Receive notification of character data.

The Parser will call this method to report each chunk of character data.
(...)
"""

Pour savoir si tu es dans l'élément 'cible' (section 1->texte dans ton
exemple), il suffit d'empiler les éléments traversés (dans la méthode
startElement()) et de les dépiler (dans la méthode endElement). Le
sommet de ta pile est alors toujours l'élément en cours. Très
grossièrement, et de mémoire - ça fait quelques siècles que je n'ai pas
bouffé du SAX, et la dernière fois c'était en PHP - ça donne quelque
chose come ça:

class myContentHandler(xml.sax.contentHandler):

def startDocument(self, ...):
(...)
self._stack = []
self._values = {}

def startElement(self, name, attrs):
self._stack.push( (name, attrs) )

def endElement(self, name):
self._stack.pop()

def characters(self, content):
name, attrs = self._stack[-1]
if name == 'section':
self._values["%s_%s" % (name, attrs.getValue('id'))] = content
...


Je te laisse consulter la doc, ainsi que ceci:
http://pyxml.sourceforge.net/topics/howto/section-SAX.html

HTH

Bruno

Avatar
remi

Bonjour,

J'arrive à bien (?) faire fonctioner l'API SAX dès qu'il s'agit de
traiter des attributs d'un élement mais j'ai du mal lorsqu'il s'agit
d'acceder au texte même.
Par exemple :
<section id="1", titre="titre">
<texte>
...texte que j'ai du mal à atteindre...
</texte>
</section>
Comment faire ?



xml.sax.contentHandler.characters(content)
"""
Receive notification of character data.

The Parser will call this method to report each chunk of character data.
(...)
"""

Pour savoir si tu es dans l'élément 'cible' (section 1->texte dans ton
exemple), il suffit d'empiler les éléments traversés (dans la méthode
startElement()) et de les dépiler (dans la méthode endElement). Le
sommet de ta pile est alors toujours l'élément en cours. Très
grossièrement, et de mémoire - ça fait quelques siècles que je n'ai pas
bouffé du SAX, et la dernière fois c'était en PHP - ça donne quelque
chose come ça:

class myContentHandler(xml.sax.contentHandler):

def startDocument(self, ...):
(...)
self._stack = []
self._values = {}

def startElement(self, name, attrs):
self._stack.push( (name, attrs) )

def endElement(self, name):
self._stack.pop()

def characters(self, content):
name, attrs = self._stack[-1]
if name == 'section':
self._values["%s_%s" % (name, attrs.getValue('id'))] = content
...


Je te laisse consulter la doc, ainsi que ceci:
http://pyxml.sourceforge.net/topics/howto/section-SAX.html


Merci beaucoup mais j'avoue ne pas y arriver :-(

Du coup, j'ai mis tout le contenu en attribut de balises.
Cela semble être plus facile à traiter avec SAX. A croire que DOM est
plus adapté pour aller chercher les PCDATA comme je souhaite le faire ?

Finalement, j'ai généré un XML avec des balises du type :

<sousSection id="1" rubrik="1" thema="subsection 1.1" content="Texte de
la soussection 1.1...">

je traite le tout avec, par exemple, dans mon dochandler :

def startElement(self, name, attrs):
if name == 'section':
monfichierIO.write("INSERT INTO ma_base
(id,lang,parent_id,name,description) VALUES
('%s','fr','0','%s','%s');n" % (str(attrs['id']), str(attrs['name']),
str(attrs['description'])))

Je ne sais pas si c'est très pythonique, ni très xmlien ? ;-)
Quoiqu'il en soit, je suis toujours preneur de conseils divers (avant
d'aller au soleil pour une semaine loin de tout ordinateur ;-))
Merci.
Rémi.


Avatar
bruno modulix
remi wrote:

(snip)


Merci beaucoup mais j'avoue ne pas y arriver :-(


C'est quoi le symptôme ?

Du coup, j'ai mis tout le contenu en attribut de balises.
Cela semble être plus facile à traiter avec SAX. A croire que DOM est
plus adapté pour aller chercher les PCDATA comme je souhaite le faire ?


Non, pas forcément. Le tout est de bien comprendre comment marche un
parseur SAX.

Finalement, j'ai généré un XML avec des balises du type :

<sousSection id="1" rubrik="1" thema="subsection 1.1" content="Texte de
la soussection 1.1...">


Beurk. (Désolé, mais c'est beurk. Un attribut XML n'est pas fait pour
contenir ce genre d'infos).

je traite le tout avec, par exemple, dans mon dochandler :
(snip)

Quoiqu'il en soit, je suis toujours preneur de conseils divers


Bin, là, mon conseil, ce serait de refaire proprement le schema XML et
d'utiliser la methode character() pour traiter le contenu des balises.
Envoie ce que tu a fait, à partir de là on pourra t'aider.

(avant
d'aller au soleil pour une semaine loin de tout ordinateur ;-))


<jaloux>
Tu va t'ennuyer...
</jaloux>


--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"

Avatar
remi
Bonjour,

remi wrote:


(snip)



Merci beaucoup mais j'avoue ne pas y arriver :-(



C'est quoi le symptôme ?


J'ai essayé avec ce code :
def characters(self, texte):
print texte
print type(texte)

Cela me renvoie bien, le texte mais tout ce qui est compris entre les
balises <texte> et </texte> et en conservant, semble-t-il, les sauts de
lignes. J'obtiens en fait :

<type 'unicode'>
Mini FAQ
<type 'unicode'>


<type 'unicode'>


<type 'unicode'>


<type 'unicode'>
Texte de la soussection 1.1
<type 'unicode'>
....

alors que je souhaiterais récupérer le texte "Texte de la soussection
1.1", le traiter et passer au texte suivant. C'est l'utilisation d'un
flag, pour justement faire ça, que je n'ai pas bien compris je pense...

Du coup, j'ai mis tout le contenu en attribut de balises.
Cela semble être plus facile à traiter avec SAX. A croire que DOM est
plus adapté pour aller chercher les PCDATA comme je souhaite le faire ?


Non, pas forcément. Le tout est de bien comprendre comment marche un
parseur SAX.


Ok, je bloque alors sur characters()


Beurk. (Désolé, mais c'est beurk. Un attribut XML n'est pas fait pour
contenir ce genre d'infos).


D'accord, en plus cela me génère des mal-formed avec le "vrai" document...

Bin, là, mon conseil, ce serait de refaire proprement le schema XML et
d'utiliser la methode character() pour traiter le contenu des balises.
Envoie ce que tu a fait, à partir de là on pourra t'aider.

(avant d'aller au soleil pour une semaine loin de tout ordinateur ;-))



<jaloux>
Tu va t'ennuyer...
</jaloux>


Mais non. Oula, il se fait tard, je dois y aller ! ;-)
@+ et merci beaucoup
Rémi.


Avatar
bruno modulix
remi wrote:
Bonjour,


remi wrote:


(snip)



Merci beaucoup mais j'avoue ne pas y arriver :-(




C'est quoi le symptôme ?



J'ai essayé avec ce code :
def characters(self, texte):
print texte
print type(texte)

Cela me renvoie bien, le texte mais tout ce qui est compris entre les
balises <texte> et </texte> et en conservant, semble-t-il, les sauts de
lignes. J'obtiens en fait :

<type 'unicode'>
Mini FAQ
<type 'unicode'>


<type 'unicode'>


<type 'unicode'>


<type 'unicode'>
Texte de la soussection 1.1
<type 'unicode'>
....

alors que je souhaiterais récupérer le texte "Texte de la soussection
1.1", le traiter et passer au texte suivant. C'est l'utilisation d'un
flag, pour justement faire ça, que je n'ai pas bien compris je pense...



Tu n'a pas regardé le code que je t'avais posté, alors.
NB : pour la suite, je me base sur ce schema:

<section id="1", titre="titre">
<texte>
...texte que j'ai du mal à atteindre...
</texte>
</section>

* Le principe *
les évènements arrivent dans cet ordre:
<debutDoc>
[tantqu'il y a des balises:
<startElement>
[si il y du CDATA:
<character>
]
<endElement>
]
<finDoc>

Donc, en utilisant startElement() et endElement(), tu peux savoir dans
quelle balise tu es au moment de l'appel à character(). Il suffit de
garder trace de l'info. Au plus simple (si il n'y a qu'un cas qui
t'intéresse), un simple flag suffit:

class StupidContentHandler(xml.sax.contentHandler):
def startDocument(self, ...)
self._in_text = False

def startElement(self, name, attr):
self._in_text = (name == 'texte')

def character(self, text):
if self._in_text:
print "texte : ", text

def endElement(self, name):
if name == 'texte':
self._in_text = False

Le problème commence si tu peut avoir des balises 'texte' ailleurs. Dans
ce cas, il faut garder trace de toutes les balises précédemment ouvertes
et non encore fermées - je te renvoie à mon poste précédent (à adapter,
bien sûr, à tes besoins).

(snip)
Ok, je bloque alors sur characters()


Quand characters() est appelé, tu n'a pas le contexte. C'est donc à toi
de maintenir ce contexte, via les évenements startElement et endElement.

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"



Avatar
remi
Bonjour,

remi wrote:


(big snip)

Le problème commence si tu peut avoir des balises 'texte' ailleurs. Dans
ce cas, il faut garder trace de toutes les balises précédemment ouvertes
et non encore fermées - je te renvoie à mon poste précédent (à adapter,
bien sûr, à tes besoins).


Oui celui avec :
self._stack = []
self._values = {}

Merci beaucoup pour toutes ces infos et explications !


(snip)

Ok, je bloque alors sur characters()



Quand characters() est appelé, tu n'a pas le contexte. C'est donc à toi
de maintenir ce contexte, via les évenements startElement et endElement.


D'où le role du Flag ?
@+
Rémi.