PHP - 检查URL是否有效

3

我正在检查URL并返回“有效”,如果URL状态代码为“200”,并且如果URL在“404”上,则返回“无效”。

URL是指重定向到某个页面(URL)的链接,我需要检查该页面(URL)的状态以确定其有效或无效的状态码。

<?php

// From URL to get redirected URL
$url = 'https://www.shareasale.com/m-pr.cfm?merchantID=83483&userID=1860618&productID=916465625';
  
// Initialize a CURL session.
$ch = curl_init();
  
// Grab URL and pass it to the variable.
curl_setopt($ch, CURLOPT_URL, $url);
  
// Catch output (do NOT print!)
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  
// Return follow location true
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
$html = curl_exec($ch);
  
// Getinfo or redirected URL from effective URL
$redirectedUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
  
// Close handle
curl_close($ch);
echo "Original URL:   " . $url . "<br/> </br>";
echo "Redirected URL: " . $redirectedUrl . "<br/>";

 function is_url_valid($url) {
  $handle = curl_init($url);
  curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($handle, CURLOPT_NOBODY, true);
  curl_exec($handle);
 
  $httpCode = intval(curl_getinfo($handle, CURLINFO_HTTP_CODE));
  curl_close($handle);
 
  if ($httpCode == 200) {
    return 'valid link';
  }
  else {
    return 'invalid link';
  }
}

// 
echo "<br/>".is_url_valid($redirectedUrl)."<br/>";

如您所见,上面的链接显示状态码为 400,但仍然显示“有效”。 我正在使用上述代码,您有任何想法或更正建议吗?以使其按预期工作? 看起来该网站有多个重定向的 URL,而该脚本仅检查其中一个,因此它会显示为有效。 您有任何想法如何解决这个问题?
这里是我正在检查的链接。

问题 -

例如 - 如果我使用此链接进行检查https://www.shareasale.com/m-pr.cfm?merchantID=66802&userID=1860618&productID=1186005518,那么在浏览器中会出现"404",但在脚本输出中是"200"


谢谢您的评论和建议,尽管我在输出中得到了404状态码。 - user15233374
2
@devhs - 我不确定这是否是适当的解决方案。但我检查了上面的一些链接,它们正在管理自定义404页面。作为一个快速解决方案,您可以使用“file_get_contents”获取URL的内容,并检查“页面标题”。 - Sachin Vairagi
@SachinVairagi 感谢您的建议,但我需要先获取最终URL,然后才能在其上使用“file_get_contents”。 因此,如果我获得了最终URL,则有几种确定方法。 - user15233374
由于“刷新”标头,您可能会遇到一些情况,无法找到最终URL的响应代码。过去,我有类似的要求,但是为了获取最终URL的og标签而放弃了一些边角案例。 - Haridarshan
1
通过“刷新”头,我指的是在这种情况下的header("Refresh:5; url=page2.php");,而curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);则不会跟随重定向,另一个是meta refresh http-equiv header和javascript重定向。 - Haridarshan
显示剩余10条评论
5个回答

2
我使用get_headers()函数来完成此操作。如果在数组中找到状态2xx,则表示URL正常。
function urlExists($url){
  $headers = @get_headers($url);
  if($headers === false) return false;
  return preg_grep('~^HTTP/\d+\.\d+\s+2\d{2}~',$headers) ? true : false;
}

谢谢您的回答,但如果主URL有重定向(多个重定向)怎么办? 假设这个URL是 - https://www.shareasale.com/m-pr.cfm?merchantID=66802&userID=1860618&productID=1186005518 - user15233374
该函数针对此URL返回true。这样可以吗? - jspit
不,因为页面的状态码是404(未找到),所以它不应该返回true。 - user15233374
2
如果我的浏览器禁用了Javascript,我就看不到广告。我认为这种转发是通过Javascript完成的。这个问题单靠PHP无法解决。 - jspit
1
我没有快速解决方案。 - jspit
显示剩余4条评论

1
这是我对这个问题的看法。基本上,要点如下:
  1. 你不需要发出多个请求。使用 CURLOPT_FOLLOWLOCATION 就能为你完成所有工作,在最后一次调用时,如果有重定向,你将得到 http 响应代码。
  2. 由于你正在使用 CURLOPT_NOBODY,请求将使用 HEAD 方法并且不会返回任何内容。因此,CURLOPT_RETURNTRANSFER 是无用的。
  3. 我已经自行使用了我的编码风格(没有冒犯之意)。
  4. 由于我从 Phpstorm 的 Scratch 文件中运行代码,我添加了一些 PHP_EOL 作为换行符以格式化输出。随意删除它们。

...  

<?php

