# Automator : indexation de fichiers + écriture de leur contenu



## MojoIII (12 Juillet 2016)

Bonjour à tous,

je me permets de poser la question à la communauté, car j'ai bien du mal à trouver une solution à mon problème. 

Voilà mon objectif : 
- J'ai un nombre élevé de fichiers qui contiennent tous deux sources d'informations. Ces informations sont contenus d'une part entre des balises <img> et d'autre part entre des balises <tags>.
- Le but est de créer un processus qui, pour chaque fichier, isole ces deux informations et les inscrit dans une liste (fichier texte ou autre) en face du nom dudit fichier.

Cela me donnerait par exemple une liste de ce type : 
[nom_du_fichier] ; [texte_entre_balises_img] ; [texte_entre_balises_tags]

Et ça serait le bonheur...

Même si je rêve en secret que l'un de vous me mâche le travail, j'aimerais savoir pour le moment si automator (dont je ne me suis quasiment jamais servi) permet ce genre de choses.
Dans un second temps, je pourrai peut être combiner les indices que vous laisserez. 

Merci, communauté.


----------



## JacqR (13 Juillet 2016)

Bonjour,


Ce n'est pas possible dans Automator sans un script, car Automator ne contient pas d'action pour extraire des données entre des balises.

Voici une solution simple:
Cela suppose que dans chaque fichier, le début et la fin de la balise sont sur la même ligne, que les fins de lignes soient *Unix* (pas *Mac*), que l'encodage des fichiers est identique et que le type de ces fichiers soit du "plain text" (pas des RTF, Word ou autre Office).

Dans Automator:
Ajoute des actions pour obtenir les fichiers désirés.
Ajoute l'action "Exécuter un script shell", sélectionnez "/bin/bash" et "comme arguments" dans les menus des deux boutons de l'action.
Copie/colle ce texte dans l'action "Exécuter un script shell".

```
for f in "$@"
do
    sed -En 's:.*<img>([^<]*)</img>:\1:p; s:.*<tags>([^<]*)</tags>:\1:p' "$f"
done
```

Les balises dans ce script sont <img>*</img>* et <tags>*</tags>*, change les noms des balises si cela est nécessaire.

Ajoute l'action "Créer un fichier texte".
C'est tout.​


----------



## MojoIII (13 Juillet 2016)

Bonjour, 

Tout  d'abord merci pour la réponse rapide. 
Il va donc falloir que je me penche sur la fabrication d'un script. 

Le problème est que les balises ne sont pas sur la même ligne... J'aurais dû le préciser avant : il s'agit de récupérer les données de fichiers .json. Chacun d'eux liste une suite de propriétés relatives à une page web. Je n'ai besoin que de certaines d'entre elles...
Je vais fouiner du côté des possibilités offertes par les script_shell.
En espérant pouvoir bientôt poster une solution !


----------



## JacqR (13 Juillet 2016)

Pour des fichiers json, vous pouvez utiliser le module json dans un script *Python*:
Voir https://docs.python.org/2.7/library/json.html
Vous n'avez rien à installer, c'est standard dans OSX

Exemples:
Si le format est simple comme ceci:


