Gorp.NET est une nouvelle bibliothèque pour créer des modèles réversibles pour extraire des données à partir de texte structuré, basé sur la base de code Salesforce Gorp existante.Dans cette publication, je vais parler un peu de la façon d'utiliser la bibliothèque pour analyser du texte structuré appelé
Gorp (l'un des exemples d'outils qui sont parfois appelés systèmes de
modèles à ingénierie inverse ).
Qu'est-ce qu'un
modèle réversible en général? Supposons que nous ayons un certain système qui nous permet de générer le texte dont nous avons besoin sur la base des données initiales que nous avons déterminées, selon des règles strictes définies par la syntaxe des modèles. Imaginons maintenant une tâche de sens opposé - nous avons un texte qui a une certaine intégrité structurelle qui pourrait être obtenue en utilisant un système basé sur les modèles de l'exemple précédent. Notre objectif est d'extraire de ce texte les données sources sur la base desquelles il a été constitué. Si nous essayons de trouver une certaine syntaxe généralisée pour résoudre ce problème, fournie à l'analyseur correspondant, qui analyse le texte d'entrée en éléments séparés, ce sera un exemple de syntaxe pour implémenter le concept de modèles réversibles.
Pourquoi ai-je décidé d'écrire spécifiquement sur
Gorp ? Le fait est que j'ai décidé de prendre ce système comme base pour finaliser mon propre projet - l'historique du projet lui-même, y compris certains détails de toutes les modifications que j'ai apportées au projet
Gorp d' origine, se trouve
dans l'article précédent . Ici, nous nous concentrerons précisément sur la partie technique, y compris en ce qui concerne l'utilisation d'une version modifiée du moteur. Pour plus de commodité, je continuerai à l'appeler
Gorp.NET , bien qu'en réalité ce ne soit pas une version de
Gorp qui ne soit pas portée sur .NET, mais seulement une version légèrement polie et finalisée de celui-ci, le tout dans le même Java. Une autre chose est que l'add-on au-dessus de la bibliothèque
Gorp elle-même (dans ma version) sous la forme d'une bibliothèque DLL gérée appelée
BIRMA.NET utilise son propre assemblage spécial - le très
Gorp.NET , que vous pouvez vous-même obtenir facilement si vous exécutez le texte source ( l'adresse de son référentiel est
https://github.com/S-presso/gorp/ ) via l'utilitaire
IKVM.NET .
Je noterai tout de suite que pour toutes sortes de tâches d'extraction de données à partir de n'importe quel texte structuré, les outils
Gorp.NET eux-mĂŞmes seront tout Ă fait suffisants pour
vous - au moins si vous avez une petite maîtrise de Java ou au moins comment appeler des méthodes à partir de modules Java externes dans vos projets sur .NET Framework, ainsi que divers types de bibliothèques JVM standard (j'y suis parvenu via le même
IKVM.NET , qui a cependant déjà le statut de projet non pris en charge). Eh bien, et que ferez-vous ensuite avec les données extraites - c'est, comme on dit, votre affaire personnelle.
Gorp et
Gorp.NET seuls ne fournissent qu'un cadre nu. Certains éléments de base pour le traitement ultérieur de toutes ces données contiennent le
BIRMA.NET susmentionné. Mais la description de la fonctionnalité
BIRMA.NET en
elle -
même est déjà un sujet pour une publication séparée (bien que j'aie déjà réussi à mentionner quelque chose dans ma précédente
revue comparative et historique des technologies BIRMA ). Ici, pour l'avenir, je vais me permettre une déclaration quelque peu audacieuse selon laquelle la technologie utilisée pour décrire les modèles réversibles utilisés dans
Gorp.NET (et, par conséquent, dans
BIRMA.NET ) est quelque peu unique parmi d'autres métiers de ce type (je dis " artisanat », car les grandes entreprises ne m'ont pas encore vu en quelque sorte promouvoir leurs propres cadres à ces fins - enfin, peut-être seulement
Salesforce lui-même avec sa mise en œuvre originale de
Gorp ).
Pour la divulgation la plus complète du concept et des aspects techniques qui sous-tendent le système de description de modèle utilisé dans Gorp, je laisse juste ici un lien vers la
documentation originale en anglais . Tout ce qui y est indiqué, vous pouvez appliquer en toute sécurité en relation avec
Gorp.NET . Et maintenant, je vais vous parler un peu de l’essence.
Ainsi, la description du modèle est une sorte de document texte (peut-être même présenté comme une seule grande ligne, qui peut être transmise à la méthode de traitement correspondante). Il se compose de trois parties contenant des descriptions séquentielles des trois entités les plus importantes:
modèles ,
modèles et
échantillons (extraits).
Les blocs de niveau le plus bas sont les
modèles - ils ne peuvent être composés que d'expressions régulières et de références à d'autres modèles. Le niveau suivant de la hiérarchie est occupé par les
modèles , dont la description contient également des liens vers des modèles, qui peuvent également être nommés, ainsi que des inclusions sous forme de littéraux de texte, des liens vers des modèles imbriqués et des extracteurs. Il y a aussi
des modèles paramétriques que je n'aborderai pas pour le moment (dans la documentation source il y a peu d'exemples de leur utilisation). Enfin, et enfin, il existe des
exemples qui spécifient des règles syntaxiques spécifiques qui associent des
modèles nommés à des occurrences spécifiques du texte source.
Si je comprends bien, l'objectif initial que les créateurs de
Gorp s'étaient fixé était d'
analyser les séquences de données contenues dans les fichiers de rapport (ou fichiers journaux). Prenons un exemple simple d'une application spécifique du système.
Supposons que nous ayons un rapport contenant la ligne suivante:
<86> 2015-05-12T20: 57: 53.302858 + 00: 00 10.1.11.141 RealSource: "10.10.5.3"
Composons un exemple de modèle pour l'analyser à l'aide des outils
Gorp :
pattern %phrase \\S+
pattern %num \\d+\n
pattern %ts %phrase
pattern %ip %phrase
extract interm {
template <%num>$eventTimeStamp(%ts) $logAgent(%ip) RealSource: "$logSrcIp(%ip)"
}
Notez que le bloc d'affectation de modèle est même omis ici, car tous les modèles nécessaires sont déjà inclus dans la sélection finale. Tous les modèles utilisés ici sont nommés, leur contenu est indiqué entre parenthèses après leur nom. Par conséquent, un ensemble de données texte sera créé avec les noms
eventTimeStamp ,
logAgent et
logSrcIp .
Nous allons maintenant écrire un programme simple pour extraire les données nécessaires. Supposons que le modèle que nous avons créé soit déjà contenu dans un fichier appelé
extractions.xtr .
import com.salesforce.gorp.DefinitionReader; import com.salesforce.gorp.ExtractionResult; import com.salesforce.gorp.Gorp;
Un autre exemple d'un modèle d'analyse simple:
# Patterns
pattern %num \d+
pattern %hostname [a-zA-Z0-9_\-\.]+
pattern %status \w+
# Templates
@endpoint $srcHost(%hostname): $srcPort(%num)
# Extraction
extract HostDefinition {
template @endpoint $status(%status)
}
Eh bien, je pense que le point est clair. Il ne sera pas non plus inutile de mentionner que pour la méthode d'
extraction , il existe également une définition avec deux paramètres d'entrée, dont le second a un type logique. Si vous le définissez sur
true , alors, lorsqu'elle est exécutée, la méthode itérera sur tous les ensembles de données potentiels - jusqu'à ce qu'elle en rencontre un approprié (vous pouvez également remplacer l'appel de méthode par
extractSafe - déjà sans le deuxième paramètre). La valeur par défaut est
false , et la méthode peut "jurer" à la différence entre les données d'entrée et le modèle utilisé.
Je note en mĂŞme temps que
Gorp.NET a également introduit une nouvelle implémentation étendue de la méthode d'
extraction : il existe maintenant une version avec deux paramètres suivants d'un type logique. À l'aide d'un appel abrégé à la
vue extractAllFound , nous les définissons tous les deux sur true par défaut. La valeur positive du troisième paramètre nous donne encore plus de possibilités de variations: nous pouvons désormais analyser le texte avec toutes les inclusions de caractères arbitraires dans les intervalles entre les échantillons souhaités, déjà structurés (contenant des ensembles de données extraites).
Le moment est donc venu de répondre à la question: qu'est-ce qui peut être unique dans cette modification de la version de base de
Gorp , outre l'extension de la méthode d'extraction?
Le fait est que lorsque j'ai créé il y a plusieurs années déjà une sorte de mon propre outil pour extraire les données requises du texte (qui était également basé sur le traitement de certains modèles avec leur propre syntaxe spécifique), il fonctionnait sur des principes légèrement différents. Leur principale différence avec l'approche mise en œuvre dans
Gorp et tous les cadres dérivés est que chaque élément de texte à extraire a été défini simplement en répertoriant ses bordures gauche et droite (chacune pouvant à son tour faire partie de l'élément lui-même ou simplement le séparer de tout le texte suivant ou précédent). En même temps, en fait, dans le cas général, la structure du texte source lui-même n'a pas été analysée, comme c'est le cas dans
Gorp , mais seules les pièces nécessaires ont été choisies. Quant au contenu du texte qui les entoure, il n'aurait pu succomber à aucune analyse structurelle (il pourrait bien s'agir de jeux de caractères incohérents).
Est-il possible d'obtenir un effet similaire dans
Gorp ? Dans sa version initiale - peut-être pas (corrigez-moi si je me trompe). Si nous écrivons simplement une expression comme
(. *) , Suivie immédiatement par le masque pour spécifier la bordure gauche de l'élément suivant à rechercher, puis en utilisant le quantificateur «cupidité», tout le texte suivant sera capturé. Et nous ne pouvons pas utiliser des habitués avec une syntaxe «non gourmande» dans les implémentations
Gorp existantes.
Gorp.NET vous permet de contourner ce problème en douceur en introduisant deux types spéciaux de modèles -
(% all_before) et
(% all_after) . Le premier d'entre eux, en fait, est une alternative Ă la version "non gourmande"
(. *) , AdaptĂ©e Ă la compilation de vos propres modèles. Quant Ă
(% all_after) , il examine également le texte source jusqu'à la première occurrence de la partie suivante du modèle décrit - mais en s'appuyant déjà sur le résultat de la recherche du modèle précédent. Tout ce qui se trouve entre eux tombera également dans la sous-chaîne extractible de l'élément courant. Dans un sens
(% all_after) «regarde en arrière» et
(% all_before) , au contraire, «regarde en avant». Je note qu'un analogue unique pour
(% all_before) dans la première version de
BIRMA Ă©tait la bordure gauche manquante dans la description de l'Ă©lĂ©ment, et analogue Ă
(% all_after) , respectivement, était un vide au lieu de la bordure droite. Si les deux limites ne sont pas définies lors de la description de l'élément suivant, l'analyseur capture évidemment tout le texte suivant! Cependant, tout cela alors la mise en œuvre de
BIRMA a maintenant une signification purement historique (vous pouvez en lire un peu plus
dans mon rapport de l'époque ).
Texte masquéLes codes sources n'ont jamais été présentés nulle part en raison de leur qualité extrêmement faible - en vérité, ils pourraient servir de monument à la mauvaise conception des systèmes logiciels.
Examinons les fonctionnalités de l'utilisation des modèles de service
(% all_before) et
(% all_after) en utilisant l'exemple de la tâche d'extraction de données utilisateur spécifiques à partir d'un site Web spécifique. Nous analyserons le site Amazon, et plus précisément cette page:
https://www.amazon.com/B06-Plus-Bluetooth-Receiver-Streaming/product-reviews/B078J3GTRK/ ).
Texte masquéUn exemple est tiré d'une tâche de test pour un poste vacant de développeur spécialisé dans l'analyse de données, envoyée par mon entreprise, qui, malheureusement, n'a pas répondu à la proposition de solution au problème. Certes, ils m'ont seulement demandé de décrire le processus général de la solution - sans fournir d'algorithme spécifique, et en réponse, j'ai déjà essayé de me référer aux modèles Gorp, alors que mes propres extensions à l'époque n'existaient que, comme on dit, «sur papier» ".
Par curiosité, je vais me permettre de citer un fragment de ma lettre de réponse, qui, apparemment, est la première mention de
Gorp.NET , bien que de nature privée.
«Pour rendre la liste ci-dessus d'expressions régulières utilisées par moi pour résoudre ce problème plus visuelle, j'ai compilé un modèle prêt à l'emploi sur sa base (attaché à la lettre), qui peut être utilisé pour extraire toutes les données nécessaires en appliquant mon propre développement de nature plus universelle, juste conçu pour résoudre ce type de problème. Son code est basé sur le projet
github.com/salesforce/gorp , et sur la même page il y a une description générale des règles de compilation de tels modèles. D'une manière générale, une telle description implique en soi l'affectation à la fois d'expressions régulières concrètes et de la logique de leur traitement. Le point le plus difficile ici est que pour chaque échantillon de données, nous devons décrire complètement par le biais de réguliers la structure entière du texte qui les contient, et pas seulement les éléments eux-mêmes (comme cela pourrait être fait lors de l'écriture de notre propre programme qui recherche séquentiellement dans une boucle, comme je décrit précédemment). "
La tâche initiale était de collecter les données suivantes à partir de la page ci-dessus:
- Identifiant
- Évaluation
- Titre de la revue
- La date
- Texte
Eh bien, je vais maintenant vous donner un modèle compilé par moi qui vous permet de terminer cette tâche rapidement et efficacement. Je pense que le sens général devrait être assez évident - peut-être que vous-même pouvez proposer une solution plus concise.
Texte masquéC'est sur cet exemple que, en général, j'ai débogué la fonctionnalité de mes propres extensions pour Gorp (déjà sans aucun objectif d'emploi, mais plutôt basé sur l'idéologie de «Proof of Concept»).
pattern %optspace ( *)
pattern %space ( +)
pattern %cap_letter [AZ]
pattern %small_letter [az]
pattern %letter (%cap_letter|%small_letter)
pattern %endofsentence (\.|\?|\!)+
pattern %delim (\.|\?|\!\,|\:|\;)
pattern %delim2 (\(|\)|\'|\")
pattern %word (%letter|\d)+
pattern %ext_word (%delim2)*%word(%delim)*(%delim2)*
pattern %text_phrase %optspace%ext_word(%space%ext_word)+
pattern %skipped_tags <([^>]+)>
pattern %sentence (%text_phrase|%skipped_tags)+(%endofsentence)?
pattern %start <div class=\"a-fixed-right-grid view-point\">
pattern %username_start <div class=\"a-profile-content\"><span class=\"a-profile-name\">
pattern %username [^\s]+
pattern %username_end </span>
pattern %user_mark_start <i data-hook=\"review-star-rating\"([^>]+)><span class=\"a-icon-alt\">
pattern %user_mark [^\s]+
pattern %user_mark_end ([^<]+)</span>
pattern %title_start data-hook=\"review-title\"([^>]+)>(%skipped_tags)*
pattern %title [^<]+
pattern %title_end </span>
pattern %span class <span class=\"[^\"]*\">
pattern %date_start <span data-hook="review-date"([^>]+)>
pattern %date ([^<]+)
pattern %date_end </span>
pattern %content_start <span data-hook=\"review-body\"([^>]+)>(%skipped_tags)*
pattern %content0 (%sentence)+
pattern %content (%all_after)
pattern %content_end </span>
template @extractUsernameStart (%all_before)%username_start
template @extractUsername $username(%username)%username_end
template @extractUserMarkStart (%all_before)%user_mark_start
template @extractUserMark $user_mark(%user_mark)%user_mark_end
template @extractTitleStart (%all_before)%title_start
template @extractTitle $title(%title)%title_end
template @extractDateStart (%all_before)%date_start
template @extractDate $date(%date)%date_end
template @extractContentStart (%all_before)%content_start
template @extractContent $content(%content)%content_end
extract ToCEntry {
template @extractUsernameStart@extractUsername@extractUserMarkStart@extractUserMark@extractTitleStart@extractTitle@extractDateStart@extractDate@extractContentStart@extractContent
}
C'est probablement tout pour aujourd'hui. À propos des outils tiers que j'ai mis en œuvre, dans lesquels ce cadre a déjà été pleinement impliqué, je peux vous dire une autre fois.