Cet article est une suite de mon livre Introduction à Python avec 100 Knocking Language Processing. Je vais vous expliquer 100 coups Chapitre 3.
Ce chapitre utilise des expressions régulières. En Python, il est géré par le module re
. Pour utiliser ce module, il est nécessaire de comprendre non seulement l'expression régulière elle-même, mais aussi la méthode et l'objet de correspondance, c'est donc assez difficile. Je ne pense plus que ce soit une entrée de gamme. Je n'avais pas l'impression de pouvoir écrire un meilleur commentaire que le Tutoriel officiel, alors veuillez le lire.
Cependant, les expressions canoniques Python sont lentes, j'essaye donc de les éviter autant que possible.
Pour le moment, téléchargez le fichier de manière appropriée. * Ce fichier est distribué sous la licence non portable Creative Commons Attribution-Inheritance 3.0. *
$ wget https://nlp100.github.io/data/jawiki-country.json.gz
Selon l'énoncé du problème, les informations d'un article par ligne sont stockées au format JSON. Le format JSON est une simple exportation de tableaux et de dictionnaires, et de nombreux langages de programmation prennent en charge ce format. Cependant, le format de tout ce fichier est appelé JSONL (JSON Lines). Jetez un œil au contenu du fichier avec $ gunzip -c jawiki-country.json.gz | less
etc. (Vous pouvez le voir directement avec less
).
json Python dispose également d'une bibliothèque pour gérer facilement ce JSON. Son nom est également json. Ceci est un exemple apporté de Document, mais il transforme une chaîne json en un objet Python comme celui-ci, et vice versa. C'est très simple à faire.
import json
dic = json.loads('{"bar":["baz", null, 1.0, 2]}')
print(type(dic))
print(dic)
<class 'dict'>
{'bar': ['baz', None, 1.0, 2]}
dumped = json.dumps(dic)
print(type(dumped))
print(dumped)
<class 'str'>
{"bar": ["baz", null, 1.0, 2]}
Comme c'était difficile à comprendre, j'ai également affiché le nom du type avec type ()
. Au fait, «s» in »se charge» et «dumps» signifie «chaîne» au lieu de 3 unités.
Lisez le fichier JSON de l'article Wikipedia et affichez le texte de l'article sur "UK". Dans les problèmes 21-29, exécutez sur le texte de l'article extrait ici.
Le fichier téléchargé est au format gz, mais je ne veux pas l'étendre autant que possible. Il est préférable de le lire avec le module gzip
de Python, ou de sortir le résultat de l'expansion en standard avec la commande Unix et de le connecter avec un tube.
Voici un exemple de réponse.
q20.py
import json
import sys
for line in sys.stdin:
wiki_dict = json.loads(line)
if wiki_dict['title'] == 'Angleterre':
print(wiki_dict.get('text'))
$ gunzip -c jawiki-country.json.gz | python q20.py > uk.txt
$ head -n5 uk.txt
{{redirect|UK}}
{{redirect|Royaume-Uni|Pays honorables au printemps et à l'automne|Anglais(Printemps et automne)}}
{{Otheruses|pays européen|Cuisine locale des préfectures de Nagasaki et Kumamoto|Igirisu}}
{{Informations de base sur le pays | Abréviation = Royaume-Uni
Je peux obtenir des redirections et des articles avec le même nom, mais il ne devrait y avoir aucun problème.
Extrayez la ligne qui déclare le nom de la catégorie dans l'article.
Consultez la Wikipédia Référence rapide du balisage et le contenu du fichier réel Pensons-y.
Il semble suffisant d'extraire les lignes commençant par '[[Category'
. str.startswith (prefix)
retournera la valeur de vérité indiquant si la chaîne commence par prefix
.
Voici un exemple de réponse.
q21.py
import sys
for line in sys.stdin:
if line.startswith('[[Category'):
print(line.rstrip())
(Je me souviens que la version 2015 était un mélange de minuscules [[category
s, mais c'est parti dans la version 2020 ...)
Extraire les noms des catégories d'articles (par nom, pas ligne par ligne).
Ce sera comme ça si vous vous coupez les mains.
q22.py
import sys
for line in sys.stdin:
print(line.lstrip("[Category:").rstrip("|*]\n"))
$ python q21.py < uk.txt | python q22.py
Angleterre Pays membres du Royaume-Uni Royaume du Royaume-Uni Pays membres du G8 États membres de l'Union européenne | Ancien Nation marine Pays souverain existant Pays insulaire Une nation / territoire créé en 1801
Affichez les noms de sections et leurs niveaux contenus dans l'article (par exemple, 1 si "== nom de section ==").
Remplacez == country name ==
par country name 1
. Vous pouvez compter le sub
dans la chaîne avec str.count (sub)
.
Voici un exemple de réponse qui n'utilise pas d'expressions régulières.
q23.py
import sys
for line in sys.stdin:
if line.startswith('=='):
sec_name = line.strip('= \n')
level = int(line.count('=')/2 - 1)
print(sec_name, level)
Extrayez tous les fichiers multimédias référencés dans l'article.
Toutes les éditions 2020Fichier:Battle of Waterloo 1815.PNG|
Il a la forme de ceci.|
Utilisez des expressions régulières, en notant que vous souhaitez les supprimer et qu'il peut y en avoir plusieurs sur une ligne. Le test d'expressions régulières est facile avec les outils de vérification en ligne.
Voici un exemple de réponse.
q24.py
import re
import sys
pat = re.compile(r'(Fichier:)(?P<filename>.+?)\|')
for line in sys.stdin:
for match in pat.finditer(line):
print(match.group('filename'))
.+?\|
Dans "Après le moins de répétitions possible de n'importe quel personnage|
"Cela signifie que. Lors de l'examen de plusieurs matchsfinditer()
Est pratique. S'il n'y a pas de correspondance, en premier lieufor
La phrase ne tourne pas.
Le même résultat peut être obtenu même si l'argument de «groupe» est 2.
Extraire les noms de champs et les valeurs du modèle "informations de base" inclus dans l'article et les stocker sous forme d'objet dictionnaire.
Il est difficile de gérer les champs qui sont cassés dans le modèle.
{{Informations de base Pays
|Nom abrégé=Angleterre
|Nom du pays japonais=Royaume-Uni de Grande-Bretagne et d'Irlande du Nord
|Nom officiel du pays= {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>Nom officiel du pays autre que l'anglais:<br/>
*{{lang|gd|An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath}}([[Gaélique écossais]])<br/>
*{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}([[Pays de Galles]])<br/>
Voici un exemple de réponse.
q25.py
import sys
import json
def main():
dic = extract_baseinf(sys.stdin)
sys.stdout.write(json.dumps(dic, ensure_ascii=False))
def extract_baseinf(fi):
baseinf = {}
isbaseinf = False
for line in fi:
if isbaseinf:
if line.startswith('}}'):
return baseinf
elif line[0] == '|':
templis = line.strip('|\n').split('=')
key = templis[0].rstrip()
value = "=".join(templis[1:]).lstrip()
baseinf[key] = value
else:
value = line.rstrip('\n')
baseinf[key] += f"\n{value}"
elif line.startswith('{{Informations de base'):
isbaseinf = True
if __name__ == '__main__':
main()
!python q25.py < uk.txt > uk_baseinf.json
S'il s'étend sur plusieurs lignes, il est traité en les concaténant.
Je l'écrirai une fois dans json car le code du prochain problème sera compliqué. A ce moment, les caractères seront déformés sauf si ʻensure_ascii = False`.
Au moment du traitement> 25, supprimez le balisage d'accentuation MediaWiki (tout accent faible, accentuation et accentuation forte) de la valeur du modèle et convertissez-le en texte (Référence: [Markup Quick Reference](http: // ja. wikipedia.org/wiki/Help:% E6% 97% A9% E8% A6% 8B% E8% A1% A8)).
Si `` '' apparaît 2, 3 ou 5 fois de suite, supprimez-le. En termes d'expression régulière, cela ressemble à r '{2,5}. +?' {2,5}
, mais c'est difficile de le faire sérieusement. Comme d'habitude, si vous le faites sans utiliser d'expressions régulières, cela ressemblera à ceci.
q26.py
import json
import sys
def main():
dic = json.loads(sys.stdin.read())
dic = remove_emphasis(dic)
print(json.dumps(dic, ensure_ascii=False, indent=4))
def remove_emphasis(dic):
for key, value in dic.items():
for n in (5, 3, 2):
eliminated = value.split("'" * n)
div, mod = divmod(len(eliminated), 2)
if mod == 1 and div > 0:
value = ''.join(eliminated)
dic[key] = value
return dic
if __name__ == '__main__':
main()
La procédure consiste à lire le fichier json créé dans la question précédente à partir de l'entrée standard et à modifier la valeur de l'objet dictionnaire. dict.items ()
est un itérateur qui renvoie une série de paires (clé, valeur)
dans le dictionnaire. Souvenons-nous-en.
Si vous voulez utiliser dans une chaîne littérale, vous devez échapper ou entourer l'extérieur avec
. Pour rendre la même chaîne contiguë, vous pouvez la multiplier par un multiple entier. Et
split ()` J'essaye de supprimer «» et de juger si le nombre d'éléments dans la liste retournée est impair afin de ne pas supprimer des «» irréguliers tels que «a» ». Il est calculé par «%», mais il peut être laissé tel quel même lorsque le quotient est égal à 0, donc en utilisant la fonction intégrée «divmod ()», le quotient et le reste sont calculés en même temps.
L'expression conditionnelle «A et B» est nouvelle pour moi, mais vous pouvez la voir en la regardant. La même chose est vraie pour «ou». Ce qui est important, c'est sa stratégie d'évaluation. Si «A et B» s'avère être «A == False», l'évaluation de l'expression se termine sans évaluer «B». Par conséquent, il est plus efficace de rendre "A" plus susceptible d'être "Faux" que "B". De même, ʻA ou B n'évalue pas
B s'il s'avère être ʻA == True
, alors écrivez une expression dans ʻA qui est plus susceptible d'être
True`.
En plus du traitement 26, supprimez le balisage de lien interne de MediaWiki de la valeur du modèle et convertissez-le en texte (Référence: [Markup Quick Reference](https://ja.wikipedia.org/wiki/Help :) % E6% 97% A9% E8% A6% 8B% E8% A1% A8)).
Puisqu'il y a 3 modèles, nous utiliserons des expressions régulières.
q27.py
"""
[[Le titre de l'article]]
[[Le titre de l'article|Caractère d'affichage]]
[[Le titre de l'article#Nom de la section|Caractère d'affichage]]
"""
import json
import re
import sys
from q26 import remove_emphasis
def main():
dic = json.loads(sys.stdin.read())
dic = remove_emphasis(dic)
dic = remove_link(dic)
print(json.dumps(dic, ensure_ascii=False, indent=4))
def remove_link(dic):
pat = re.compile(r"""
\[\[ # [[
([^|]+\|)* #Le titre de l'article|Pas ou répété
([^]]+) #Remplacez la partie qui correspond au caractère d'affichage pat par celle-ci
\]\] # ]]
""", re.VERBOSE)
for key, value in dic.items():
value = pat.sub(r'\2', value)
dic[key] = value
return dic
if __name__ == '__main__':
main()
Après avoir traité la question précédente, c'est un flux pour changer à nouveau la valeur du dictionnaire.
Vous pouvez écrire une chaîne littérale qui s'étend sur plusieurs lignes en la mettant entre guillemets triples. De plus, avec re.VERBOSE
, les espaces, les sauts de ligne et les commentaires sont ignorés dans l'expression régulière, mais c'est toujours difficile à voir ...
La partie de pat.sub (r '\ 2', valeur)
signifie remplacer la partie de valeur
qui correspond à pat
pargroupe (2)
de l'objet de correspondance. ..
En plus du traitement de> 27, supprimez autant que possible le balisage MediaWiki de la valeur du modèle et formatez les informations de base du pays.
Vous pouvez le faire avec Pandoc et pypandoc ... Si vous faites de votre mieux avec les expressions régulières, vous devez supprimer le balisage en surbrillance, les liens internes, les références de fichier, les liens externes, <ref>
, <br />
, {{0}}
, uniquement les expressions régulières. Je vais le mettre ...
basic_info = re.compile(r"\|(.+?)\s=\s(.+)")
emphasize = re.compile(r"('+){2,5}(.+?)('+){2,5}")
link_inner = re.compile(r"\[\[(.+?)\]\]")
file_ref = re.compile(r"\[\[Fichier:.+?\|.+?\|(.+?)\]\]")
ref = re.compile(r"<ref((\s.+?)>|(>.+?)</ref>)")
link_website = re.compile(r"\[.+?\]")
lang_template = re.compile(r"{{.+?\|.+?\|(.+?)}}")
br = re.compile(r"<.+?>")
space = re.compile(r"{{0}}")
Utilisez le contenu du modèle pour obtenir l'URL de l'image du drapeau. (Indice: MediaWiki API imageinfo .2F_ii) peut être appelée pour convertir la référence de fichier en URL)
Il semble que vous devriez demander https: // commons.wikimedia.org / w / api.php
avec divers paramètres (nom de fichier, etc.). Si vous google "info image de l'api du wiki multimédia" etc., les paramètres seront affichés. Vous pouvez utiliser ʻurllib` pour accéder à l'API avec le module standard Python. Dans la documentation Exemples d'utilisation, "Ce qui suit est un exemple de session pour utiliser la méthode GET pour obtenir une URL contenant des paramètres. Vous pouvez le faire en regardant la partie "est:".
Voici un exemple de réponse.
q29.py
import json
import sys
from urllib import request, parse
import re
baseinf = json.loads(sys.stdin.read())
url = 'https://commons.wikimedia.org/w/api.php'
params = {'action': 'query', 'prop': 'imageinfo', 'iiprop': 'url',
'format': 'json', 'titles': f'File:{baseinf["Image du drapeau"]}'}
req = request.Request(f'{url}?{parse.urlencode(params)}')
with request.urlopen(req) as res:
body = res.read()
# print(body['query']['pages']['347935']['imageinfo'][0]['url'])
print(re.search(r'"url":"(.+?)"', body.decode()).group(1))
!python q29.py < uk_baseinf.json
https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg
Étant donné que le fichier JSON renvoyé est compliqué, il est plus pratique de rechercher la partie de type URL. Pour une raison quelconque, body
était une chaîne d'octets, donc cela ne fonctionnait que si je le décodais.
re
json
str.startswith()
dict.items()
--ʻEt, ʻor
et sa stratégie d'évaluationurllib
Personnellement, ce chapitre a été douloureux. Est-ce que c'est comme la PNL de la prochaine fois?
Recommended Posts