Question

Je crée une application qui prendra une URL en entrée, récupérer sur le web et l'extrait contenu html de la page tout ce qui ne figure pas dans une balise . En d'autres termes, le contenu textuel de la page, comme on le voit par le visiteur à cette page. Cela inclut « masquage » tout ce <script></script> dans encapsulé, <style></style> et <!-- -->, étant donné que ces parties contiennent du texte qui ne sont pas enveloppé dans une balise (mais il est préférable de laisser seul).

J'ai construit ce regex:

(?:<(?P<tag>script|style)[\s\S]*?</(?P=tag)>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>)

Il sélectionne correctement tout le contenu que je veux ignorer, et ne laisse que le contenu du texte de la page. Cependant, cela signifie que ce que je veux extrait ne sera pas affiché dans la collection de match (j'utilise VB.Net dans Visual Studio 2010).

Y at-il un moyen de « inverser » la mise en correspondance d'un document tout comme celui-ci, de sorte que je reçois tous les matches sur les chaînes de texte qui sont laissés par le correspondant dans l'expression rationnelle ci-dessus?

Jusqu'à présent, ce que je faisais était d'ajouter une autre alternative à la fin, qui sélectionne « toute séquence qui ne contient pas de », ce qui signifie alors le texte de restes. Je l'ai appelé ce dernier bit dans un groupe de capture, et quand je itérer sur les matches, je vérifie la présence de texte dans le groupe « texte ». Cela fonctionne, mais je me demandais s'il était possible de le faire tout au long de regex et juste finissent avec des allumettes sur le texte brut.

Ceci est censé fonctionner de manière générique, sans connaître les balises spécifiques dans le code HTML. Il est censé extrait tout le texte . De plus, je dois préserver le code html d'origine pour que la page conserve tous ses liens et les scripts - i seulement besoin d'être en mesure d'extraire le texte afin que je puisse effectuer des recherches et des remplacements en son sein, sans crainte de « renommer » des balises, attributs ou variables de script etc (donc je ne peux pas faire un « remplacer par rien » sur tous les matches que je reçois, parce que même si je suis alors parti avec ce que je dois, il est embêtant de réinsérer ce retour dans les bons endroits de la entièrement le document fonctionnel).

Je veux savoir si cela est possible à l'aide d'expressions régulières (et je sais que sur le langage HTML Agility Pack et XPath, mais ne se sentent pas comme).

Toutes les suggestions?

Mise à jour: Voici la solution (basée sur les expressions régulières) J'ai fini avec: http://www.martinwardener.com/regex/ , mis en œuvre dans une application web de démonstration qui montrera à la fois les chaînes de regex actives avec un moteur d'essai qui vous permet d'exécuter l'analyse sur une page html en ligne, vous donnant analysez les temps et les résultats extraits (pour le lien, URL et texte parties individuellement -. ainsi que des vues où tous les matches de regex sont mises en évidence dans le document HTML complet)

Était-ce utile?

La solution 5

OK, donc voici comment je le fais:

En utilisant mon regex d'origine (avec le motif de recherche ajouté le texte brut, qui se trouve être tout texte qui reste après que les recherches tag sont effectuées):

(?:(?:<(?P<tag>script|style)[\s\S]*?</(?P=tag)>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>))|(?P<text>[^<>]*)

Puis, en VB.Net:

Dim regexText As New Regex("(?:(?:<(?<tag>script|style)[\s\S]*?</\k<tag>>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>))|(?<text>[^<>]*)", RegexOptions.IgnoreCase)
Dim source As String = File.ReadAllText("html.txt")
Dim evaluator As New MatchEvaluator(AddressOf MatchEvalFunction)
Dim newHtml As String = regexText.Replace(source, evaluator)

Le remplacement réel du texte se produit ici:

Private Function MatchEvalFunction(ByVal match As Match) As String
    Dim plainText As String = match.Groups("text").Value
    If plainText IsNot Nothing AndAlso plainText <> "" Then
        MatchEvalFunction = match.Value.Replace(plainText, plainText.Replace("Original word", "Replacement word"))
    Else
        MatchEvalFunction = match.Value
    End If
End Function

Voila. newHtml contient maintenant une copie exacte de l'original, à l'exception toutes les occurrences de « mot d'origine » dans la page (comme il est présenté dans un navigateur) est activé avec « mot de remplacement », et tout le code html et script est conservé intact. Bien sûr, on pourrait / aurait mis dans une routine de remplacement plus élaborée, mais cela montre le principe de base. Ceci est 12 lignes de code, y compris la déclaration de fonction et le chargement de code html etc. Je serais très intéressé à voir une solution parallèle, fait dans DOM etc pour la comparaison (oui, je sais que cette approche peut être jeté hors d'équilibre par certains occurrences de quelques balises imbriquées bizarreries - dans SCRIPT ré-écriture - mais les dégâts de ce seront encore très limitées, le cas échéant (voir quelques-uns des commentaires ci-dessus), et en général cela va faire le travail assez bien sacrément ).

