Un moyen facile de tester une URL pour 404 en PHP?

152

Je m'enseigne moi-même sur le grattage de base et j'ai constaté que parfois les URL que j'insère dans mon code renvoient 404, ce qui gomme tout le reste de mon code.

J'ai donc besoin d'un test en haut du code pour vérifier si l'URL renvoie 404 ou non.

Cela semblerait être une tâche assez simple, mais Google ne me donne aucune réponse. Je crains que je ne recherche le mauvais truc.

Un blog m'a recommandé d'utiliser ceci:

$valid = @fsockopen($url, 80, $errno, $errstr, 30);

puis testez pour voir si $ valide s'il est vide ou non.

Mais je pense que l'URL qui me pose des problèmes a une redirection, donc $ valid devient vide pour toutes les valeurs. Ou peut-être que je fais autre chose de mal.

J'ai également examiné une "demande principale", mais je n'ai pas encore trouvé d'exemples de code réels avec lesquels je peux jouer ou essayer.

Suggestions? Et qu'est-ce que c'est curl?

gros nez
la source

Réponses:

276

Si vous utilisez les curlliaisons PHP , vous pouvez vérifier le code d'erreur en utilisant curl_getinfocomme tel:

$handle = curl_init($url);
curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);

/* Get the HTML or whatever is linked in $url. */
$response = curl_exec($handle);

/* Check for 404 (file not found). */
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
    /* Handle 404 here. */
}

curl_close($handle);

/* Handle $response here. */
strager
la source
1
Je ne suis pas encore familier avec cURL, il me manque donc quelques concepts. Que dois-je faire avec la variable $ response ci-dessous? Que contient-il?
1
@bflora, j'ai fait une erreur dans le code. (Corrige dans une seconde.) Vous pouvez voir la documentation de curl_exec sur le site de PHP.
strager
4
@bflora $ response contiendra le contenu de $ url afin que vous puissiez faire des choses supplémentaires comme vérifier le contenu pour des chaînes spécifiques ou autre. Dans votre cas, vous ne vous souciez que de l'état 404, vous n'avez donc probablement pas à vous soucier de $ response.
Beau Simensen
5
Que faire si vous souhaitez simplement charger les en-têtes au lieu de télécharger le fichier entier?
patrick
13
@patrick alors vous devez spécifier curl_setopt($handle, CURLOPT_NOBODY, true);avant d'exécutercurl_exec
utilisateur
101

Si vous utilisez PHP5, vous pouvez utiliser:

$url = 'http://www.example.com';
print_r(get_headers($url, 1));

Alternativement avec php4, un utilisateur a contribué à ce qui suit:

/**
This is a modified version of code from "stuart at sixletterwords dot com", at 14-Sep-2005 04:52. This version tries to emulate get_headers() function at PHP4. I think it works fairly well, and is simple. It is not the best emulation available, but it works.

Features:
- supports (and requires) full URLs.
- supports changing of default port in URL.
- stops downloading from socket as soon as end-of-headers is detected.

Limitations:
- only gets the root URL (see line with "GET / HTTP/1.1").
- don't support HTTPS (nor the default HTTPS port).
*/

if(!function_exists('get_headers'))
{
    function get_headers($url,$format=0)
    {
        $url=parse_url($url);
        $end = "\r\n\r\n";
        $fp = fsockopen($url['host'], (empty($url['port'])?80:$url['port']), $errno, $errstr, 30);
        if ($fp)
        {
            $out  = "GET / HTTP/1.1\r\n";
            $out .= "Host: ".$url['host']."\r\n";
            $out .= "Connection: Close\r\n\r\n";
            $var  = '';
            fwrite($fp, $out);
            while (!feof($fp))
            {
                $var.=fgets($fp, 1280);
                if(strpos($var,$end))
                    break;
            }
            fclose($fp);

            $var=preg_replace("/\r\n\r\n.*\$/",'',$var);
            $var=explode("\r\n",$var);
            if($format)
            {
                foreach($var as $i)
                {
                    if(preg_match('/^([a-zA-Z -]+): +(.*)$/',$i,$parts))
                        $v[$parts[1]]=$parts[2];
                }
                return $v;
            }
            else
                return $var;
        }
    }
}

