web-dev-qa-db-fra.com

SQL - Supprimer toutes les balises HTML d'une chaîne

Dans mon jeu de données, j'ai un champ qui stocke du texte balisé en HTML. Le format général est le suivant:

<html><head></head><body><p>My text.</p></body></html>

Je pourrais tenter de résoudre le problème en procédant comme suit:

REPLACE(REPLACE(Table.HtmlData, '<html><head></head><body><p>', ''), '</p></body></html>')

Cependant, ce n'est pas une règle stricte car certaines entrées enfreignent les normes W3C et n'incluent pas <head> tags par exemple. Pire encore, il pourrait manquer des balises de fermeture. Il me faudrait donc inclure la fonction REPLACE pour chaque balise d'ouverture et de fermeture qui pourrait exister.

REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
    Table.HtmlData,
    '<html>', ''),
    '</html>', ''),
    '<head>', ''),
    '</head>', ''),
    '<body>', ''),
    '</body>', ''),
    '<p>', ''),
    '</p>', '')

Je me demandais s'il y avait une meilleure façon d'accomplir cela que d'utiliser plusieurs fonctions imbriquées REPLACE. Malheureusement, les seules langues dont je dispose dans cet environnement sont SQL et Visual Basic (pas .NET).

12
dkmann
DECLARE @x XML = '<html><head></head><body><p>My text.</p></body></html>'

SELECT t.c.value('.', 'NVARCHAR(MAX)')
FROM @x.nodes('*') t(c)

Mise à jour - Pour les chaînes avec des balises non fermées:

DECLARE @x NVARCHAR(MAX) = '<html><head></head><body><p>My text.<br>More text.</p></body></html>'

SELECT x.value('.', 'NVARCHAR(MAX)')
FROM (
    SELECT x = CAST(REPLACE(REPLACE(@x, '>', '/>'), '</', '<') AS XML)
) r
8
Devart

Si le HTML est bien formé, il n'est pas nécessaire d'utiliser replace pour analyser XML.
Il suffit de le convertir ou de le convertir en un type XML et d'obtenir la ou les valeurs.

Voici un exemple pour sortir le texte de toutes les balises:

declare @htmlData nvarchar(100) = '<html>
<head>
</head>
<body>
   <p>My text.</p>
   <p>My other text.</p>
</body>
</html>';

select convert(XML,@htmlData,1).value('.', 'nvarchar(max)');

select cast(@htmlData as XML).value('.', 'nvarchar(max)');

Notez qu'il y a une différence dans la sortie des espaces entre cast et convert.

Pour obtenir uniquement le contenu d'un nœud spécifique, la syntaxe XQuery est utilisée. (XQuery est basé sur la syntaxe XPath)

Par exemple:

select cast(@htmlData as XML).value('(//body/p/node())[1]', 'nvarchar(max)');

select convert(XML,@htmlData,1).value('(//body/p/node())[1]', 'nvarchar(max)');

Résultat : My text.

Bien sûr, cela suppose toujours un XML valide.
Si, par exemple, une balise de fermeture est manquante, cela soulèvera un XML parsing Erreur.

Si le HTML n'est pas bien formé en XML, alors on pourrait utiliser PATINDEX & SUBSTRING pour obtenir la première balise p. Et puis transformez cela en un type XML pour obtenir la valeur.

select cast(SUBSTRING(@htmlData,patindex('%<p>%',@htmlData),patindex('%</p>%',@htmlData) - patindex('%<p>%',@htmlData)+4) as xml).value('.','nvarchar(max)');

ou via une méthode récursive funky:

declare @xmlData nvarchar(100);
WITH Lines(n, x, y) AS (
  SELECT 1, 1, CHARINDEX(char(13), @htmlData)
  UNION ALL
  SELECT n+1, y+1, CHARINDEX(char(13), @htmlData, y+1) FROM Lines
  WHERE y > 0
)
SELECT @xmlData = concat(@xmlData,SUBSTRING(@htmlData,x,IIF(y>0,y-x,8)))
FROM Lines
where PATINDEX('%<p>%</p>%', SUBSTRING(@htmlData,x,IIF(y>0,y-x,10))) > 0
order by n;

select 
@xmlData as xmlData, 
convert(XML,@xmlData,1).value('(/p/node())[1]', 'nvarchar(max)') as FirstP;
8
LukStorms

Créez d'abord une fonction définie par l'utilisateur qui supprime le code HTML comme suit:

