expressão regular para extrair texto de html
-
05-07-2019 - |
Pergunta
Eu gostaria de extrair de uma página HTML geral, todo o texto (exibido ou não).
Eu gostaria de remover
- Quaisquer tags HTML
- Qualquer javascript
- Quaisquer estilos CSS
Existe uma expressão regular (uma ou mais) que conseguirá isso?
Solução
Você não pode realmente analisar o HTML com expressões regulares. É muito complexo. Re está <![CDATA[
seções corretamente. Além disso, alguns tipos de coisas comuns de HTML como <text>
Funcionará em um navegador como texto adequado, mas poderá confundir um RE ingênuo.
Você ficará mais feliz e mais bem -sucedido com um analisador HTML adequado. As pessoas do python costumam usar algo Linda sopa Para analisar HTML e tirar tags e scripts.
Além disso, os navegadores, por design, toleram HTML malformados. Então, você geralmente se vê tentando analisar o HTML, o que é claramente impróprio, mas funciona bem em um navegador.
Você pode analisar o HTML ruim com RE. Tudo o que requer é paciência e trabalho duro. Mas muitas vezes é mais simples usar o analisador de outra pessoa.
Outras dicas
Remova JavaScript e CSS:
<(script|style).*?</\1>
Remova tags
<.*?>
Precisava de uma solução regex (em php) isso retornaria o texto simples da mesma forma (ou melhor que) phpsimpledom, apenas muito mais rápido. Aqui está a solução que eu criei:
function plaintext($html)
{
// remove comments and any content found in the the comment area (strip_tags only removes the actual tags).
$plaintext = preg_replace('#<!--.*?-->#s', '', $html);
// put a space between list items (strip_tags just removes the tags).
$plaintext = preg_replace('#</li>#', ' </li>', $plaintext);
// remove all script and style tags
$plaintext = preg_replace('#<(script|style)\b[^>]*>(.*?)</(script|style)>#is', "", $plaintext);
// remove br tags (missed by strip_tags)
$plaintext = preg_replace("#<br[^>]*?>#", " ", $plaintext);
// remove all remaining html
$plaintext = strip_tags($plaintext);
return $plaintext;
}
Quando testei isso em alguns sites complicados (os fóruns parecem conter parte do HTML mais difícil para analisar), esse método retornou o mesmo resultado que o texto simples phpsimpledom, apenas muito, muito mais rápido. Ele também lidou com os itens da lista (tags Li) corretamente, onde o phpsimpledom não.
Quanto à velocidade:
- SimpleDom: 0,03248 seg.
- Regex: 0,00087 seg.
37 vezes mais rápido!
Contenção de fazer isso com expressões regulares é assustador. Você já considerou o XSLT? A expressão do XPath para extrair todos os nós de texto em um documento XHTML, menos script e conteúdo de estilo, seria:
//body//text()[not(ancestor::script)][not(ancestor::style)]
Usando a sintaxe Perl para definir as regexes, um começo pode ser:
!<body.*?>(.*)</body>!smi
Em seguida, aplicando a seguinte substituição ao resultado desse grupo:
!<script.*?</script>!!smi
!<[^>]+/[ \t]*>!!smi
!</?([a-z]+).*?>!!smi
/<!--.*?-->//smi
É claro que isso não formará as coisas bem como um arquivo de texto, mas retira todo o HTML (principalmente, há alguns casos em que pode não funcionar bem). Uma idéia melhor é usar um analisador XML em qualquer idioma que você estiver usando para analisar o HTML corretamente e extrair o texto disso.
A maneira mais simples de simples html (exemplo no python):
text = "<p>This is my> <strong>example</strong>HTML,<br /> containing tags</p>"
import re
" ".join([t.strip() for t in re.findall(r"<[^>]+>|[^<]+",text) if not '<' in t])
Retorna isto:
'This is my> example HTML, containing tags'
Aqui está uma função para remover as tags HTML mais complexas.
function strip_html_tags( $text )
{
$text = preg_replace(
array(
// Remove invisible content
'@<head[^>]*?>.*?</head>@siu',
'@<style[^>]*?>.*?</style>@siu',
'@<script[^>]*?.*?</script>@siu',
'@<object[^>]*?.*?</object>@siu',
'@<embed[^>]*?.*?</embed>@siu',
'@<applet[^>]*?.*?</applet>@siu',
'@<noframes[^>]*?.*?</noframes>@siu',
'@<noscript[^>]*?.*?</noscript>@siu',
'@<noembed[^>]*?.*?</noembed>@siu',
// Add line breaks before & after blocks
'@<((br)|(hr))@iu',
'@</?((address)|(blockquote)|(center)|(del))@iu',
'@</?((div)|(h[1-9])|(ins)|(isindex)|(p)|(pre))@iu',
'@</?((dir)|(dl)|(dt)|(dd)|(li)|(menu)|(ol)|(ul))@iu',
'@</?((table)|(th)|(td)|(caption))@iu',
'@</?((form)|(button)|(fieldset)|(legend)|(input))@iu',
'@</?((label)|(select)|(optgroup)|(option)|(textarea))@iu',
'@</?((frameset)|(frame)|(iframe))@iu',
),
array(
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
"\n\$0", "\n\$0", "\n\$0", "\n\$0", "\n\$0", "\n\$0",
"\n\$0", "\n\$0",
),
$text );
// Remove all remaining tags and comments and return.
return strip_tags( $text );
}
Se você estiver usando o PHP, tente o simples HTML DOM, disponível no SourceForge.
Caso contrário, o Google HTML2Text e você encontrará uma variedade de implementações para diferentes idiomas que basicamente usam uma série de expressões regulares para sugar toda a marcação. Cuidado aqui, porque as tags sem finais às vezes podem ser deixadas, bem como caracteres especiais como & (que é &).
Além disso, cuidado com os comentários e o JavaScript, pois achei particularmente irritante lidar com expressões regulares e por que geralmente prefiro deixar um analisador gratuito fazer todo o trabalho por mim.
Nem com certeza esta página Poderia ajudar.
Você não pode simplesmente usar o controle WebBrowser disponível com C#?
System.Windows.Forms.WebBrowser wc = new System.Windows.Forms.WebBrowser();
wc.DocumentText = "<html><body>blah blah<b>foo</b></body></html>";
System.Windows.Forms.HtmlDocument h = wc.Document;
Console.WriteLine(h.Body.InnerText);
string decode = System.Web.HttpUtility.HtmlDecode(your_htmlfile.html);
Regex objRegExp = new Regex("<(.|\n)+?>");
string replace = objRegExp.Replace(g, "");
replace = replace.Replace(k, string.Empty);
replace.Trim("\t\r\n ".ToCharArray());
then take a label and do "label.text=replace;" see on label out put
.
Eu acredito que você pode simplesmente fazer
document.body.innerText
Que retornará o conteúdo de todos os nós de texto no documento, visível ou não.
Edit (Olliej): suspirar Não esquece, isso só funciona no Safari e no IE, e não posso me incomodar em baixar um Firefox todas as noites para ver se ele existe no porta-malas:-/