Les deux auraient un résultat similaire à:

Array
(
    [0] => HTTP/1.1 200 OK
    [Date] => Sat, 29 May 2004 12:28:14 GMT
    [Server] => Apache/1.3.27 (Unix)  (Red-Hat/Linux)
    [Last-Modified] => Wed, 08 Jan 2003 23:11:55 GMT
    [ETag] => "3f80f-1b6-3e1cb03b"
    [Accept-Ranges] => bytes
    [Content-Length] => 438
    [Connection] => close
    [Content-Type] => text/html
)

Par conséquent, vous pouvez simplement vérifier que la réponse d'en-tête était OK, par exemple:

$headers = get_headers($url, 1);
if ($headers[0] == 'HTTP/1.1 200 OK') {
//valid 
}

if ($headers[0] == 'HTTP/1.1 301 Moved Permanently') {
//moved or redirect page
}

Codes et définitions du W3C

Asciant
la source
J'ai apporté quelques améliorations au formatage de votre réponse, j'ai également ajouté la possibilité pour https: get_headers($https_url,1,443);je suis sûr que cela fonctionnera bien que ce ne soit pas dans la get_headers()fonction standard .. N'hésitez pas à le tester et à lui répondre avec un statut.
JamesM-SiteGen
1
belle solution de contournement pour php4, mais pour des cas comme celui-ci, nous avons la méthode HEAD http.
vidstige
Donc, ce serait en fait plus rapide que la méthode curl?
VOL le
4
Cette solution n'est pas valide lorsque l'URL cible redirige vers 404. Dans ce cas, $ headers [0] sera un code de redirection, et le code 404 final sera ajouté quelque part plus tard dans le tableau de retour.
roomcays
1
Cela finit par être plus difficile que cela ne vaut la peine en php de filtrer le code réel de la chaîne résultante, en essayant simplement de traiter le code d'état dans un script, au lieu de faire écho au résultat pour la lecture.
Kzqai
37

Avec le code de strager, vous pouvez également vérifier le CURLINFO_HTTP_CODE pour d'autres codes. Certains sites Web ne signalent pas un 404, ils redirigent simplement vers une page 404 personnalisée et renvoient 302 (redirection) ou quelque chose de similaire. Je l'ai utilisé pour vérifier si un fichier réel (par exemple, robots.txt) existait sur le serveur ou non. Il est clair que ce type de fichier ne provoquerait pas de redirection s'il existait, mais s'il ne le faisait pas, il redirigerait vers une page 404, qui, comme je l'ai déjà dit, peut ne pas avoir de code 404.

function is_404($url) {
    $handle = curl_init($url);
    curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);

    /* Get the HTML or whatever is linked in $url. */
    $response = curl_exec($handle);

    /* Check for 404 (file not found). */
    $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
    curl_close($handle);

    /* If the document has loaded successfully without any redirection or error */
    if ($httpCode >= 200 && $httpCode < 300) {
        return false;
    } else {
        return true;
    }
}
Aram Kocharyan
la source
5
+1 pour l'utilisation de codes HTTP "succès" au lieu de 404 ... L'utilisateur peut obtenir un 408 Request Timeout, pas un404
guillaume
A travaillé comme un charme. J'utilise ceci pour vérifier si un article sur ebay est toujours en ligne.
Nerdkowski
Pour ceux qui s'attendent à ce que le code ci-dessus fonctionne avec https, essayez d'ajouter ce qui suit:curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, FALSE);
Kirk Hammett
mais cela renverrait également 404 = true s'il y a une redirection 302 légitime?
Robert Sinclair
22