CREATE FUNCTION [dbo].[udf_StripHTML] (@HTMLText VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
     BEGIN
         DECLARE @Start INT;
         DECLARE @End INT;
         DECLARE @Length INT;
         SET @Start = CHARINDEX('<', @HTMLText);
         SET @End = CHARINDEX('>', @HTMLText, CHARINDEX('<', @HTMLText));
         SET @Length = (@End - @Start) + 1;
         WHILE @Start > 0
               AND @End > 0
               AND @Length > 0
             BEGIN
                 SET @HTMLText = STUFF(@HTMLText, @Start, @Length, '');
                 SET @Start = CHARINDEX('<', @HTMLText);
                 SET @End = CHARINDEX('>', @HTMLText, CHARINDEX('<', @HTMLText));
                 SET @Length = (@End - @Start) + 1;
             END;
         RETURN LTRIM(RTRIM(@HTMLText));
     END;
GO

Lorsque vous essayez de le sélectionner:

SELECT dbo.udf_StripHTML([column]) FROM SOMETABLE

Cela devrait vous éviter d'avoir à utiliser plusieurs instructions de remplacement imbriquées.

Crédit et informations complémentaires: http://blog.sqlauthority.com/2007/06/16/sql-server-udf-user-defined-function-to-strip-html-parse-html-no-regular -expression /

3
cp50

Une autre solution, juste pour montrer une astuce pour remplacer plusieurs valeurs d'une table (facile à maintenir !!!) dans une seule instruction:

--ajoutez tous les modèles de remplacement ici:

CREATE TABLE ReplaceTags (HTML VARCHAR(100));
INSERT INTO ReplaceTags VALUES
 ('<html>'),('<head>'),('<body>'),('<p>'),('<br>')
,('</html>'),('</head>'),('</body>'),('</p>'),('</br>');
GO

--Cette fonction effectuera le "truc"

CREATE FUNCTION dbo.DoReplace(@Content VARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
    SELECT @Content=REPLACE(@Content,HTML,'')
    FROM ReplaceTags;

    RETURN @Content;
END
GO

--Tous les exemples que j'ai trouvés dans votre question et dans les commentaires

DECLARE @content TABLE(Content VARCHAR(MAX));
INSERT INTO @content VALUES
 ('<html><head></head><body><p>My text.</p></body></html>')
,('<html><head></head><body><p>My text.<br>More text.</p></body></html>')
,('<html><head></head><body><p>My text.<br>More text.</p></body></html>')
,('<html><head></head><body><p>My text.</p></html>');

--c'est la requête réelle

SELECT dbo.DoReplace(Content) FROM @content;
GO

--Nettoyer

DROP FUNCTION dbo.DoReplace;
DROP TABLE ReplaceTags;

MISE À JOUR

Si vous ajoutez une valeur de remplacement à la table de modèles, vous pouvez même utiliser des valeurs différentes comme remplacements, comme remplacer un <br> avec un saut de ligne réel ...

1
Shnugo

Vous mentionnez que le XML n'est pas toujours valide, mais contient-il toujours les balises <p> et </p>?

Dans ce cas, les éléments suivants fonctionneraient:

SUBSTRING(Table.HtmlData, 
    CHARINDEX('<p>', Table.HtmlData) + 1, 
    CHARINDEX('</p>', Table.HtmlData) - CHARINDEX('<p>', Table.HtmlData) + 1)

Pour trouver toutes les positions d'un <p> dans un HTML, il y a déjà un bon article ici: https://dba.stackexchange.com/questions/41961/how-to-find-all-positions-of- une chaîne dans une autre chaîne

Alternativement, je suggère d'utiliser Visual Basic, comme vous l'avez mentionné, c'est également une option.

0
Zsuzsa

SSMS 2017 a une fonctionnalité intégrée pour rechercher et remplacer par une expression régulière, mais je n'ai pas compris comment le faire avec une requête.

Certaines des réponses ici utilisent XML parsing, mais cette méthode ne fonctionne que pour les chaînes html simples et renvoie une erreur: XML parsing: line 5, character 6, end tag does not match start tag avec des chaînes html complexes, comme celle-ci, nous obtenons d'un système de helpdesk.

'<!--html--><div>
<div>A sentence.</div>
<br>
<div>Thank you!</div>
</div>'

J'ai fini par utiliser CTE récursif pour faire le travail. Voici ma fonction.

ALTER FUNCTION [dbo].[udf_removeHtmlTags] 
(
    @html NVARCHAR(MAX)
)  
    RETURNS NVARCHAR(MAX)
AS  
BEGIN 
    DECLARE @returnHtml NVARCHAR(MAX)

    ;with cte(id, html) as (
        select 1, STUFF(@html, CHARINDEX('<', @html), CHARINDEX('>', @html)-CHARINDEX('<', @html)+1, '')
        union all
        select id + 1, STUFF(html, CHARINDEX('<', html), CHARINDEX('>', html)-CHARINDEX('<', html)+1, '')
        from cte
        where html like '%<%>%'
    )
    select top 1 @returnHtml = html 
    from cte
    order by id desc

    RETURN @returnHtml
END
0
Weihui Guo

C'est juste un exemple. Vous pouvez utiliser ceci dans un script pour supprimer toutes les balises html:

 DECLARE @VALUE VARCHAR(MAX),@start INT,@end int,@remove varchar(max)
SET @VALUE='<html itemscope itemtype="http://schema.org/QAPage">
<head>

<title>sql - Converting INT to DATE then using GETDATE on conversion? - Stack Overflow</title>
<html>
</html>
'

set @start=charindex('<',@value)
while @start>0
begin
set @end=charindex('>',@VALUE)

set @remove=substring(@VALUE,@start,@end)
set @value=replace(@value,@remove,'')
set @start=charindex('<',@value)
end
print @value
0
Ranjana Ghimire

C'est le moyen le plus simple.

DECLARE @str VARCHAR(299)

SELECT @str = '<html><head></head><body><p>My text.</p></body></html>'

SELECT cast(@str AS XML).query('.').value('.', 'varchar(200)')
0
StackUser