Регулярные выражения

ARN

Guest
Регулярные выражения

Подскажите регулярное выражение для поиска ссылок на страничке...

вот это

#\<a.*?href=(?>[\"'])?([^\s\>'\"]+)(?>[\"'])?.*>#i

многие ссылки пропускает...
нужно найти только ссылки, без названий...
 

ForJest

- свежая кровь
PHP:
<?php
$contents = "<a href='http://www.nagash.org?blah=supa&kama&dzhoba'>blah <b>blah</b>?</a>";
$contents = "<a href=\"http://www.nagash.ru?blah=zz'zz\"> some link </a>";
$contents = "<a href=http://www.nagash.ru?blah=zzzz>some link </a>";
$contents = '<a href="http://www.nagash.ru?blah=    zz">some link </a>';
$contents = '<a href="http://www.nagash.ru?blah=zz" title="cool link">some link </a>';
$contents = '<a name="abazaba></a><a href="abazaba2">test</a>';
//$contents = '<a  title="cool href= link" href="http://www.nagash.ru?blah=zz">link</a>';

$pattern = "~<a(.+?)href[\s\r\n]*=[\s\r\n]*(((['\"])([^\\4>]*?)\\4)|([^\s\n\r>]*))[^>]*>[^<]*</a>~si";
//$pattern = "~<a((.+?)(href|name)\s*=\s*(\'|\"){0,1}\s*((.*?)(\\4){0,1}\s*)\s*)>~si";

preg_match($pattern, $contents, $matches);
print_r($matches);
?>
-~{}~ 12.01.05 10:23:

В общем случае эта задача с помощью регов не решаема
 

ARN

Guest
не решаемых задач не бывает... как можно решить эту задачу по другому?
 

sage

