我有这个正则表达式模式/[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,}/i
,用于从字符串中获取电子邮件地址。但是现在我只想获取所有电子邮件地址,这些地址是任意 HTML 元素属性的值,包括属性本身。看看我的例子,一切都应该很清楚:
<?php
$subject = 'abc dont@get.me 123 <input value="please@get.me">xyz';
$pattern = '/[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,}/i';
preg_match_all( $pattern, $subject, $matches );
var_dump( $matches );
将产生类似以下内容:
array(1) { [0]=> array(2) {
[0]=> string(11) "dont@get.me"
[1]=> string(13) "please@get.me"
} }
但我需要:
array(1) { [0]=> array(1) {
[0]=> string(13) "value="please@get.me""
} }
请注意,<input value="please@get.me">
只是一个例子。我需要一种可以处理具有"all"属性的"所有"HTML 元素的模式(我将"all"放在引号中以明确表示我知道在某些边缘情况下,该模式可能会失败,因为 HTML 不是常规的)并且:
<?php
$subject = "<br data-xyz=please@get.me /> dont@get.me <[tag] [attr]='[pre] andPlease@get.me [ap]'>";
preg_match_all( $pattern, $subject, $matches );
var_dump( $matches );
应该产生类似的东西:
array(1) { [0]=> array(2) {
[0]=> string(13) "data-xyz=please@get.me"
[1]=> string(13) "[attr]='[pre] andPlease@get.me [ap]'"
} }
老实说,我真的很不擅长正则表达式模式,所以我不知道如何实现它。希望有人可以帮助我解决这个问题!
编辑:比正则表达式之外的另一种解决方案也完全没问题!
要使用 DOMDocument 和 XPath 来执行此操作,您需要首先将文档加载为 HTML,然后使用 XPath 查找包含"@"符号的任何属性。
$subject = 'abc dont@get.me 123 <input value="please@get.me">
<span t="please@get.me2" u="please@get.me3" />
<span t="pleasedont get.me" />
<span t="@@@@">xyz';
$doc = new DOMDocument();
$doc->loadHTML($subject);
$xp = new DOMXPath($doc);
$possibilities = $xp->query('//*/@*[contains(., "@")]');
foreach ( $possibilities as $match ) {
if ( filter_var($match->nodeValue, FILTER_VALIDATE_EMAIL) ) {
echo $match->parentNode->nodeName." ".
$match->nodeName."=". $match->nodeValue.PHP_EOL;
}
}
(按照 Hayden 在评论中的建议进行编辑- 在打印出值之前,我已经更新了答案以验证它是一个电子邮件地址)。
将输出
input value=please@get.me
span t=please@get.me2
span u=please@get.me3
分解XPath...
//*/@*[contains(., "@")]
//*
查找任何节点 -/@*
表示任何属性 -[]
表达式之后是条件,因此仅返回与条件匹配的节点。 条件contains(., "@")
表示节点的文本必须包含@
。 所以放在一起,它说任何具有包含@
的属性的节点。 然后,$match->nodeValue
将输出值,$match->nodeName
将显示刚刚添加的属性名称$match->parentNode->nodeName
也将显示元素名称。
另请注意,此方法将返回同一元素但位于不同属性中的多个匹配项(例如please@get.me3
)。
您可以使用此正则表达式来确保匹配将包含电子邮件的任何标签名称,如果属性值的任何名称包含单词w
字符,
<w+.*?([w-]+=["']*s*(?:w+s*)*[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,}s*(?:['"]?(?:w+s*)*['"]?)?["']*).*?>
并捕获第一个分组模式的值。
这里的假设是标签名称和属性名称将包含来自w
的字符,但如果您想包含更多字符,例如包含-
或.
那么您需要在正则表达式中将w
更改为[w.-]
。
演示
编辑:
另一种方法,如果您不想从 group1 捕获数据,而是希望完全匹配仅包含属性名称和电子邮件,则可以将K
运算符与此正则表达式一起使用,
<w+.*?K[w-]+=["']*s*(?:w+s*)*[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,}s*(?:['"]?(?:w+s*)*['"]?)?["']*(?=.*?>)
包含所需文本的完全匹配演示