> {
> "img":  "xyz bla",
> "ref1":  "file",
> "menu1": "sssssss",
> ...


Ce sera ce script:

```
import json,sys
for f in sys.argv[1:]:
    with open(f, 'r') as f1:
        obj=json.load(f1)
        print obj['img']
        print obj['tags']
```
Un exemple plus complexe:


> {
> "menu": {
> "img":  "xyz bla",
> "ref1":  "file",
> ...


Ce sera ce script:

```
import json,sys
for f in sys.argv[1:]:
    with open(f, 'r') as f1:
        obj=json.load(f1)
        print obj["menu"]["img"]
        print obj["menu"]["tags"]
```

Pour Automator, ce sera la même procédure que celui dans le message #2, sauf pour le script et ce sera "*/usr/bin/python*" au lieu de "*/bin/bash*"


----------



## MojoIII (13 Juillet 2016)

...
Je fais mes test et je reviens vers vous.

Merci !


----------



## MojoIII (19 Juillet 2016)

Bonjour !
Encore merci pour les conseils en python. J'y connais rien en py mais j'avance doucement.
J'ai un problème d'encodage : le fichier de sortie ne comprend pas certains caractères et il m'arrive de recevoir une erreur du type : UnicodeEncodeError: 'ascii' codec can't encode character u'\ufffc' in position 0: ordinal not in range(128).
Pas top... Je continue à chercher.


----------



## JacqR (19 Juillet 2016)

Bonjour,


Quand on utilise la commande *print* dans un script python, l'encodage de sortie doit-être toujours en '*utf-8*'.
Normalement, dans le *Terminal*, l'encodage de sortie (sys.stdout.encoding pour python) est définie en '*utf-8*'.
Mais, dans *Automator*, l'encodage de sortie (sys.stdout.encoding pour python) n'est pas définie, il est à *None,* donc un caractère autre que de l'ascii donnera une erreur si on utilise la commande print.
Donc, il faut utiliser *.encode('utf-8')*, alors que c'est pas nécessaire quand on exécute le script dans le terminal.
Exemple:


> print obj["menu"]["img"].encode('utf-8')
> print obj['menu']['tags'].encode('utf-8')




Si l'encodage des fichiers n'est pas en "*utf-8"*
Il y aura une erreur avec la commande *json.load(f1)*
Il faut utiliser with codecs.open(f, 'r', 'the encoding name') as f1: au lieu de with open(f, 'r') as f1:
Important, il faut importer *codecs* dans la première ligne du script python.

Example pour des fichiers en Occidental (Mac OS Roman)

```
import json,sys,codecs
for f in sys.argv[1:]:
    with codecs.open(f, 'r', 'macroman') as f1:
        obj=json.load(f1)
        print obj['menu']['img'].encode('utf-8')
        print obj['menu']['tags'].encode('utf-8')
```

Exemple pour des fichiers en Unicode (UTF-16)

```
with codecs.open(f,'r', encoding='utf-16') as f1:
```


----------



## MojoIII (20 Juillet 2016)

Bonjour JacqR,

C'est beaucoup mieux pour l'encodage !
Cependant, un problème n'arrivant jamais seul, j'ai maintenant une autre erreur :
AttributeError: 'list' object has no attribute 'encode'.
Effectivement une partie des des obj contiennent eux-mêmes des listes sous cette forme :


```
{
"tags": [
    "a",
    "b",
    "c",
    "d",]
}
```

En effet, j'arrive à avoir un retour en faisant 

```
print obj['tags']
```
Mais ce n'est pas encodé correctement...

Par ailleurs, un de mes objets me retourne :

```
<center><img src="https://www.zzz.net/aaa/b.png"><font size="2pt">z</font></center>
```

Vous me voyez venir : est il possible de faire en sorte que seule l'url soit écrite ?

Keep searching !
Merci en tout cas !


----------



## JacqR (20 Juillet 2016)

Bonjour,

Oui, cela ne fonctionne pas pour une liste, car il faut utiliser *.encode()* que sur un texte.

Donc, il faut utiliser la commande .join() sur la liste, exemples :
Si vous voulez que chaque élément dans la liste soit séparée par une ligne:

```
print '\n '.join(obj['tags']).encode('utf-8')
```
La sortie sera


> a
> b
> c
> d​


Si vous voulez que chaque élément dans la liste soit sur la même ligne, séparée par un espace:

```
print ' '.join(obj['tags']).encode('utf-8')
```



> a b c d



Si vous voulez que chaque élément dans la liste soit sur la même ligne, séparée par une virgule et un espace:

```
print ', '.join(obj['tags']).encode('utf-8')
```



> a, b, c, d




Pour un code HTML, vous pouvez utiliser le module HTMLParser :
Exemple de script:

```
import json,sys; from HTMLParser import HTMLParser

class myhtmlparser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
    def handle_starttag(self, tag, attrs):
        if tag == mytag:
            for name, value in attrs:
                if name == myAttr: print value

mytag = 'img'   ### nom de la balise à recherché dans un texte HTML
myAttr = 'src'   ### nom de l'attribut à recherché dans la balise img
parser = myhtmlparser()   ### crée une instance

for f in sys.argv[1:]:
    with open(f, 'r') as f1:
        obj=json.load(f1)
        htmlString = obj["img"].encode('utf-8') ### on récupère le HTML
        parser.feed(htmlString) ### la sortie sera la valeur de src dans le tag img
        print ', '.join(obj['tags']).encode('utf-8')
```


----------



## MojoIII (21 Juillet 2016)

Très impressionnant !
Ça marche très bien.

Problème résolu. Merci beaucoup JacqR !


----------

