Les expressions régulières ont leurs limites quand il s'agit de l'analyse HTML. Comme beaucoup l'ont fait avant moi, je ferai référence à cette célèbre réponse.
Les Problèmes potentiels lorsque s'appuyant sur les Expressions Régulières
Par exemple, imaginez cette balise s'affiche dans le HTML avant de la partie qui doit être extraits:
<p attr="Hello 進撃の巨人!">This comes before the match</p>
De nombreux regexp solutions de trébucher sur ce, et de retourner une chaîne de caractères qui commence au milieu de cette ouverture en p
balise.
Ou envisager un commentaire à l'intérieur de la section HTML qui doit être assortie:
<!-- Next paragraph will display "Lorem ipsum." -->
Ou, certains lâche inférieur et supérieur signes apparaissent (disons dans un commentaire, ou une valeur d'attribut):
<!-- Next paragraph will display >-> << Lorem ipsum. >> -->
<p data-attr="->->->" class="myclass">
Ce que les regexes faire?
Ce sont juste des exemples... il y a d'innombrables autres situations qui posent des problèmes à l'expression régulière en fonction des solutions.
Il y a plus de moyens fiables pour analyser le code HTML.
Charger le code HTML dans un DOM
Je propose ici une solution basée sur le DOMDocument de l'interface, à l'aide de cet algorithme:
Obtenir le contenu du texte du document HTML et d'identifier les deux décalages, les deux sous-chaînes (début/fin) sont situés.
Ensuite, passez par le DOM nœuds de texte de garder la trace des décalages de ces nœuds de s'intégrer. Dans les nœuds où l'un des deux de délimitation des décalages sont croisés, un prédéfini délimiteur (|
) est inséré. Ce délimiteur ne devraient pas être présents dans la chaîne HTML. Par conséquent, il est doublé (||
, ||||
, ...) jusqu'à ce que cette condition est remplie;
Enfin diviser la représentation HTML par ce séparateur et d'en extraire la partie du milieu comme résultat.
Voici le code:
function extractBetween($html, $begin, $end) {
$dom = new DOMDocument();
// Load HTML in DOM, making sure it supports UTF-8; double HTML tags are no problem
$dom->loadHTML('<html><head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head></html>' . $html);
// Get complete text content
$text = $dom->textContent;
// Get positions of the beginning/ending text; exit if not found.
if (($from = strpos($text, $begin)) === false) return false;
if (($to = strpos($text, $end, $from + strlen($begin))) === false) return false;
$to += strlen($end);
// Define a non-occurring delimiter by repeating `|` enough times:
for ($delim = '|'; strpos($html, $delim) !== false; $delim .= $delim);
// Use XPath to traverse the DOM
$xpath = new DOMXPath($dom);
// Go through the text nodes keeping track of total text length.
// When exceeding one of the two offsets, inject a delimiter at that position.
$pos = 0;
foreach($xpath->evaluate("//text()") as $node) {
// Add length of node's text content to total length
$newpos = $pos + strlen($node->nodeValue);
while ($newpos > $from || ($from === $to && $newpos === $from)) {
// The beginning/ending text starts/ends somewhere in this text node.
// Inject the delimiter at that position:
$node->nodeValue = substr_replace($node->nodeValue, $delim, $from - $pos, 0);
// If a delimiter was inserted at both beginning and ending texts,
// then get the HTML and return the part between the delimiters
if ($from === $to) return explode($delim, $dom->saveHTML())[1];
// Delimiter was inserted at beginning text. Now search for ending text
$from = $to;
}
$pos = $newpos;
}
}
Vous serait-il appeler comme ceci:
// Sample input data
$html = '
<html>
<body>
<p>This comes before the match</p>
<p>Hey! Hello <em>進撃の巨人</em>!</p>
random code
random code
<p>Lorem <span>ipsum<span>. la la la</p>
<p>This comes after the match</p>
</body>
</html>
';
$begin = 'Hello 進撃の巨人!';
$end = 'Lorem ipsum.';
// Call
$html = extractBetween($html, $begin, $end);
// Output result
echo $html;
Sortie:
Hello <em>進撃の巨人</em>!</p>
random code
random code
<p>Lorem <span>ipsum<span>.
Vous trouverez ce code est également plus facile à entretenir que les regex alternatives.
Voir courir sur eval.dans.