Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Sanitize HTML, comment retirer des attributs via une wildcard ?

+2 votes

Dans un script je voudrai pouvoir shooter des attributs de noeuds HTML commençant par "on" ou "data" .
Actuellement j'ai ceci

Mais c'est très nettement insuffisant à mon goût parce qu'avec tous les framework css/js bootstrap et consors qui nous plombent le HTML à coup de data-xxx ou onMachin ca devient pénible de rajouter un par un ces pseudo attributs moisis dans ce module sanitize.

Comme je sais bien qu'on peut pas faire des

element.removeAttribute(foobar) 

foobar contient une wildcard, je sèche pour identifier la tartine d'attributs on* et data* et les retirer un par un du dom.

demandé 24-Jul-2015 par foxmask (2,652 points)

1 Réponse

+3 votes
 
Meilleure réponse

Si je me refere à la doc, un Element hérite de Node qui dispose d'une propriété attributes.

Du coup je changerais les lignes 56-59 de ton script par quelque chose comme :

prohibited_attributes = ["id", "class", "accesskey", "data", 
                         "dynsrc", "tabindex", "frame", "rules", 
                         "width"] + [att for att in element.attributes.keys() if att.lower().startswith("on") or att.lower().startswith("data-")]

J'ai pas testé mais tu comprendra l'idée : prendre les fixes plus tous les attributs réels qui respectent le pattern.

Je pense que tu peux d'ailleurs éviter le try-catch qui suit grâce à un truc comme :

filter_term = lambda att : att.startswith("on") or att.startswith("data-") or att in ["id", "class", "accesskey", "data",  "dynsrc", "tabindex", "frame", "rules", "width"]

to_be_removed_atts =  [att for att in element.attributes.keys() if  filter_term(att.lower()]
répondu 24-Jul-2015 par Kje (464 points)
sélectionné 25-Jul-2015 par foxmask

J'essaie des que possible. Merci

le premier morceau marche parfaitement.
par contre je n'ai pas saisi ce que tu aurais fait du 2nd et quel try-catch cela remplacerait parce que dans le try-catch suivant on retire les attributs non désirés. Mais tes 2 lignes m'ont l'air d'être la meme chose que le 1° morceau de code non ?

Dans le deuxième morceaux de code il y a une différence subtile : tu ne garde que les attributs qui existent réellement. Du coup tu peux faire les removeAttribute sans craindre qu'un NotFoundErr soit déclenché puisque tu ne tente de supprimer que des attributs qui existent réellement.

j'ai dû faire le test pour voir la différence ; c'est bien pratique !
Par contre question subsidiaire : laquelle des 2 méthodes serait la moins gourmande ?

je vais tenter line_profiler pour voir

la premiere solution donne :

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    51                                           @profile
    52                                           def remove_prohibited_attributes(element):
    53                                               """
    54                                                   To fit the Evernote DTD need, drop this attribute name
    55                                               """
    56         4            7      1.8      0.5      prohibited_attributes = ["id", "class", "accesskey", "data",
    57         4            8      2.0      0.5                               "dynsrc", "tabindex", "frame", "rules",
    58         4            8      2.0      0.5                               "width"]
    59                                               # All on* and data-* attributes are prohibited.
    60         4           56     14.0      3.7      prohibited_attributes += [att for att in element.attributes.keys()
    61                                                                         if att.lower().startswith("on") or
    62                                                                         att.lower().startswith("data-")]
    63                                           
    64         4          129     32.2      8.5      print(prohibited_attributes)
    65                                           
    66        42           70      1.7      4.6      for attribute in prohibited_attributes:
    67        38           69      1.8      4.5          try:
    68        38          386     10.2     25.3              element.removeAttribute(attribute)
    69        36           95      2.6      6.2          except xml.dom.NotFoundErr:
    70        36           79      2.2      5.2              pass
    71         4            5      1.2      0.3      try:
    72         4           15      3.8      1.0          if element.hasAttribute("href"):
    73         1           61     61.0      4.0              t = element.toxml()
    74         1          468    468.0     30.7              if re.search('href="http', t) or re.search('href="https', t):
    75                                                           pass
    76                                                       else:
    77                                                           element.removeAttribute("href")
    78                                               except:
    79                                                   pass
    80                                           
    81         4            8      2.0      0.5      list_on_children = element.childNodes
    82        15           29      1.9      1.9      for child in list_on_children:
    83        11           18      1.6      1.2          if child.nodeType == 1:
    84         3           14      4.7      0.9              remove_prohibited_attributes(child)

Total time: 0.010529 s
File: test2.py

la 2nde :

File: /home/foxmask/.pyenv/versions/django-trigger-happy/django-th/th_evernote/sanitize.py
Function: remove_prohibited_attributes at line 51

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    51                                           @profile
    52                                           def remove_prohibited_attributes(element):
    53                                               """
    54                                                   To fit the Evernote DTD need, drop this attribute name
    55                                               """
    56         4           11      2.8      1.2      filter_term = lambda att: att.startswith("on") or\
    57                                                                         att.startswith("data-") or\
    58                                                                         att in ["id", "class", "accesskey", "data",  "dynsrc", "tabindex", "frame", "rules", "width"]
    59                                           
    60         4           58     14.5      6.3      to_be_removed_atts = [att for att in element.attributes.keys() if filter_term(att.lower())]
    61                                           
    62         4          124     31.0     13.6      print(to_be_removed_atts)
    63                                           
    64         6           17      2.8      1.9      for attribute in to_be_removed_atts:
    65         2           71     35.5      7.8          element.removeAttribute(attribute)
    66         4            8      2.0      0.9      try:
    67         4           17      4.2      1.9          if element.hasAttribute("href"):
    68         1           61     61.0      6.7              t = element.toxml()
    69         1          465    465.0     50.9              if re.search('href="http', t) or re.search('href="https', t):
    70                                                           pass
    71                                                       else:
    72                                                           element.removeAttribute("href")
    73                                               except:
    74                                                   pass
    75                                           
    76         4            9      2.2      1.0      list_on_children = element.childNodes
    77        15           34      2.3      3.7      for child in list_on_children:
    78        11           25      2.3      2.7          if child.nodeType == 1:
    79         3           14      4.7      1.5              remove_prohibited_attributes(child)

Total time: 0.009855 s
File: test.py

Ça ne m’étonne pas qu'il y ai peu de différences. Perso je ferais plus la deuxième, c'est plus propre : tu ne va supprimer que les attributs que tu sais qu'ils existent sur l'élément. Mais bon fondamentalement c'est presque pareil.

...