0

From a file_get_contents I get the HTML code of a url.

$html = file_get_contents($url);

Now I would like to capture the href link.

The HTML code is:

<li class="four-column mosaicElement">
<a href="https://example.com" title="Lorem ipsum">
...
</a>
</li>
<li class="four-column mosaicElement">
<a href="https://example.org" title="Lorem ipsum">
...
</a>
</li>

So I'm using this:

preg_match_all('/class=\"four-column mosaicElement\"><a href=\"(.+?)\" title=\"(.+?)"/m', $html, $urls, PREG_SET_ORDER, 0);

foreach ($urls as $key => $url) {
    echo $url[1];
}

How do I solve this problem?

Emma
  • 27,428
  • 11
  • 44
  • 69
PacPac
  • 267
  • 2
  • 8
  • 1
    I think you should add a newline `\n` or `\R` after `class=\"four-column mosaicElement\">\R`. https://regex101.com/r/GtBZfG/1 Why not use DOMDocument instead? – The fourth bird Jun 16 '19 at 14:06

3 Answers3

3

Another option is to use DOMXPath with an xpath expression that finds all list items with both class names and then gets the anchors:

//li[contains(@class, 'four-column') and contains(@class, 'mosaicElement')]/a

For example:

$string = <<<DATA
<li class="four-column mosaicElement">
<a href="https://example.com" title="Lorem ipsum">
</a>
</li>
<li class="four-column mosaicElement">
<a href="https://example.org" title="Lorem ipsum">
</a>
</li>
DATA;

$dom = new DOMDocument();
$dom->loadHTML($string);
$xpath = new DOMXpath($dom);

foreach($xpath->query("//li[contains(@class, 'four-column') and contains(@class, 'mosaicElement')]/a") as $v) {
    echo $v->getAttribute("href") . PHP_EOL;
}

Result

https://example.com
https://example.org

See a php demo

The fourth bird
  • 154,723
  • 16
  • 55
  • 70
2

I was able to get your code working by just modify the regex pattern to the following:

class="four-column mosaicElement">\s*<a href="(.+?)" title="(.+?)"
                                 ^^^^^

Note carefully that I allow for any amount of whitespace between the class attribute from the outer tag (<li>) and the inner anchor.

Here is your updated script:

$html = "<li class=\"four-column mosaicElement\">\n<a href=\"https://example.com\" title=\"Lorem ipsum\">\n</a>\n</li>\n<li class=\"four-column mosaicElement\">\n<a href=\"https://example.org\" title=\"Lorem ipsum\">\n</a>\n</li>";
preg_match_all('/class="four-column mosaicElement">\s*<a href="(.+?)" title="(.+?)"/m', $html, $urls, PREG_SET_ORDER, 0);

foreach ($urls as $key => $url) {
    echo $url[1] . "\n";
}    

This prints:

https://example.com
https://example.org
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
1

Here, we can also use an expression with positive lookahead and optional spaces, just in case,

(?=class="four-column mosaicElement")[\s\S]*?href="\s*(https?[^\s]+)\s*"

and our desired URLs are in this group:

(https?[^\s]+)

DEMO

TEST

$re = '/(?=class="four-column mosaicElement")[\s\S]*?href="\s*(https?[^\s]+)\s*"/m';
$str = '<li class="four-column mosaicElement">
<a href="https://example.com" title="Lorem ipsum">
...
</a>
</li>
<li class="four-column mosaicElement">
<a href="https://example.org" title="Lorem ipsum">

<li class="four-column mosaicElement">
<a href="   https://example.org   " title="Lorem ipsum">

<li class="four-column mosaicElement">
<a href="   https://example.org                " title="Lorem ipsum">
...
</a>
</li>
';

preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);

foreach ($matches as $key => $url) {
    echo $url[1] . "\n";
}

Output

https://example.com
https://example.org
https://example.org
https://example.org

RegEx Circuit

jex.im visualizes regular expressions:

enter image description here

Emma
  • 27,428
  • 11
  • 44
  • 69