$linksToCheck = [
    'https://click.linksynergy.com/link?id=GsILx6E5APM&offerid=547531.5112&type=15&murl=https%3A%2F%2Fwww.peopletree.co.uk%2Fwomen%2Fdresses%2Fanna-checked-dress',
    'https://click.linksynergy.com/link?id=GsILx6E5APM&offerid=330522.2335&type=15&murl=https%3A%2F%2Fwww.wearethought.com%2Fagnetha-black-floral-print-bamboo-dress-midnight-navy%2F%2392%3D1390%26142%3D198',
    'https://click.linksynergy.com/link?id=GsILx6E5APM&offerid=330522.752&type=15&murl=https%3A%2F%2Fwww.wearethought.com%2Fbernice-floral-tunic-dress%2F%2392%3D1273%26142%3D198',
    'https://click.linksynergy.com/link?id=GsILx6E5APM&offerid=330522.6863&type=15&murl=https%3A%2F%2Fwww.wearethought.com%2Fjosefa-smock-shift-dress-in-midnight-navy-hemp%2F%2392%3D1390%26142%3D208',
    'https://www.shareasale.com/m-pr.cfm?merchantID=16570&userID=1860618&productID=546729471',
    'https://www.shareasale.com/m-pr.cfm?merchantID=53661&userID=1860618&productID=680698793',
    'https://www.shareasale.com/m-pr.cfm?merchantID=66802&userID=1860618&productID=1186005518',
    'https://www.shareasale.com/m-pr.cfm?merchantID=83483&userID=1860618&productID=916465625',
];

function isValidUrl($url) {
    echo "Original URL:   " . $url . "<br/>\n";

    $handle = curl_init($url);

    // Follow any redirection.
    curl_setopt($handle, CURLOPT_FOLLOWLOCATION, TRUE);

    // Use a HEAD request and do not return a body.
    curl_setopt($handle, CURLOPT_NOBODY, true);

    // Execute the request.
    curl_exec($handle);

    // Get the effective URL.
    $effectiveUrl = curl_getinfo($handle, CURLINFO_EFFECTIVE_URL);
    echo "Effective URL:   " . $effectiveUrl . "<br/> </br>";

    $httpResponseCode = (int) curl_getinfo($handle, CURLINFO_HTTP_CODE);

    // Close this request.
    curl_close($handle);

    if ($httpResponseCode == 200) {
        return '✅';
    }
    else {
        return '❌';
    }
}

foreach ($linksToCheck as $linkToCheck) {
    echo PHP_EOL . "Result: " . isValidUrl($linkToCheck) . PHP_EOL . PHP_EOL;
}

哈哈,很酷的使用UTF8!不幸的是,OP也想要跟随JavaScript重定向,有关信息请参见我下面的答案:( - hanshenrik

1
注意:我们使用CURLOPT_NOBODY只是为了检查连接,而不是获取整个主体。
  $url = "Your URL";
  $curl = curl_init($url);
  curl_setopt($curl, CURLOPT_NOBODY, true);
  $result = curl_exec($curl);
 if ($result !== false)
 {
    $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);  
 if ($statusCode == 404)
 {
   echo "URL Not Exists"
 }
 else
 {
   echo "URL Exists";
  }
 }
else
{
  echo "URL not Exists";
}

0
以下代码运行良好,但当我将URL放入数组并测试相同的功能时,它不能给出正确的结果? 有什么想法吗? 此外,如果有人想更新答案以使其具有动态性(应同时检查多个URL,当提供URL数组时)。
  <?php
    
    // URL to check
    $url = 'https://www.shareasale.com/m-pr.cfm?merchantID=66802&userID=1860618&productID=1186005518';
      
    $ch = curl_init(); // Initialize a CURL session.
    curl_setopt($ch, CURLOPT_URL, $url); // Grab URL and pass it to the variable.
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); // Catch output (do NOT print!)
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); // Return follow location true
    $html = curl_exec($ch);
    $redirectedUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); // Getinfo or redirected URL from effective URL
    curl_close($ch); // Close handle
    
    $get_final_url = get_final_url($redirectedUrl);
    if($get_final_url){
        echo is_url_valid($get_final_url);
    }else{
        echo $redirectedUrl ? is_url_valid($redirectedUrl) : is_url_valid($url);
    }
    
    function is_url_valid($url) {
      $handle = curl_init($url);
      curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($handle, CURLOPT_NOBODY, true);
      curl_exec($handle);
     
      $httpCode = intval(curl_getinfo($handle, CURLINFO_HTTP_CODE));
      curl_close($handle);
      echo $httpCode;
      if ($httpCode == 200) {
        return '<b> Valid link </b>';
      }
      else {
        return '<b> Invalid link </b>';
      }
    }
    
    function get_final_url($url) {
            $ch = curl_init();
            if (!$ch) {
                return false;
            }
            $ret = curl_setopt($ch, CURLOPT_URL,            $url);
            $ret = curl_setopt($ch, CURLOPT_HEADER,         1);
            $ret = curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
            $ret = curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            $ret = curl_setopt($ch, CURLOPT_TIMEOUT,        30);
            $ret = curl_exec($ch);
    
            if (!empty($ret)) {
                $info = curl_getinfo($ch);
                curl_close($ch);
                return false;
            if (empty($info['http_code'])) {
                return false;
            } else {
                preg_match('#(https:.*?)\'\)#', $ret, $match);
                $final_url = stripslashes($match[1]);
                return stripslashes($match[1]);
            }
        }
    } 