Autres conseils

  

ce que je faisais était d'ajouter une autre alternative à la fin, qui sélectionne « toute séquence qui ne contient pas < ou > », ce qui signifie alors le texte de restes. Je l'ai appelé ce dernier bit dans un groupe de capture, et quand je itérer sur les matches, je vérifie la présence de texte dans le groupe « texte ».

C'est ce que l'on ferait normalement. Ou encore plus simple, remplacer chaque correspondance du motif de balisage avec et chaîne vide et ce que vous avez à gauche est la substance que vous cherchez.

  

Ce genre de travaux, mais il semble y avoir une chaîne ici et là qui est ramassé cela ne devrait pas être.

Eh bien oui, c'est parce que votre expression et regex en général est insuffisant pour analyser même HTML valide, et encore moins les horreurs qui sont là sur le web réel. Premier conseil à regarder, si vous voulez vraiment poursuivre cette approche futile:. Les valeurs d'attributs (ainsi que le contenu du texte en général) peut contenir un caractère > non échappés

Je voudrais une nouvelle fois suggérer les avantages de HTML Agility Pack.

ETA. Puisque vous semblez vouloir, voici quelques exemples de balisage qui ressemble à ça va trébucher votre expression

<a href=link></a> - unquoted
<a href= link></a> - unquoted, space at front matched but then required at back
<a href="~/link"></a> - very common URL char missing in group
<a href="link$!*'link"></a> - more URL chars missing in group
<a href=lïnk></a> - IRI
<a href
    ="link"> - newline (or tab)
<div style="background-image: url(link);"> - unquoted
<div style="background-image: url( 'link' );"> - spaced
<div style="background-image: u&#114;l('link');"> - html escape
<div style="background-image: ur\l('link');"> - css escape
<div style="background-image: url('link\')link');"> - css escape
<div style="background-image: url(\
'link')"> - CSS folding
<div style="background-image: url
('link')"> - newline (or tab)

et qui est juste complètement des marqueurs valides qui pas correspond le lien à droite, pas du balisage non valide possible, le balisage qui ne devrait pas, mais ne correspond un lien, ou l'un des nombreux problèmes avec l'autre technique de balisage de fractionnement du texte. Ceci est la pointe de l'iceberg.

Regex n'est pas fiable pour récupérer le contenu textuel des documents HTML. Regex ne peut pas gérer les balises imbriquées. En supposant un document ne contient aucune balise imbriquée, regex nécessite encore tous les balises sont bien fermées.

Si vous utilisez PHP, pour simplifier, je vous recommande vivement d'utiliser DOM (Document Object Model de) pour analyser / extraire des documents HTML. bibliothèque DOM existe généralement dans toutes les langues de programmation.

Si vous cherchez des pièces d'extrait d'une chaîne pas couvertes par un regex, vous pouvez remplacer simplement les parties que sont en correspondance avec une chaîne vide pour le même effet.

Notez que la seule raison pour laquelle ce travail pourrait parce que les balises que vous vous intéressez à la suppression, balises <script> et <style>, ne peuvent pas être imbriquées.

Cependant, il est pas rare pour une étiquette de <script> pour contenir le code à ajouter une autre balise programme <script>, auquel cas votre regex échouera. Il sera également échouer dans le cas où une étiquette est mal fermée.

Vous ne pouvez pas parse HTML avec des expressions régulières.

Parsing HTML avec des expressions régulières conduit à la tristesse.

Je sais que vous faites juste pour le plaisir, mais il y a tellement de paquets là-bas que ne fait l'analyse de la bonne façon, et le faire de manière fiable, et ont été testés.

Ne pas aller réinventer la roue, et ce faisant d'une manière qui est pratiquement garanti que vous frustrer sur la route.

Pour votre information,

Au lieu de Regex, avec JQuery, Il est possible de texte extrait seul à partir d'une balise html. Pour cela, vous pouvez utiliser le modèle suivant.

$("<div/>").html("#elementId").text()

Vous pouvez consulter cette jsFiddle

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top