使用正则表达式从HTML文档中提取链接的URL。

3
我需要捕获给定 HTML 中的所有链接。
以下是示例代码:
<div class="infobar">
    ... some code goes here ...
    <a href="/link/some-text">link 1</a>
    <a href="/link/another-text">link 2</a>
    <a href="/link/blabla">link 3</a>
    <a href="/link/whassup">link 4</a>
    ... some code goes here ...
</div>

我需要获取

中以"/link/"开头的所有链接。

我尝试了以下方法:

preg_match_all('#<div class="infobar">.*?(href="/link/(.*?)") .*?</div>#is', $raw, $x);

但它只给出第一个匹配项。

谢谢建议。


也许有一个HTML解析器可以更轻松地为您完成这个任务? - user456814
我已经使用 preg_match 获取 div.infobar 内部,然后使用 preg_match_all 获取链接。但是由于正则表达式提供了更多的灵活性,为什么不使用它呢?我只需要一个好的模式。我想知道如何只使用 1 个 preg_match_all 来完成这个任务。 - Valour
2
你不能只用一个正则表达式来完成这个任务。你需要先分离出div,然后从中提取所需的链接。--关于这些简短的注释:你可以使用phpQuery或QueryPath更轻松地提取链接,使用foreach (qp($html)->find("div.infobar a") as $a) { print $a->attr("href"); }。只有在已知的连贯HTML输入块的性能原因时,才真正适合使用特定的正则表达式。 - mario
HTML不是一种常规语言,因此使用正则表达式解析HTML是不明智的(https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454)。 - sarnold
@stereofrog,说得好;对于这个特定情况,我无法改进anubhava的答案,而且我认为一点幽默感是展示尝试使用错误工具可能会导致难以置信的挫败感的绝妙方式。 - sarnold
@Stereofrog,我们自己的Jeff Atwood提供了进一步的建议,即使用正则表达式解析非正则语言(如HTML)可能大部分时间都能工作,但是很脆弱。是的,新的引擎称为“正则表达式”可以匹配一些非正则语言,但我仍然认为使用更强大的解析器(如DOMDocument或XPath方法)编写的匹配将比使用这些语言更难维护。 - sarnold
4个回答

7
我建议您使用 DOMDocument 来实现此目的,而不是使用正则表达式。请考虑以下简单代码:
$content = '
<div class="infobar">
    <a href="/link/some-text">link 1</a>
    <a href="/link/another-text">link 2</a>
    <a href="/link/blabla">link 3</a>
    <a href="/link/whassup">link 4</a>
</div>';
$dom = new DOMDocument();
$dom->loadHTML($content);

// To hold all your links...
$links = array();

// Get all divs
$divs = $dom->getElementsByTagName("div");
foreach($divs as $div) {
  // Check the class attr of each div
  $cl = $div->getAttribute("class");
  if ($cl == "infobar") {
    // Find all hrefs and append it to our $links array
    $hrefs = $div->getElementsByTagName("a");
    foreach ($hrefs as $href)
       $links[] = $href->getAttribute("href");
  }
}
var_dump($links);

输出

array(4) {
  [0]=>
  string(15) "/link/some-text"
  [1]=>
  string(18) "/link/another-text"
  [2]=>
  string(12) "/link/blabla"
  [3]=>
  string(13) "/link/whassup"
}

让我们看看这个操作员是否仍然认为正则表达式更好 :d - dynamic
这个和正则表达式之间的执行时间是多少?我只需要用2个preg_match_all函数就可以做到这一点。 - Valour
执行时间将与基于正则表达式的代码相当(甚至更好),但更重要的是,与正则表达式代码相比,基于DOM的代码不会在意外时间中断。 - anubhava

2

我修改了之前的回答。你需要分两步进行:

//This first step grabs the contents of the div.
preg_match('#(?<=<div class="infobar">).*?(?=</div>)#is', $raw, $x);

//And here, we grab all of the links.
preg_match_all('#href="/link/(.*?)"#is', $x[0], $x);

谢谢。但这次它得到了最后一个 :D - Valour
我将它分成了两个步骤。div 第一次被匹配后,就不能再次匹配了。 - Jacob Eggers

2

http://simplehtmldom.sourceforge.net/ :

// Create DOM from URL or file
$html = file_get_html('http://www.google.com/');

// Find all links
foreach($html->find('a') as $element)
       echo $element->href . '<br>'; 

0

试试这个(我添加了一个+):

preg_match_all('#<div class="infobar">.*?(href="/link/(?:.*?)")+ .*?</div>#is', $raw, $x);

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