只是一个想法:你的脚本请求以主机检测到的模式进入,然后抵消你的意图。或者你可能会这样说:为什么那个主机破坏了我的期望?这是他们的服务器,你只能发送请求,并且必须接受答案(响应);) - hakre

0
看,这里的问题是你想要跟随JAVASCRIPT重定向, 你抱怨的URL https://www.shareasale.com/m-pr.cfm?merchantID=66802&userID=1860618&productID=1186005518 确实重定向到一个响应HTTP 200 OK的URL,而该页面包含了JavaScript。
<script LANGUAGE="JavaScript1.2">
                window.location.replace('https:\/\/www.tenthousandvillages.com\/bicycle-statue?sscid=71k5_4yt9r ')
                </script>

所以你的浏览器,它理解JavaScript,会遵循JavaScript重定向,而那个JS重定向是到一个404页面...不幸的是,从PHP中没有好的方法来做到这一点,你最好的选择可能是一个无头浏览器,例如PhantomJS或puppeteer或Selenium之类的东西。

不过,你还可以通过正则表达式搜索JavaScript重定向并希望取得最佳效果,例如

<?php
function is_url_valid(string $url):bool{
    if(0!==strncasecmp($url,"http",strlen("http"))){
        // file:///etc/passwd and stuff like that aren't considered valid urls right?
        return false;
    }
    $ch=curl_init();
    if(!curl_setopt_array($ch,array(
        CURLOPT_URL=>$url,
        CURLOPT_FOLLOWLOCATION=>1,
        CURLOPT_RETURNTRANSFER=>1
    ))){
        // best guess: the url is so malformed that even CURLOPT_URL didn't accept it.
        return false;
    }
    $resp= curl_exec($ch);
    if(false===$resp){
        return false;
    }
    if(curl_getinfo($ch,CURLINFO_RESPONSE_CODE) != 200){
        // only HTTP 200 OK is accepted
        return false;
    }
    // attempt to detect javascript redirects... sigh
    // window.location.replace('https:\/\/www.tenthousandvillages.com\/bicycle-statue?sscid=71k5_4yt9r ')
    $rex = '/location\.replace\s*\(\s*(?<redirect>(?:\'|\")[\s\S]*?(?:\'|\"))/';
    if(!preg_match($rex, $resp, $matches)){
        // no javascript redirects detected..
        return true;
    }else{
        // javascript redirect detected..
        $url = trim($matches["redirect"]);
        // javascript allows both ' and " for strings, but json only allows " for strings
        $url = str_replace("'",'"',$url);
        $url = json_decode($url, true,512,JSON_THROW_ON_ERROR); // we extracted it from javascript, need json decoding.. (well, strictly speaking, it needs javascript decoding, but json decoding is probably sufficient, and we only have a json decoder nearby)
        curl_close($ch);
        return is_url_valid($url);
    }
}
var_dump(

    is_url_valid('https://www.shareasale.com/m-pr.cfm?merchantID=66802&userID=1860618&productID=1186005518'),
    is_url_valid('http://example.org'),
    is_url_valid('http://example12k34jr43r5ehjegeesfmwefdc.org'),
    
);

但那是一个相当靠运气的hacky解决方案。


谢谢您的回答,让我检查一下是否可以同时处理多个URL,例如如果我创建了一个包含在问题中发布的URL的数组,并在循环中调用“is_url_valid”方法。 - user15233374
@devhs不应该是问题,顺便提一下,我刚刚注意到这种方法还有另一个重大弱点:它无法处理无限重定向。例如,如果page1重定向到page2,然后重定向到page1,再重定向到page2...,那么这个脚本将永远跟随重定向,直到达到php max_execution_time或调用堆栈耗尽为止。(不过这是可以修复的) - hanshenrik
谢谢,我会检查一下。 我刚在这里进行了测试-https://paiza.io/projects/N3m4E11HZAmq5uTb8gLjcg 但似乎没有起作用。 - user15233374
@devhs,如果你确保将paiza.io更改为https://paiza.io,那么该网址对我来说返回的是布尔值true。你得到了什么? - hanshenrik
当您访问此链接时,这是一个编译器,我已经测试了您的代码。 paiza.io/projects/N3m4E11HZAmq5uTb8gLjcg - user15233374
@devhs 嗯,实际上我进行了测试,paiza.io的超时限制是2000毫秒...但是在我的笔记本电脑上检查第一个URL需要大约1956毫秒,我猜他们的系统有点慢,刚好足够达到超时限制:P - hanshenrik

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