Новичок
ARN
попробуй
PHP:
$pattern = "(?:.*?)href=\s*(?:"|\')?([^"'<>]+)(?:"|\')?(?:.*?)?\>(.+?)<\/a>";
ForJest
Скорее всего ему не в пхп коде искать ссылки надо, а в голом html, так что такого
"<a href=\"http://www.nagash.ru?blah=zz'zz\"> some link </a>"
быть попросту не может
 

ForJest

- свежая кровь
sage
Странно. Как мне это не пришло сразу в голову? Я то думал что ему нужно искать ссылки именно в PHP коде. Спасибо что указал мне на эту досадную оплошность.

-~{}~ 12.01.05 11:23:

быть попросту не может
С этим я согласиться не могу, к сожалению. Я порылся в вебе и нашёл, представь себе образчик именно такого HTML
Здесь
 

ARN

Guest
речь шла о слэшах а не о кавычках... а то что он не поймет адрес где есть (') меня это меньше всего волнует.. я такой адрес впервые вижу.. и не думаю что их так уж много...
 

valyala

Новичок
Рассмотрим по порядку предложенные регулярные выражения в этой теме:
#\<a.*?href=(?>[\"'])?([^\s\>'\"]+)(?>[\"'])?.*>#i
\<a - бэкслеш перед знаком '<' не нужен

<a.*?href - означает, что после строки <a могут идти произвольные символы, исключая перевод строки (т.к. нет модификатора s), за которыми идет строка href. Пример:<a тра-ля-ля>ля-ляhref. Уже что-то не то?

href= - буквально означает эту строку. Но в HTML-е между href и символом = может быть любое количество пробельных символов, которые в регулярных выражениях имеют обозначение \s. Так что вот такая строчка не выловится: <a href = "тра-ля-ля">.

(?>[\"'])?.*> - а вот это вообще лишнее. Это регулярное выражение совпадет со строкой, в начале которой может быть одиночная или двойная кавычка, за которой идет строка любой длины (в том числе и нулевой), состоящая из любых символов, исключая перевод строки (т.к. нет модификатора \s), за которой следует символ '>'. Проблема тут в том, что любые символы могут включать символ '>'. Это означает, что данное регулярное выражение совпадет со строкой: тра-ля-ля>ляля<<<<>>>>, что не совсем соответствует ожиданиям автора, составлявшего данное регулярное выражение.

Еще данное регулярное выражение некорректно выловит адреса, содержащие кавычки, знак '>' или пробельные символы. Например:
<a href="тра'ля"> <a href='тра"ля'> <a href="javascript: if ( 1 > 2) alert('hello') ">

"~<a(.+?)href[\s\r\n]*=[\s\r\n]*(((['\"])([^\\4>]*?)\\4)|([^\s\n\r>]*))[^>]*>[^<]*</a>~si"
Это вообще какой-то монстр. Попытаюсь в нем разобраться.

<a(.+?)href - та же проблема, что и в первом реге, т.е. оно совпадет со строкой <a name="тра-ля">тра-ляhref.

[\s\r\n] - Тут лишние \r\n, которые входят в класс пробельных символов \s.

(((['\"])([^\\4>]*?)\\4)|([^\s\n\r>]*)) - прям какое-то нагромождение скобок :) . Зачем их столько? [^\\4>] - этот "фокус" не пройдет. Наверное, автор ожидал, что это регулярное выражение означает символ, отличный от '>' и того, что заключено в четвертую скобку. Но на самом деле это регулярное выражение означает символ, отличный от chr(4) и '>'.

[^>]*>[^<]*</a> - это тоже лишнее, как и в предыдущем примере. Содержит аналогичную ошибку. Кроме того, из-за этого "мусора" регулярка не совпадет с тэгом <A>, в котором находится любой другой тэг. Например, <a href="тра-ля-ля"><b>ля-ля</b></a>

"(?:.*?)href=\s*(?:"|')?([^"'<>]+)(?:"|\')?(?:.*?)?\>(.+?)<\/a>"
(?:.*?) - не могу понять назначения этого регулярного выражения.

href= - после href тоже могут быть пробельные символы.

(?:"|') - можно сделать и так, но лучше вот так: ['"] - говорят, так быстрее будет работать.

(?:"|\')?(?:.*?)?\>(.+?)<\/a> - это снова лишнее. Хотя, тут нет ошибок, допущенных в предыдущих примерах.

Кроме того, это регулярное выражение содержит много ненужных скобок, а также некорректно вылавливает ссылки, содержащие символы ["'<>].

Ну и напоследок приведу свой вариант регулярного выражения, вырезающего ссылки:
PHP:
preg_match_all('/\\shref\\s*=\\s*("[^"]+"|\'[^\']+\'|[^\'">][^>\\s]+)/i', $str, $matches);
print_r($matches);
Буду рад, если кто-нибудь придирется к нему :)
 

ForJest

- свежая кровь
PHP:
$contents = '<a  title="cool href= link" href="http://www.nagash.ru?blah=zz">link</a>';
$contents = 'Chpter 3. Using href. You can write something like href = http://site.com';
$contents = "<!-- a href='http://fuck.off.com/damned/bot'> </a-->";
$contents = '<a  bots_pissed_off= "<a href= http://google.com<a>hehe</a>" href="Contents">link</a>';
Развлекайся :). Автоматная грамматика не может съесть контекстно-зависимую задачу. А что до ошибок - это была попытка написать именно "универсальную" регу :). Видимо я оставил на пол пути.
------
 

sage

Новичок
ForJest
может фигней страдать не будем. ты сам когда-нибудь видел такие ссылки?

valyala
(?:.*?) - не могу понять назначения этого регулярного выражения.
(?:.*?) = \\s
href= - после href тоже могут быть пробельные символы
согласен, забыл про это
(?:"|\')?(?:.*?)?\>(.+?)<\/a> - это снова лишнее. Хотя, тут нет ошибок, допущенных в предыдущих примерах.
(?:"|\')? = ['"]
(?:.*?)? = \\s*
(.+?) - сохраняем текст ссылки, если надо.

Не подскажешь, что в твоём выражении означает [^\'">][^>\\s]+. Т.е. зачем [^\'">]? Всё прекрасно работает и без этих квадратных скобочек.

-~{}~ 13.01.05 11:42:

\shref\s*=\s*("[^"]*"|'[^']*'|[^'">\s]+)

Взято из "Регулярные выражения" Фридл, 2-е издание
 

ForJest

- свежая кровь
sage
Я же не спорю - не будем страдать фигнёй :) Задача "вытянуть со страницы все ссылки регой" мне и представляется именно этим самым страданием фигнёй :) Чисто умозрительная задача, не применимая на практике.
 

valyala

Новичок
Развлекайся :). Автоматная грамматика не может съесть контекстно-зависимую задачу. А что до ошибок - это была попытка написать именно "универсальную" регу :). Видимо я оставил на пол пути.
Задача "вытянуть со страницы все ссылки регой" мне и представляется именно этим самым страданием фигнёй :) Чисто умозрительная задача, не применимая на практике
ForJest, ты недооцениваешь потенциал регулярных выражений. Развлекайся:
PHP:
function get_all_urls($str)
{
    $tag_name = "(?>(?i)[!a-z](?!--)[-:_a-z]*)";
    $param_name = "(?>\"[^\"]*\"|'[^']*'|[^\"'](?:[^\\s>=/]+|/(?!>))*)";
    $param_value = "(?>\"[^\"]*\"|'[^']*'|[^\"'\\s>](?:[^\\s>/]+|/(?!>))*)";
    $param_pair = "(?>${param_name}(?:\\s*=\\s*${param_value}|(?!\\s*=)))";
    $params = "(?>(?:\\s+${param_pair})*\\s*)";
    $tag = "(?></?${tag_name}${params}/?>)";
    $script = "(?>(?i)<script${params}(?:(?s)/>|>.*?</script${params}>))";
    $style = "(?>(?i)<style${params}(?:(?s)/>|>.*?</style${params}>))";
    $comment = "(?>(?s)<!--.*?-->)";
    $text = "(?>(?:[^<]+|(?!${tag})(?!${comment})<)+)";
    $html_element = "(?>${text}|${comment}|${script}|${style}|${tag})";
    $html_doc = "(?>(?s)^${html_element}*$)";

    $tag_a = "^(?i)<a(?:\\s+${param_name}(?<!href)(?:\\s*=\\s*${param_value}|(?!\\s*=)))*\\s+href\\s*=\\s*(${param_value})";

    preg_match_all('#' . $html_element . '#', $str, $matches);

    $tmp = $matches[0];
    $n = sizeof($tmp);

    $urls = array();
    for ($i = 0; $i < $n; $i++) {
        if (preg_match('#' . $tag_a . '#', $tmp[$i], $matches)) {
            $href = $matches[1];
            if ($href{0} == "'" || $href{0} == '"') {
                $href = substr($href, 1, -1);
            }
            $urls[] = html_entity_decode($href, ENT_QUOTES);
        }
    }

    return $urls;
}

echo "test 1\n";
print_r(get_all_urls('<a  title="cool href= link" href="http://www.nagash.ru?blah=zz">link</a>'));

echo "test 2\n";
print_r(get_all_urls('Chpter 3. Using href. You can write something like href = http://site.com'));

echo "test 3\n";
print_r(get_all_urls("<!-- a href='http://fuck.off.com/damned/bot'> </a-->"));

echo "test 4\n";
print_r(get_all_urls('<a  bots_pissed_off= "<a href= http://google.com<a>hehe</a>" href="Contents">link</a>'));
Не подскажешь, что в твоём выражении означает [^\'">][^>\\s]+. Т.е. зачем [^\'">]? Всё прекрасно работает и без этих квадратных скобочек
Да, можно обойтись и без этих скобочек. Но тогда регулярное выражение не будет правильно обрабатывать строки, начинающиеся с кавычки, но не оканчивающиеся ей. Например:
"something shit - по идее вообще не должно совпать. А оно будет возвращать "somthing
'single quote - аналогичная проблема.
 

yamayki

Новичок
Здравствуйте!

У меня такая задача, не могу ее решить.

Нужно на странице получить количество (или список) уникальных внешних ссылок. Т.е. локальные ссылки на тот-же сайт не учитывать, и две ссылки с одним доменом считать за одну. Как это можно решить с помошью регулярных выражений?

Заранее спасибо за любой ответ!
 
Сверху