PHP + 正则表达式用于查找和替换<a>源代码中某个类中的任何标签



我在PHP变量中有一个HTML代码,我需要替换包含在另一个具有"obfuscate"类的标签中的每个链接,例如:

<div class="obfuscate foobar">
<strong>
<a href="https://example.com" class="randomclass" target="_BLANK">test</a>
</strong>
</div>

我需要将<a>标签替换为继承原始标签的所有内容的<span>,添加"akn-obf-link"类,并在"data-o"属性下通过base64_encode()传递混淆链接,如果链接具有目标_blank或"0",则具有"1"值的"data-b"属性。

在上面的例子中,<a>标签应该转换为:
<span class="akn-obf-link randomclass" data-o="aHR0cHM6Ly9leGFtcGxlLmNvbQ==" data-b="1">test</span>

<a>标签本身具有"obfuscate"类时,我已经有了这样做的代码,如果这可能有帮助:

$result = preg_replace_callback('#<a[^>]+((?<=s)href=("|')([^"']*)('|")[^>]+(?<=s)class=("|')[^'"]*(?<!w|-)obfuscate(?!w|-)[^'"]*("|')|(?<=s)class=("|')[^'"]*(?<!w|-)obfuscate(?!w|-)[^'"]*("|')[^>]+(?<=s)href=("|')([^"']*)('|"))[^>]*>(.*)</a>#miUs', function($matches) {
preg_match('#<a[^>]+(?<=s)class=["|\']([^\'"]+)["|\']#imUs',$matches[0],$matches_classes);
$classes = trim(preg_replace('/s+/',' ',str_replace('obfuscate','',$matches_classes[1])));
return '<span class="akn-obf-link'.($classes?' '.$classes:'').'" data-o="'.base64_encode($matches[3]?:$matches[10]).'" data-b="'.((strpos(strtolower($matches[0]),'_blank')!==false)?'1':'0').'">'.$matches[12].'</span>';
}, $code);

我需要相同的,但每当标签是在另一个标签有"obfuscate"类。

尝试用正则表达式解决这个问题将是痛苦和不安全的,原因在Stackoverflow上已经讨论过很多次了。

如果<div class="obfuscate">包含子节点,通常会发生什么<div>标签?

<div class="obfuscate foobar">
<div>Something</div>
<strong>
<a href="https://example.com" class="randomclass" target="_BLANK">test</a>
</strong>
</div>

这意味着你必须在正则中处理递归表达式这个正则表达式不能工作:

~<s*divs+
# The mandatory class anywhere in the tag:
(?=[^>]*bclass="(?<class>[^>]*?)")
# The rest of the attributes:
[^>]*>
# The content of the <div>, in an ungreedy way:
(.*?)
# The closing </div> tag:
<s*/s*divs*>~gsx

正如我们在这里看到的,它没有捕获div的全部内容。您需要一个平衡良好的正则表达式来解决这个问题这个问题。好,让我们假设您有带有漂亮双引号的class="..."属性就像经典爱情电影里演的那样。我们假设你没有孩子潜水。这意味着您可以捕获内部HTML,然后查找所有<a>具有相对复杂模式的标签,例如:

~# Declaration of all regex sub-routines:
(?(DEFINE)
# This sub-routine will match an attribute value with or without the quotes around it.
# So it will match "https://example.com" or 'https://example.com' (example with href)
# but also match my-class-name if we had something like <div class=my-class-name>
(?<attr_value_with_delim>(?:(?<delimiter>["']).*?(?:k<delimiter>)|[^"'=<>s]+))
)
# The regex pattern starts here:
# Match an opening <a> tag.
<s*as+
# All the attributes are optional as <a name="my-anchor"></a> is allowed.
# But you can remove the ? at the end if you want to make them mandatory.
# You may also add other attributes such as hreflang, type, data-*, etc.
(?=[^>]*bhrefs*=s*(?<href>g<attr_value_with_delim>))?
(?=[^>]*bids*=s*(?<id>g<attr_value_with_delim>))?
(?=[^>]*bclasss*=s*(?<class>g<attr_value_with_delim>))?
(?=[^>]*bnames*=s*(?<name>g<attr_value_with_delim>))?
(?=[^>]*btargets*=s*(?<target>g<attr_value_with_delim>))?
(?=[^>]*btitles*=s*(?<title>g<attr_value_with_delim>))?
(?=[^>]*bdownloads*=s*(?<download>g<attr_value_with_delim>))?
(?=[^>]*brels*=s*(?<rel>g<attr_value_with_delim>))?
[^>]*>
(.*?)
<s*/s*as*>~isxg

我已经做到了:https://regex101.com/r/ZSx69l/2

我想处理带有双引号、单引号和no的属性引号。我试图捕捉价值没有引号,但没有找到如何正确地做。没关系,因为preg_replace_callback()函数,然后可以用trim(..., '"'')或一个正则表达式。然后您就可以计算base64并将其重写为期望的输出。

但是这真的能解决所有格式错误的HTML代码吗?可能不会。

我会坚持PHP的DOMDocument拥有安全的东西。现在到处都安装了,而且与bug的风险相比,执行时间并不重要。

您可能不需要解析HTML页面的全部内容

使用防弹正则表达式获取所需内容

最新更新