Sélectionner tous les éléments x contenu dans un tag y

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour à tous,
J'essaye de parser cette page à l'aide de BeautifulSoup. Pour le moment, je cherche à analyser chaque ligne de <table></table> afin de savoir si on est en présence d'un aérodrome où d'une piste. Je pensais que ça allais être simple, mais ça n'est pas aussi simple que ce que je pensais.
Voici ce que j'ai pour le moment :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from bs4 import BeautifulSoup
from urllib.request import urlopen

def strip_del(soup):
    """
        Supprime toutes les balises <del></del> et leur contenu
    """
    for d in soup.find_all('del'):
        d.decompose()

def download_airfields_index():
    """
        Télécharge l'intégralité de la page HTML contenant toutes les
        informations des aérodromes français
    """
    return urlopen('https://www.sia.aviation-civile.gouv.fr/aip/enligne/FRANCE/AIRAC-2015-09-17/html/eAIP/FR-AD-1.3-fr-FR.html#AD-1.3').read()

def num_there(s):
    """
        Retourne true si la chaine s contient un chiffre, sinon, retourne false
    """
    return any(i.isdigit() for i in s)

def is_airfield(tr):
    """
        Retourne true si tr décrit un aérodrome (regarde le contenu du premier
        td, si il a des chiffres dedans, c'est que ce n'est pas un aérodrome)
    """
    return not num_there(tr.td.span.text)

if __name__ == '__main__':
    html = download_airfields_index()
    soup = BeautifulSoup(html, 'lxml')
    strip_del(soup)

    # On prend la liste des tr contenu dans <tbody>
    for tr in soup.find('tbody').find_all('tr'):
        if is_airfield(tr):
            print('yes')
        else:
            print('no')

J'ai besoin de chercher les <tr></tr> dans <tbody></tbody> car il y a aussi des <tr></tr> dans <thead></thead> mais je n'en veux pas.
La liste retournée par find_all('tr') ne contient que le premier élément. Pourquoi ?

Merci de votre aide!

PS : si ça intéresse quelqu'un, je cherche une personne pour m'aider à faire ce parser, car ça m'a l'air d'être bien costaud à faire. :)

Édité par Wizix

Mon projet : OpenPlane, un utilitaire en Java pour les pilotes, les vrais !

+1 -0

PS : si ça intéresse quelqu'un, je cherche une personne pour m'aider à faire ce parser, car ça m'a l'air d'être bien costaud à faire. :)

Wizix

Présent ! Pour avoir utilisé bs plusieurs fois, je commence à connaitre.


Edit - Pour ton problème :

  • python3 : ca marche out of the box
  • python2 : j'ai du changer ça :
    • python n'aime pas les accents et le 'ç'
    • from urllib.request import urlopen => from urllib import urlopen
    • retirer le '#' et ce qui suis dans l'url. J'ai l'impression que c'etait ça le problème, mais je ne vois absolument pas la raison … A priori ca ne change rien au contenu que urllib récupère …

Édité par Nodraak

http://nodraak.fr/ – Dictateur bienveillant de l'iTeam, logiciel libre et sécurité à l'ECE Paris

+2 -0
Auteur du sujet

Avec plaisir ! Je t'envoie ce soir un message privé pour qu'on en rediscute. :)


Tout mon programme tourne sous Python3. Et même sous Python3, ça ne fonctionne pas. J'ai toujours qu'un seul yes alors que je devrais en avoir une bonne centaine. C'est à dire que le tableau renvoyé par soup.find('tbody').find_all('tr') ne contient qu'un seul élément !

Mon projet : OpenPlane, un utilitaire en Java pour les pilotes, les vrais !

+1 -0

Bizarre, chez moi j'ai un bon millier d'élément dans la liste

1
2
>>> len(soup.find("tbody").find_all("tr"))
1146

Chez moi j'ai eu un problème avec urlopen qui refusait le https (jme suis pas cassé la tête: Firefox > Afficher le code source > copier/coller dans un fichier local). Peut-être qu'il y a le même problème de ton coté ? La source html est bien téléchargée ?

+0 -0
Auteur du sujet