Comme le suggère Strager, examinez l'utilisation de cURL. Vous pouvez également être intéressé par la configuration de CURLOPT_NOBODY avec curl_setopt pour ignorer le téléchargement de la page entière (vous voulez juste les en-têtes).

Beau Simensen
la source
1
+1 pour me mentionner ^ W ^ Wfournissant une alternative plus efficace, dans le cas où seul l'en-tête doit être vérifié. =]
strager
16

Si vous recherchez une solution la plus simple et celle que vous pouvez essayer en une seule fois sur php5, faites

file_get_contents('www.yoursite.com');
//and check by echoing
echo $http_response_header[0];
Nasaralla
la source
3
btw, si vous faites cela et les URL 404, un avertissement est levé, provoquant une sortie.
Chris K
plus facile à faire $ isExists = @file_get_contents ('www.yoursite.com'); if ($ isExists! == true) {echo "yields 404"}
Tebe
mettre une attrape d'essai, puis manipuler la 404 avec attrape
Garet Claborn
7

J'ai trouvé cette réponse ici :

if(($twitter_XML_raw=file_get_contents($timeline))==false){
    // Retrieve HTTP status code
    list($version,$status_code,$msg) = explode(' ',$http_response_header[0], 3);

    // Check the HTTP Status code
    switch($status_code) {
        case 200:
                $error_status="200: Success";
                break;
        case 401:
                $error_status="401: Login failure.  Try logging out and back in.  Password are ONLY used when posting.";
                break;
        case 400:
                $error_status="400: Invalid request.  You may have exceeded your rate limit.";
                break;
        case 404:
                $error_status="404: Not found.  This shouldn't happen.  Please let me know what happened using the feedback link above.";
                break;
        case 500:
                $error_status="500: Twitter servers replied with an error. Hopefully they'll be OK soon!";
                break;
        case 502:
                $error_status="502: Twitter servers may be down or being upgraded. Hopefully they'll be OK soon!";
                break;
        case 503:
                $error_status="503: Twitter service unavailable. Hopefully they'll be OK soon!";
                break;
        default:
                $error_status="Undocumented error: " . $status_code;
                break;
    }

Essentiellement, vous utilisez la méthode "file get contents" pour récupérer l'URL, qui remplit automatiquement la variable d'en-tête de réponse http avec le code d'état.

Ross
la source
2
Intéressant - je n'avais jamais entendu parler de cette magie mondiale auparavant. php.net/manual/en/reserved.variables.httpresponseheader.php
Frank Farmer
2
ironie - le lien est un 404
Hamzah Malik
6

Cela vous donnera vrai si l'url ne renvoie pas 200 OK

function check_404($url) {
   $headers=get_headers($url, 1);
   if ($headers[0]!='HTTP/1.1 200 OK') return true; else return false;
}
Juergen
la source
C'est beaucoup plus rapide que d'utiliser cURL, si vous voulez faire une simple vérification booléenne sur une URL. Je vous remercie.
Drmzindec
5

addendum; testé ces 3 méthodes en tenant compte des performances.

Le résultat, au moins dans mon environnement de test:

Curl gagne

Ce test est effectué en considérant que seuls les en-têtes (noBody) sont nécessaires. Testez-vous:

$url = "http://de.wikipedia.org/wiki/Pinocchio";

$start_time = microtime(TRUE);
$headers = get_headers($url);
echo $headers[0]."<br>";
$end_time = microtime(TRUE);
echo $end_time - $start_time."<br>";


$start_time = microtime(TRUE);
$response = file_get_contents($url);
echo $http_response_header[0]."<br>";
$end_time = microtime(TRUE);
echo $end_time - $start_time."<br>";

$start_time = microtime(TRUE);
$handle = curl_init($url);
curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($handle, CURLOPT_NOBODY, 1); // and *only* get the header 
/* Get the HTML or whatever is linked in $url. */
$response = curl_exec($handle);
/* Check for 404 (file not found). */
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
// if($httpCode == 404) {
    // /* Handle 404 here. */
// }
echo $httpCode."<br>";
curl_close($handle);
$end_time = microtime(TRUE);
echo $end_time - $start_time."<br>";
Email
la source
3

Comme indice supplémentaire de la grande réponse acceptée:

Lors de l'utilisation d'une variante de la solution proposée, j'ai eu des erreurs à cause du paramètre php 'max_execution_time'. Donc ce que j'ai fait était le suivant:

set_time_limit(120);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_NOBODY, true);
$result = curl_exec($curl);
set_time_limit(ini_get('max_execution_time'));
curl_close($curl);

