php超强万能正则表达式!匹配网页所有a标签网址

2019年12月14日01:48:11

今天博客因为需要把文章所有外部链接替换成一个跳转页面的链接,因此要找出文章里所有a标签网址,然后进行替换。

看到网上有不少实现代码,但看了后发现都不够严密,因为这些代码都没有考虑到各种不标准的写法,如有的网址有单引号引住,有的是双引号引住,而有的连单双引号都没有,还有空格问题等等。

为此,我研究了一下正则表达式语法,自己写了一个“万能”正则表达式,能匹配到a标签里各种写法的网址,可谓是完全无视html语法了。

实例

html代码如:

<ul class="partner">    
<li><a href='http://www.vps5u.com/?ref=2170-2-5' target =_blank title='vps5u-1' >vps评测网-1</a></li>
<li><a href = 'http://www.vps5u.com/' target =_blank title='vps5u-2' >vps评测网-2</a></li>
<li><a href ="http://vps5u.com/" target =_blank title="vps5u-3" >vps评测网-3</a></li>
<li><a href=https://www.vps5u.com/ target =_blank title=vps5u-4 >vps评测网-4</a></li>
<li><a href=  http://www.vps5u.com/vps/index.html target =_blank  title= vps5u-5 >vps评测网-5</a></li>
</ul>

注意上面的各个网址写法,都不一样,各种不标准不严密的写法都有,我们写代码要做到能忽视这些问题。

我们现在要找出所有a标签的网址,我写的“万能”正则表达式如下:

$pattern = '/<a(?:[\s\S]*?)href\s*?=\s*[\'"]?(http(?:s?):\/\/([^\/]+)?([^\s\'"]+)?)[?:\s\'"]?[^>]*>([\s\S]*?)<\/a>/i';
preg_match_all($pattern, $data, $links); // $data 为上面的html代码字符串 $links 是一个存放匹配结果的多维数组
echo var_dump($links); //输出数组看匹配结果

输出的数组是这样:

array(5) {
  [0]=>
  array(5) {
    [0]=>
    string(95) "<a href='http://www.vps5u.com/?ref=2170-2-5' target =_blank title='vps5u-1' >vps评测网-1</a>"
    [1]=>
    string(84) "<a href = 'http://www.vps5u.com/' target =_blank title='vps5u-2' >vps评测网-2</a>"
    [2]=>
    string(79) "<a href ="http://vps5u.com/" target =_blank title="vps5u-3" >vps评测网-3</a>"
    [3]=>
    string(79) "<a href=https://www.vps5u.com/ target =_blank title=vps5u-4 >vps评测网-4</a>"
    [4]=>
    string(96) "<a href=  http://www.vps5u.com/vps/index.html target =_blank  title= vps5u-5 >vps评测网-5</a>"
  }
  [1]=>
  array(5) {
    [0]=>
    string(34) "http://www.vps5u.com/?ref=2170-2-5"
    [1]=>
    string(21) "http://www.vps5u.com/"
    [2]=>
    string(17) "http://vps5u.com/"
    [3]=>
    string(22) "https://www.vps5u.com/"
    [4]=>
    string(35) "http://www.vps5u.com/vps/index.html"
  }
  [2]=>
  array(5) {
    [0]=>
    string(13) "www.vps5u.com"
    [1]=>
    string(13) "www.vps5u.com"
    [2]=>
    string(9) "vps5u.com"
    [3]=>
    string(13) "www.vps5u.com"
    [4]=>
    string(13) "www.vps5u.com"
  }
  [3]=>
  array(5) {
    [0]=>
    string(14) "/?ref=2170-2-5"
    [1]=>
    string(1) "/"
    [2]=>
    string(1) "/"
    [3]=>
    string(1) "/"
    [4]=>
    string(15) "/vps/index.html"
  }
  [4]=>
  array(5) {
    [0]=>
    string(14) "vps评测网-1"
    [1]=>
    string(14) "vps评测网-2"
    [2]=>
    string(14) "vps评测网-3"
    [3]=>
    string(14) "vps评测网-4"
    [4]=>
    string(14) "vps评测网-5"
  }
}

从数组可清晰看到:

$links[0]是所有<a>标签。
$links[1]是所有href。
$links[2]是所有域名,如:www.vps5u.com。
$links[4]是所有a标签内容。

现在,我们就可以用php代码从这个数组里取出我们想要的东西了。例如要取出所有网址,就读取$links[1]里所有数值,用for循环即可,代码如下:

for ($x = 0; $x < count($links[1]); $x++) {  // count()函数是获得数组长度
	echo $links[1][$x]."-----";
}

用同样的方法,可获得其他内容,如所有a标签内容,所有域名。

本文运用的正则表达式语法解释:

  • \s
    空白字符
  • \S
    非空白字符
  • /fo+/
    正则表达式中包含“+”元字符,表示可以与目标对象中的 “fool”, “fo”, 或者 “football”等在字母f后面连续出现一个或多个字母o的字符串相匹配。
  • /eg*/
    正则表达式中包含“*”元字符,表示可以与目标对象中的 “easy”, “ego”, 或者 “egg”等在字母e后面连续出现零个或多个字母g的字符串相匹配。
  • /Wil?/
    正则表达式中包含“?”元字符,表示可以与目标对象中的 “Win”, 或者“Wilson”,等在字母i后面连续出现零个或一个字母l的字符串相匹配。