Oui, la source HTML est bien téléchargée par urlopen… Voici ce que j'ai comme code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
if __name__ == '__main__':
    html = download_airfields_index()
    soup = BeautifulSoup(html, 'lxml')
    strip_del(soup)

    print(len(soup.find('tbody').find_all('tr')))

    # On prend la liste des tr contenu dans <tbody>
    for tr in soup.find('tbody').find_all('tr'):
        if is_airfield(tr):
            print('yes')
        else:
            print('no')

En téléchargeant la source depuis internet :

1
2
1
yes

Et depuis un fichier :

1
2
1
yes

Mon projet : OpenPlane, un utilitaire en Java pour les pilotes, les vrais !

+0 -0

Cette réponse a aidé l'auteur du sujet

Voila ce que j'ai bricolé entre deux cours cette aprèm. C'est pas parfait donc je vais continuer à bosser dessus ce soir.

Pour commencer, le code (qui manque de commentaires, j'espère que c'est à peu près compréhensible) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import sys

from bs4 import BeautifulSoup
from urllib import urlopen

"""
airfields = [
    {
        'field1': value1,
        'field2': value2,
        ...
        'runways': [
            {
                'field1': value1,
                'field2': value2,
                ...
            },
            ...
        ],
    },
    ...
]
"""

labels_airfield = (
    'code',
    'name',
    'mag',
    'geo_arp',
    'alt',
    'traffic',
    'gund',
    'status',
)

labels_runway = (
    'rwy',
    'dimensions (m) / surface',
    'orientation geo',
    'thr : position geo',
    'thr : alt ft',
    'dthr : position geo',
    'dthr : alt ft',
    'statut ad',
)

def parse_airfields():
    html = urlopen('https://www.sia.aviation-civile.gouv.fr/aip/enligne/FRANCE/AIRAC-2015-09-17/html/eAIP/FR-AD-1.3-fr-FR.html').read()
    #html = open('AIP.html').read()
    soup = BeautifulSoup(html)

    # Supprime toutes les balises <del></del> et leur contenu
    for d in soup.find_all('del'):
        d.decompose()

    airfields = []
    current_airfield = None

    for tr in soup.find('tbody').find_all('tr'):
        if tr.td.span.text.isalpha():  # airfield
            if current_airfield != None:  # dont apend an empty airfield on the first loop
                airfields.append(current_airfield)

            # parse new airfield
            data = [''.join(td.stripped_strings) for td in tr.find_all('td')]
            current_airfield = dict(zip(labels_airfield, data))
            current_airfield['runways'] = []
        else:  # runway
            data = [''.join(td.stripped_strings) for td in tr.find_all('td')]
            current_runway = dict(zip(labels_runway, data))

            current_airfield['runways'].append(current_runway)

    # append last airfield
    airfields.append(current_airfield)

    return airfields


if __name__ == '__main__':
    airfields = parse_airfields()
    json.dump(airfields, sy s.stdout, sort_keys=True, indent=4, separators=(',', ': '))
    sys.stdout.write('\n')