J'ai d'abord défini la limite de temps sur un nombre de secondes plus élevé, à la fin je l'ai remise à la valeur définie dans les paramètres php.

Markus
la source
hhhmmmm ... en plus ... votre code consomme moins de ressources car vous ne renvoyez pas le contenu ... quand même si vous pouvez ajouter le transfert de retour à false, vous pouvez économiser beaucoup de ressources lorsque les personnes utilisant plusieurs appels ... les débutants ne pensent pas beaucoup et donc c'est la raison de 40 votes ... c'est bien ...
Jayapal Chandran
3
<?php

$url= 'www.something.com';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, true);   
curl_setopt($ch, CURLOPT_NOBODY, true);    
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.4");
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$output = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);


echo $httpcode;
?>
Melbin Mathew Antony
la source
3

Voici une courte solution.

$handle = curl_init($uri);
curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($handle,CURLOPT_HTTPHEADER,array ("Accept: application/rdf+xml"));
curl_setopt($handle, CURLOPT_NOBODY, true);
curl_exec($handle);
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
if($httpCode == 200||$httpCode == 303) 
{
    echo "you might get a reply";
}
curl_close($handle);

Dans votre cas, vous pouvez application/rdf+xmlutiliser ce que vous utilisez.

Andreas
la source
2

Cette fonction renvoie le code de statut d'une URL en PHP 7:

/**
 * @param string $url
 * @return int
 */
function getHttpResponseCode(string $url): int
{
    $headers = get_headers($url);
    return substr($headers[0], 9, 3);
}

Exemple:

echo getHttpResponseCode('https://www.google.com');
//displays: 200
Sebastian Viereck
la source
1

Vous pouvez également utiliser ce code pour voir l'état de n'importe quel lien:

<?php

function get_url_status($url, $timeout = 10) 
{
$ch = curl_init();
// set cURL options
$opts = array(CURLOPT_RETURNTRANSFER => true, // do not output to browser
            CURLOPT_URL => $url,            // set URL
            CURLOPT_NOBODY => true,         // do a HEAD request only
            CURLOPT_TIMEOUT => $timeout);   // set timeout
curl_setopt_array($ch, $opts);
curl_exec($ch); // do it!
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); // find HTTP status
curl_close($ch); // close handle
echo $status; //or return $status;
    //example checking
    if ($status == '302') { echo 'HEY, redirection';}
}

get_url_status('http://yourpage.comm');
?>
T.Todua
la source
0

c'est juste une tranche de code, j'espère que cela fonctionnera pour vous

            $ch = @curl_init();
            @curl_setopt($ch, CURLOPT_URL, 'http://example.com');
            @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1");
            @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
            @curl_setopt($ch, CURLOPT_TIMEOUT, 10);

            $response       = @curl_exec($ch);
            $errno          = @curl_errno($ch);
            $error          = @curl_error($ch);

                    $response = $response;
                    $info = @curl_getinfo($ch);
return $info['http_code'];

la source
0

Voici un moyen!

<?php

$url = "http://www.google.com";

if(@file_get_contents($url)){
echo "Url Exists!";
} else {
echo "Url Doesn't Exist!";
}

?>

Ce simple script fait simplement une demande à l'URL pour son code source. Si la requête est terminée avec succès, elle affichera "L'URL existe!". Sinon, il affichera "L'URL n'existe pas!".

Hayden Frobenius
la source