从HTML中提取所有链接和锚文本的正则表达式

6
我希望有一个或多个正则表达式可以实现以下功能:
1)获取大型页面的HTML代码。
2)查找所有链接中包含的URL,例如:
<a href="http://example1.com">Test 1</a>
<a class="foo" id="bar" href="http://example2.com">Test 2</a>
<a onclick="foo();" id="bar" href="http://example3.com">Test 3</a>

因此,它应该提取包含在'href'属性中的URL,无论前面或后面是什么。

3)提取所有链接的锚文本,例如在上面的示例中,它应该返回'http://example1.com'和锚文本'Test 1',然后是'http://example2.com'和'Test 2'等等。


4
你为什么不想使用 DOM 解析器来处理这个?你为什么找不到重复的东西? - Gordon
1
可能是php正则表达式匹配特定url模式的重复问题。 - Gordon
1
可能是重复的问题:正则表达式获取A元素的href属性 - Gordon
1
我喜欢每天都有无数次这样的问题被问到。 - ySgPjx
1
可能是从HTML页面中抓取数据的PHP的重复问题。 - Gordon
显示剩余3条评论
6个回答

8
<?

$dom = new DomDocument();
$dom->loadHTML($html);
$urls = $dom->getElementsByTagName('a');

1
很多人只是随口说一句“只需使用DOM解析器!”但从未展示过它能做什么的快速示例。http://php.net/manual/en/book.dom.php它可以做比我的示例更多的事情。值得学习。 - Oliver O'Neill
2
这个答案不完整,这里有一个可行的解决方案:https://dev59.com/dW855IYBdhLWcg3wSiU7 - giorgio79

5

您需要查看前后查找

<?php

$string = '<a href="http://example1.com">Test 1</a>
<a class="foo" id="bar" href="http://example2.com">Test 2</a>
<a onclick="foo();" id="bar" href="http://example3.com">Test 3</a>';

if(preg_match_all("|<a.*(?=href=\"([^\"]*)\")[^>]*>([^<]*)</a>|i", $string, $matches))
        {
        /*** if we find the word white, not followed by house ***/
        echo 'Found a match';
        print_r($matches);
    }
else
        {
        /*** if no match is found ***/
        echo 'No match found';
        }
?>

当然,正确的方法是使用DOM解析器,但也可以使用正则表达式实现。 - Sergi
请看一下GameBit的解决方案下面我的评论。它同样适用于你的正则表达式。 - Gordon
不会的,如果属性中有单引号,只需尝试即可。实际上,如果您使用此正则表达式#<a.(?=href="([^"]*)")[^>]>([^<])</a>|<a.*(?=href='([^']*)')[^>]>([^<])</a> |<a.*(?=href=([^\s]*)\s)[^>]>([^<]*)</a>#i或类似的东西,并且之后丢弃空结果集,则即使您使用单引号或根本不使用引号,它也不会中断。唯一的破坏方式是在锚文本中使用<,因为我无法使用具有无限字符(PHP正则表达式限制)的向后查找来检查它是否标记链接的结尾还是文本内的单个字符。 - Sergi

5
<?php
$regexp = "<a\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>(.*)<\/a>";
if(preg_match_all("/$regexp/siU", $html, $matches, PREG_SET_ORDER))
{ foreach($matches as $match)
{// $match[2] = link address
// $match[3] = link text}
}
?>

这将提取链接和锚文本。


我使用这个,因为它只需要54毫秒来处理4MB的文件,而真正的解析器需要10-30秒的时间 :) - KoalaBear
真的是一项伟大的工作,只需要一个正则表达式就可以完成所有工作。今天学到了新的方法。 - kanudo

2
尝试像这样做:

尝试像这样做:

//not tested
$regex_pattern = "/<a href=\"(.*)\">(.*)<\/a>/";

这不会匹配 OP 给出的示例标记中的第二个和第三个链接。 - Gordon

2
/<a[^>]+href\s*=\s*["']([^"']+)["'][^>]*>(.*?)<\/a>/mis

当属性值被双引号包含并且包含单引号时,此代码将失效。当忽略引号时,也会失效,这在 href 值像 next_page.htm 中是允许的。请参考http://www.w3.org/TR/html401/intro/sgmltut.html#h-3.2.2。 - Gordon
1
这个正则表达式非常强大(可以在此处进行测试http://www.martinwardener.com/regex):`\b(((src|href|action|url) *(=|:) *(?<mh>"|'|))(?<url>[\w ~$!*'/.?=#&@:%+,();-[]]+)\k<mh>|url *( *(?<mc>"|'|)(?<url>[\w ~$!*'/.?=#&@:%+,();-[]]+)\k<mc>))` - d7samurai

0

就从HTML中提取链接而言,这个正则表达式非常强大:

\b(((src|href|action|url) *(=|:) *(?<mh>"|'|))(?<url>[\w ~$!*'/.?=#&@:%+,();\-\[\]]+)\k<mh>|url *\( *(?<mc>"|'|)(?<url>[\w ~$!*'/.?=#&@:%+,();\-\[\]]+)\k<mc>\))

这是一个从HTML文档中提取所有“纯文本”(即标签外的内容)的正则表达式:

(<(?<tag>script|style)[\s\S]*?</\k<tag>>)|<!--[\s\S]*?-->|<[\s\S]*?>|(?<text>[^<>]*)

在此处测试它们:http://www.martinwardener.com/regex


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接