Ce qui donne ceci en sortie (j'ai gardé que le début, c'est un peu long sinon - et oui, j'ai qq problèmes d'encoding, je vais améliorer ça) :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
[
    {
        "alt": "220",
        "code": "LFOI",
        "geo_arp": "50\u00b008'32\"N001\u00b049'52\"E",
        "gund": "NIL",
        "mag": "-0.09\u00b0",
        "name": "ABBEVILLE",
        "runways": [
            {
                "dimensions (m) / surface": "1250x23rev\u00eatue",
                "dthr : alt ft": "",
                "dthr : position geo": "",
                "orientation geo": "22\u00b0202\u00b0",
                "rwy": "0220",
                "statut ad": "",
                "thr : alt ft": "",
                "thr : position geo": ""
            },
            {
                "dimensions (m) / surface": "900x100non rev\u00eatue",
                "dthr : alt ft": "",
                "dthr : position geo": "",
                "orientation geo": "22\u00b0202\u00b0",
                "rwy": "02L20R",
                "statut ad": "",
                "thr : alt ft": "",
                "thr : position geo": ""
            },
            {
                "dimensions (m) / surface": "560x80non rev\u00eatue",
                "dthr : alt ft": "",
                "dthr : position geo": "",
                "orientation geo": "132\u00b0312\u00b0",
                "rwy": "1331",
                "statut ad": "",
                "thr : alt ft": "",
                "thr : position geo": ""
            }
        ],
        "status": "ouvert \u00e0 la CAP",
        "traffic": "INTL/NTL VFR P"
    },
    {
        "alt": "204",
        "code": "LFBA",
        "geo_arp": "44\u00b010'29\"N000\u00b035'26\"E",
        "gund": "157",
        "mag": "-0.74\u00b0",
        "name": "AGEN LA GARENNE",
        "runways": [
            {
                "dimensions (m) / surface": "2165x30rev\u00eatue",
                "dthr : alt ft": "203200",
                "dthr : position geo": "44\u00b010'37.71\"N000\u00b034'56.44\"E44\u00b010'18.14\"N000\u00b036'03.14\"E",
                "orientation geo": "112\u00b0292\u00b0",
                "rwy": "1129",
                "statut ad": "voir aussi AD2",
                "thr : alt ft": "202202",
                "thr : position geo": "44\u00b010'39.73\"N000\u00b034'49.54\"E44\u00b010'13.25\"N000\u00b036'19.78\"E"
            }
        ],
        "status": "ouvert \u00e0 la CAP",
        "traffic": "INTL/NTL IFR/VFR S/NS/P"
    },
    {
        "alt": "266",
        "code": "LFDA",
        "geo_arp": "43\u00b042'31\"N000\u00b014'50\"W",
        "gund": "159",
        "mag": "-0.95\u00b0",
        "name": "AIRE SUR L'ADOUR",
        "runways": [
            {
                "dimensions (m) / surface": "1000x30rev\u00eatue",
                "dthr : alt ft": "259263",
                "dthr : position geo": "43\u00b042'33.89\"N000\u00b015'05.47\"W43\u00b042'26.69\"N000\u00b014'44.52\"W",
                "orientation geo": "115\u00b0295\u00b0",
                "rwy": "1230",
                "statut ad": "",
                "thr : alt ft": "258265",
                "thr : position geo": "43\u00b042'36.08\"N000\u00b015'11.85\"W43\u00b042'22.21\"N000\u00b014'31.51\"W"
            },
            {
                "dimensions (m) / surface": "975x80non rev\u00eatue",
                "dthr : alt ft": "261264",
                "dthr : position geo": "43\u00b042'35.82\"N000\u00b015'05.30\"W43\u00b042'27.99\"N000\u00b014'42.58\"W",
                "orientation geo": "115\u00b0295\u00b0",
                "rwy": "12L30R",
                "statut ad": "",
                "thr : alt ft": "260266",
                "thr : position geo": "43\u00b042'37.36\"N000\u00b015'09.83\"W43\u00b042'23.82\"N000\u00b014'30.46\"W"
            }
        ],
        "status": "ouvert \u00e0 la CAP",
        "traffic": "NTL VFR P"
    },
    {
        "alt": "368",
        "code": "LFMA",
        "geo_arp": "43\u00b030'19\"N005\u00b022'02\"E",
        "gund": "162",
        "mag": "1.24\u00b0",
        "name": "AIX LES MILLES",
        "runways": [
            {
                "dimensions (m) / surface": "1504x30rev\u00eatue",
                "dthr : alt ft": "360",
                "dthr : position geo": "43\u00b030'07.49\"N005\u00b022'13.57\"E",
                "orientation geo": "146\u00b0326\u00b0",
                "rwy": "1432",
                "statut ad": "voir aussi AD2",
                "thr : alt ft": "340368",
                "thr : position geo": "43\u00b030'39.55\"N005\u00b021'43.47\"E43\u00b029'59.30\"N005\u00b022'21.26\"E"
            }
        ],
        "status": "ouvert \u00e0 la CAP",
        "traffic": "NTL IFR/VFR P"
    }
]

A bientôt pour une version mise à jour :)


Tout mon programme tourne sous Python3. Et même sous Python3, ça ne fonctionne pas. J'ai toujours qu'un seul yes alors que je devrais en avoir une bonne centaine. C'est à dire que le tableau renvoyé par soup.find('tbody').find_all('tr') ne contient qu'un seul élément !

Wizix

Hum. Tout ce que je peux te dire c'est ça :

