Я сделал эту функцию, чтобы проверить, есть ли у контента 301 перенаправление. Пожалуйста, помогите мне оптимизировать его лучше, потому что он слишком медленный.
function contains_http_301_link($content, $current_site){
$pattern = '~<a(.*?)href="([^"]+)"(.*?)>~';
$links = array();
// Check if there is a url in the content
if(preg_match_all($pattern, $content, $urls)) {
foreach($urls[0] as $url){
// get external links
if ( !strpos( $url, $current_site )) { // Is an external link
$vowels = array('"' ,"<a", " ", "href", "=", ">");
$links[] = str_replace($vowels, "", $url);
}
}
}
foreach ($links as $link) {
$_headers = @get_headers($link,1);
if($_headers && strpos( $_headers[0], '301')) {
return true;
}
}
return false;
}
1 ответ
Я никогда не пытался выполнить такую задачу, но предполагаю, что вам нравились методы cURL (включая CURLOPT_NO_BODY
и CURLINFO_HTTP_CODE
) как предлагают на 1 и 2.
Что касается синтаксического анализа html-документа, использование регулярного выражения может превзойти законный синтаксический анализатор, но вам может понравиться более точный скейпинг, если вы используете парсер DOM.
Поскольку вы сосредоточены на производительности, я [cringe] предложить регулярное выражение для анализа вашего html.
Ваш шаблон ~<a(.*?)href="([^"]+)"(.*?)>~
использует ленивые квантификаторы, и это может немного замедлить работу. Кроме того, ваш <a(.*?)href
позволит сопоставить <a data-href
и <abbr href
— а это не то, что вы намеревались сделать.
Ваше намерение состоит в том, чтобы захватить ТОЛЬКО URL-адрес внутри href
атрибут, поэтому не пытайтесь сопоставить больше, чем вам нужно. Вы, вероятно, сможете обойтись без ограничения слов после того, как a
и пробел перед href
, затем сбросьте совпадение полной строки с помощью K
чтобы сохранить только значение href. Если это недостаточно сильная проверка, вы можете добавить опережающий просмотр, чтобы убедиться, что открывающий тег заполнен правильно.
~<ab.*? href="K[^"]+~
или же~<ab.*? href="K[^"]+(?=".*?>)~
В любом случае ваш код может пропустить этап очистки. Слава богу, потому что $vowels
содержит данные, которые не являются всеми гласными.
strpos()
может вернуться false
или же 0
как смещение. Это означает, что вы не должны полагаться на условие манипулирования типом, используя просто !
. Вы должны явно проверить false
или если вы используете версию php, которая пользуется str_contains()
— это сработает.
Я лично осуждаю использование «оператора stfu» (@
), но поскольку я не знаком с этой задачей, я не буду называть ее мерзостью.
function contains_http_301_link(string $content, string $current_site): bool
{
preg_match_all('~<ab.*? href="K[^"]+~', $content, $matches);
foreach ($matches[0] as $url) {
if (strpos($url, $current_site) !== false) {
continue;
}
$headers = @get_headers($url, 1);
if($headers && strpos($headers[0], '301') !== false) {
return true;
}
}
return false;
}
Я мог бы включить $current_site
фильтрация в регулярное выражение, но это может принести больше вреда, чем пользы.
Если вы собирались развлечь (и протестировать) сценарий парсера DOM, возможно, используйте это:
function contains_http_301_link(string $content, string $current_site): bool
{
$dom = new DOMDocument;
$dom->loadHTML($content);
$xpath = new DOMXPath($dom);
foreach ($xpath->query("//a[not(contains(@href, '$current_site'))]/@href") as $href) {
$headers = @get_headers($href->nodeValue, 1);
if($headers && strpos($headers[0], '301') !== false) {
return true;
}
}
return false;
}