当你在这里搜索URL时,有必要弄清楚是什么使它与上下文有所不同。
一般不包含空格,往往它封闭在某种报价或括号,使得它很容易的识别网址:
URL -- surrounded by whitespace --
"URL" -- quoted like in your example --
<URL> -- the class way of marking an URL --
这将使描述网址为下面的表达式
~(?P<url>[^\s<>"\']+)~
运行仅此就在您的示例文件已经做了某种这里工作的,它提供了13笔,其中12个是假阳性,但网址是:
#1 h1, #2 Test, #3 /h1,
#4 some, #5 html, #6 elements,
#7 a, #8 href=,
#9 www.example.com/test?abc=xxxx&def=yyyy&ghi=zzzzz,
#10 /a, #11 more, #12 html,
#13 elements.
幸运的是,你有更多的标准什么URL在你的情况下,所以这可以被添加。例如查询字符串必须在那里。它,它必须包含一个问号:
~(?P<url>[^\s<>"\'?]+\?[^\s<>"\'?]+)~
问号已被排除在允许的字符组,该组有两个被分割和问号,现在在中间需要。由于URL只能包含一次,所以这非常好。
现在只剩下一场比赛了。
www.example.com/test?abc=xxxx&def=yyyy&ghi=zzzzz
这是因为现在实在是太难看了,让我们写这更好的了下来:
~
(?(DEFINE)
(?<Chars> [^\s<>"\'?]+)
)
(?P<url> (?&Chars) \? (?&Chars))
~x
而这还不是又不失结束,因为你清楚地知道你在找什么,那abc=(.*?)&
部分。这有点不对,价值被&
终止,所以它不能包含它。与问号,这应该被放入它的模式,因为这样的值可以在URL的结尾为好,下面剩下的就作出可选:
~
(?(DEFINE)
(?<Chars> [^\s<>"\'?]+)
(?<Val> [^\s<>"\'?&]*)
)
(?P<url> (?&Chars) \? (?&Chars)? abc = (?P<value> (?&Val)) &? (?&Chars)? )
~x
所以只要你”对一个特定的URL感兴趣,使用正则表达式做这件事相对简单,但是:文档中的URL不能被标准化,并且其他类似的问题可能会发生。因此,通常最值得先对URL进行规范化处理,然后继续处理。例如,在查询信息部分查看URL参数时。
在写这篇文章的时候我实际上认为,从文档中获取URL的过滤应该独立于解析方法。正如其他用户所评论的,您可能想使用HTML解析器而不是正则表达式。或者你也许想要两个。
我们先来关注正则表达式场景。这是一个正确的URL解析正则表达式。作为预防措施,网址的最大长度已经从6到256个字节的限制:
$matcher = new PregStringMatcher('~([^\s<>"\']{6,256})~');
$segments = new StringMatcherIterator($matcher, $input);
$all = new DecoratingIterator($segments, 'Net_URL2');
$urls = new CallbackFilterIterator($all, function (Net_URL2 $url) {
return isset($url->getQueryVariables()['abc']);
});
foreach ($urls as $url) {
echo $url->getQueryVariables()['abc'], ' - ', $url, "\n";
}
此代码使用的类从IteratorGarden和梨Net_URL2
。输出是(我修改你的HTML示例一点点):
xxxx - www.example.com/test?%61%62%63=xxxx&def=yyyy&ghi=zzzzz
如果你现在考虑切换到HTML解析器,你不需要改变太多,代码。由于过滤逻辑是一样的,所有你需要的是Exchange中的基础Traversable的:
$doc = new DOMDocument();
$saved = libxml_use_internal_errors(true);
$doc->loadHTML($input);
libxml_use_internal_errors($saved);
$attributes = (new DOMXPath($doc))->query('//@href');
$segments = new DecoratingIterator($attributes, function (DOMAttr $attr) {
return $attr->nodeValue;
});
的其余代码可以保持不变,结果在这种情况下是一样的。所以我希望这些检查是有用的,并展示如何处理正则表达式以及如何添加更多检查的一些方法。
这里的代码示例与正则表达式和HTML解析器完全兼容。 URL过滤器在两者中都是相同的:
<?php
/**
* preg_match: return entire url by matching a word inside it
*
* @link http://stackoverflow.com/a/29481904/367456
*/
require __DIR__ . '/vendor/autoload.php';
$input = <<<BUFFER
<h1> Test </h1>
<some html elements>
<a href="www.example.com/test?%61%62%63=xxxx&def=yyyy&ghi=zzzzz"></a>
<more html elements>
BUFFER;
// Regex based retrieval
$matcher = new PregStringMatcher('~([^\s<>"\']{6,256})~');
$segments = new StringMatcherIterator($matcher, $input);
$all = new DecoratingIterator($segments, 'Net_URL2');
$urls = new CallbackFilterIterator($all, function (Net_URL2 $url) {
return isset($url->getQueryVariables()['abc']);
});
foreach ($urls as $url) {
echo $url->getQueryVariables()['abc'], ' - ', $url, "\n";
}
// DOMDocument based retrieval
$doc = new DOMDocument();
$saved = libxml_use_internal_errors(true);
$doc->loadHTML($input);
libxml_use_internal_errors($saved);
$attributes = (new DOMXPath($doc))->query('//@href');
$segments = new DecoratingIterator($attributes, function (DOMAttr $attr) {
return $attr->nodeValue;
});
$all = new DecoratingIterator($segments, 'Net_URL2');
$urls = new CallbackFilterIterator($all, function (Net_URL2 $url) {
return isset($url->getQueryVariables()['abc']);
});
foreach ($urls as $url) {
echo $url->getQueryVariables()['abc'], ' - ', $url, "\n";
}
你甚至尝试过什么吗? – Rizier123
是的。第一个是提取“xxx”,所以我的正则表达式是'/abc=(.*?)\&/'。但现在我需要获取整个网址。 – typescript
^在您的问题中添加您的尝试 – Rizier123