1
2
3
4
5
6
7
8
$python3
Python 3.4.2 (default, Oct  8 2014, 10:45:20) 
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import bs4
>>> bs4.__version__
'4.3.2'
>>>

(Avec Debian 8.2)

Édité par Nodraak

http://nodraak.fr/ – Dictateur bienveillant de l'iTeam, logiciel libre et sécurité à l'ECE Paris

+1 -0
Auteur du sujet

Gros merci ! Je n'aurais jamais fait mieux ! (Étonné que le code soit si court!)

Petite faute dans le if __name__ == '__main__' :

1
2
3
4
5
json.dump(airfields, sy s.stdout, sort_keys=True, indent=4, separators=(',', ': '))
# devient alors 
json.dump(airfields, sys.stdout, sort_keys=True, indent=4, separators=(',', ': '))
#                      ^ 
#                  espace supprimé

Aussi, le code ne fonctionne pas avec Python3. :(

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Traceback (most recent call last):
  File "algo.py", line 85, in <module>
    airfields = parse_airfields()
  File "algo.py", line 69, in parse_airfields
    data = [''.join(td.stripped_strings) for td in tr.find_all('td')]
  File "/home/louis/.local/lib/python3.4/site-packages/bs4/element.py", line 1255, in find_all
    return self._find_all(name, attrs, text, limit, generator, **kwargs)
  File "/home/louis/.local/lib/python3.4/site-packages/bs4/element.py", line 525, in _find_all
    return ResultSet(strainer, result)
  File "/home/louis/.local/lib/python3.4/site-packages/bs4/element.py", line 1712, in __init__
    super(ResultSet, self).__init__(result)
  File "/home/louis/.local/lib/python3.4/site-packages/bs4/element.py", line 522, in <genexpr>
    result = (element for element in generator
  File "/home/louis/.local/lib/python3.4/site-packages/bs4/element.py", line 1273, in descendants
    current = current.next_element
AttributeError: 'NoneType' object has no attribute 'next_element'

C'est exactement la même erreur que j'avais.

Merci beaucoup de ton aide !


Tien, on a pas la même version :

1
2
3
4
5
6
Python 3.4.0 (default, Jun 19 2015, 14:20:21) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import bs4
>>> bs4.__version__
'4.4.0'

Édité par Wizix

Mon projet : OpenPlane, un utilitaire en Java pour les pilotes, les vrais !

+0 -0

Tiens, on a pas la même version :

1
2
3
4
5
6
Python 3.4.0 (default, Jun 19 2015, 14:20:21) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import bs4
>>> bs4.__version__
'4.4.0'

Wizix

Apres avoir testé chez moi, j'en conclu que mon code ne fonctionne pas avec la version 4.4 de BeautifulSoup. Donc version 4.3 requise, pour le moment en tout cas.

http://nodraak.fr/ – Dictateur bienveillant de l'iTeam, logiciel libre et sécurité à l'ECE Paris

+1 -0

Re.

J'ai amélioré le script, il parse maintenant correctement :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import sys

from bs4 import BeautifulSoup
from urllib import urlopen

# Workaround for unicode issue, cf. http://stackoverflow.com/a/5530880
# set the output encoding as utf8 instead of the system default (ascii in my case)
import codecs
import sys
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)


def parse_runway(tr):
    data = [list(td.stripped_strings) for td in tr.find_all('td')]

    # some dirty hacks to fix some malformed rows

    if len(data[1]) == 2: data[1] = [data[1][0], 'x', '-1', data[1][1]]
    if len(data[1]) == 3: data[1].append('-')

    if len(data[2]) == 0: data[2] = ['-1', 'deg', '-1']

    if len(data[3]) == 0: data[3] = [''] * 4
    if len(data[3]) == 2: data[3].extend(data[3]) # duplicate the data

    if len(data[4]) == 0: data[4] = [''] * 2
    if len(data[4]) == 1: data[4].extend(data[4]) # duplicate the data

    if len(data[5]) == 0: data[5] = [''] * 4
    if len(data[5]) == 2: data[5].extend(data[5]) # duplicate the data

    if len(data[6]) == 0: data[6] = [''] * 2
    if len(data[6]) == 1: data[6].extend(data[6]) # duplicate the data

    # parse

    ret = {
        'length': data[1][0],
        'width': data[1][2],
        'surface': data[1][3],
    }

    for i in [0, 1]:
        ret['side%d' % (i+1)] = {
            'code': data[0][i],
            'orientation': data[2][2*i],
            #'status': data[7][0],  # todo
            'thr': {
                'position': ' '.join((data[3][2*i+1], data[3][2*i+1])),
                'alt': data[4][i],
            },
            'dthr': {
                'position': ' '.join((data[5][2*i+1], data[5][2*i+1])),
                'alt': data[6][i],
            },

        }

    return ret


def parse_airfields():
    html = urlopen('https://www.sia.aviation-civile.gouv.fr/aip/enligne/FRANCE/AIRAC-2015-09-17/html/eAIP/FR-AD-1.3-fr-FR.html').read()
    #html = open('AIP.html').read()
    soup = BeautifulSoup(html, 'lxml')

    # Supprime toutes les balises <del></del> et leur contenu
    for d in soup.find_all('del'):
        d.decompose()

    airfields = []
    current_airfield = None

    for tr in soup.find('tbody').find_all('tr'):
        if tr.td.span.text.strip() == '' or tr.td.span.text.isalpha():  # airfield
            if current_airfield != None:  # dont apend an empty airfield on the first loop
                airfields.append(current_airfield)

            # parse new airfield

            labels_airfield = (
                'code',
                'name',
                'mag',
                'geo_arp',
                'alt',
                'traffic',
                'gund',
                'status',
            )

            data = [' '.join(td.stripped_strings) for td in tr.find_all('td')]
            current_airfield = dict(zip(labels_airfield, data))
            current_airfield['runways'] = []
        else:  # runway
            current_airfield['runways'].append(parse_runway(tr))

    # append last airfield
    airfields.append(current_airfield)

    return airfields

if __name__ == '__main__':
    airfields = parse_airfields()
    json.dump(airfields, sys.stdout, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
    sys.stdout.write('\n')

Un exemple de sortie (tronquée) :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
[
    {
        "alt": "220",
        "code": "LFOI",
        "geo_arp": "50°08'32\"N 001°49'52\"E",
        "gund": "NIL",
        "mag": "-0.09 °",
        "name": "ABBEVILLE",
        "runways": [
            {
                "length": "1250",
                "side1": {
                    "code": "02",
                    "dthr": {
                        "alt": "",
                        "position": " "
                    },
                    "orientation": "22",
                    "thr": {
                        "alt": "",
                        "position": " "
                    }
                },
                "side2": {
                    "code": "20",
                    "dthr": {
                        "alt": "",
                        "position": " "
                    },
                    "orientation": "202",
                    "thr": {
                        "alt": "",
                        "position": " "
                    }
                },
                "surface": "revêtue",
                "width": "23"
            },
            {
                "length": "900",
                "side1": {
                    "code": "02L",
                    "dthr": {
                        "alt": "",
                        "position": " "
                    },
                    "orientation": "22",
                    "thr": {
                        "alt": "",
                        "position": " "
                    }
                },
                "side2": {
                    "code": "20R",
                    "dthr": {
                        "alt": "",
                        "position": " "
                    },
                    "orientation": "202",
                    "thr": {
                        "alt": "",
                        "position": " "
                    }
                },
                "surface": "non revêtue",
                "width": "100"
            },
            {
                "length": "560",
                "side1": {
                    "code": "13",
                    "dthr": {
                        "alt": "",
                        "position": " "
                    },
                    "orientation": "132",
                    "thr": {
                        "alt": "",
                        "position": " "
                    }
                },
                "side2": {
                    "code": "31",
                    "dthr": {
                        "alt": "",
                        "position": " "
                    },
                    "orientation": "312",
                    "thr": {
                        "alt": "",
                        "position": " "
                    }
                },
                "surface": "non revêtue",
                "width": "80"
            }
        ],
        "status": "ouvert à la CAP",
        "traffic": "INTL/NTL VFR P"
    },
    {
        "alt": "204",
        "code": "LFBA",
        "geo_arp": "44°10'29\"N 000°35'26\"E",
        "gund": "157",
        "mag": "-0.74 °",
        "name": "AGEN LA GARENNE",
        "runways": [
            {
                "length": "2165",
                "side1": {
                    "code": "11",
                    "dthr": {
                        "alt": "203",
                        "position": "000°34'56.44\"E 000°34'56.44\"E"
                    },
                    "orientation": "112",
                    "thr": {
                        "alt": "202",
                        "position": "000°34'49.54\"E 000°34'49.54\"E"
                    }
                },
                "side2": {
                    "code": "29",
                    "dthr": {
                        "alt": "200",
                        "position": "000°36'03.14\"E 000°36'03.14\"E"
                    },
                    "orientation": "292",
                    "thr": {
                        "alt": "202",
                        "position": "000°36'19.78\"E 000°36'19.78\"E"
                    }
                },
                "surface": "revêtue",
                "width": "30"
            }
        ],
        "status": "ouvert à la CAP",
        "traffic": "INTL/NTL IFR/VFR S/NS/P"
    },
    {
        "alt": "266",
        "code": "LFDA",
        "geo_arp": "43°42'31\"N 000°14'50\"W",
        "gund": "159",
        "mag": "-0.95 °",
        "name": "AIRE SUR L'ADOUR",
        "runways": [
            {
                "length": "1000",
                "side1": {
                    "code": "12",
                    "dthr": {
                        "alt": "259",
                        "position": "000°15'05.47\"W 000°15'05.47\"W"
                    },
                    "orientation": "115",
                    "thr": {
                        "alt": "258",
                        "position": "000°15'11.85\"W 000°15'11.85\"W"
                    }
                },
                "side2": {
                    "code": "30",
                    "dthr": {
                        "alt": "263",
                        "position": "000°14'44.52\"W 000°14'44.52\"W"
                    },
                    "orientation": "295",
                    "thr": {
                        "alt": "265",
                        "position": "000°14'31.51\"W 000°14'31.51\"W"
                    }
                },
                "surface": "revêtue",
                "width": "30"
            },
            {
                "length": "975",
                "side1": {
                    "code": "12L",
                    "dthr": {
                        "alt": "261",
                        "position": "000°15'05.30\"W 000°15'05.30\"W"
                    },
                    "orientation": "115",
                    "thr": {
                        "alt": "260",
                        "position": "000°15'09.83\"W 000°15'09.83\"W"
                    }
                },
                "side2": {
                    "code": "30R",
                    "dthr": {
                        "alt": "264",
                        "position": "000°14'42.58\"W 000°14'42.58\"W"
                    },
                    "orientation": "295",
                    "thr": {
                        "alt": "266",
                        "position": "000°14'30.46\"W 000°14'30.46\"W"
                    }
                },
                "surface": "non revêtue",
                "width": "80"
            }
        ],
        "status": "ouvert à la CAP",
        "traffic": "NTL VFR P"
    },
    {
        "alt": "368",
        "code": "LFMA",
        "geo_arp": "43°30'19\"N 005°22'02\"E",
        "gund": "162",
        "mag": "1.24 °",
        "name": "AIX LES MILLES",
        "runways": [
            {
                "length": "1504",
                "side1": {
                    "code": "14",
                    "dthr": {
                        "alt": "360",
                        "position": "005°22'13.57\"E 005°22'13.57\"E"
                    },
                    "orientation": "146",
                    "thr": {
                        "alt": "340",
                        "position": "005°21'43.47\"E 005°21'43.47\"E"
                    }
                },
                "side2": {
                    "code": "32",
                    "dthr": {
                        "alt": "360",
                        "position": "005°22'13.57\"E 005°22'13.57\"E"
                    },
                    "orientation": "326",
                    "thr": {
                        "alt": "368",
                        "position": "005°22'21.26\"E 005°22'21.26\"E"
                    }
                },
                "surface": "revêtue",
                "width": "30"
            }
        ],
        "status": "ouvert à la CAP",
        "traffic": "NTL IFR/VFR P"
    },
]

Pour le problème d'encoding, Wizix m'a souflé la solution par mp :

Ajoute l'argument ensure_ascii=False dans le json.dump()

J'ai du rajouter trois lignes pour que mon terminal gère correctement ce nouvel encoding (ref. http://stackoverflow.com/a/5530880 )

Voila ça m'a l'air bon \o/

http://nodraak.fr/ – Dictateur bienveillant de l'iTeam, logiciel libre et sécurité à l'ECE Paris

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte