Un modo semplice per testare un URL per 404 in PHP?


152

Mi sto insegnando un po 'di raschiatura di base e ho scoperto che a volte l'URL che inserisco nel mio codice restituisce 404, il che rende tutto il resto del mio codice.

Quindi ho bisogno di un test nella parte superiore del codice per verificare se l'URL restituisce 404 o meno.

Sembrerebbe un compito piuttosto diretto, ma Google non mi sta dando alcuna risposta. Temo di cercare cose sbagliate.

Un blog mi consiglia di utilizzare questo:

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

e quindi prova per vedere se $ valido se vuoto o no.

Ma penso che l'URL che mi sta dando problemi abbia un reindirizzamento, quindi $ valid sta diventando vuoto per tutti i valori. O forse sto facendo qualcos'altro che non va.

Ho anche esaminato una "richiesta principale", ma non ho ancora trovato alcun esempio di codice reale con cui posso giocare o provare.

Suggerimenti? E di cosa si tratta il ricciolo?

Risposte:


276

Se stai usando i curlbinding di PHP , puoi controllare il codice di errore usando curl_getinfocome tale:

$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. */

1
Non ho ancora familiarità con CURL, quindi mi mancano alcuni concetti. Cosa devo fare con la variabile $ response in basso? Cosa contiene?

1
@bflora, ho fatto un errore nel codice. (Risolverà tra un secondo.) Puoi vedere la documentazione per curl_exec sul sito di PHP.
Strager

4
@bflora $ response conterrà il contenuto di $ url in modo da poter fare cose aggiuntive come controllare il contenuto per stringhe specifiche o altro. Nel tuo caso, ti preoccupi solo dello stato 404, quindi probabilmente non devi preoccuparti di $ response.
Beau Simensen,

5
Cosa succede se si desidera caricare solo le intestazioni anziché scaricare l'intero file?
patrick,

13
@patrick quindi è necessario specificare curl_setopt($handle, CURLOPT_NOBODY, true);prima di eseguirecurl_exec
utente

101

Se stai usando php5 puoi usare:

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

In alternativa con php4 un utente ha contribuito come segue:

/**
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;
        }
    }
}

Entrambi avrebbero un risultato simile a:

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
)

Pertanto, è possibile verificare che la risposta dell'intestazione sia corretta, ad esempio:

$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
}

Codici e definizioni W3C


Ho apportato alcuni miglioramenti alla formattazione della tua risposta, ho anche aggiunto la possibilità di https: get_headers($https_url,1,443);sono sicuro che funzionerà anche se non è nella get_headers()funzione standard . Sentiti libero di testarlo e rispondere con uno stato per esso.
JamesM-SiteGen,

1
bella soluzione per php4, ma per casi come questo abbiamo il metodo http HEAD.
vidstige,

Quindi questo sarebbe effettivamente più veloce del metodo di arricciatura?
VOLA

4
Questa soluzione non è valida quando l'URL di destinazione reindirizza a 404. In questo caso $ headers [0] sarà un codice di reindirizzamento e il codice 404 finale verrà aggiunto da qualche parte in seguito nella matrice di ritorno.
roomcays

1
Questo finisce per essere più problematico di quanto valga la pena in php filtrare il codice effettivo dalla stringa risultante, quando si cerca di gestire semplicemente il codice di stato in uno script, invece di echeggiare il risultato per la lettura.
Kzqai,

37

Con il codice dello strager, puoi anche controllare CURLINFO_HTTP_CODE per altri codici. Alcuni siti Web non riportano un 404, ma semplicemente reindirizzano a una pagina 404 personalizzata e restituiscono 302 (reindirizzamento) o qualcosa di simile. Ho usato questo per verificare se sul server esisteva o meno un file reale (es. Robots.txt). Chiaramente questo tipo di file non provocherebbe un reindirizzamento se esistesse, ma in caso contrario reindirizzerebbe a una pagina 404, che come ho detto prima potrebbe non avere un codice 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;
    }
}

5
+1 per l'uso di codici HTTP "successo" invece di 404 ... L'utente può ottenere un 408 Request Timeout, non un404
guillaume

Ha funzionato come un fascino. Lo uso per verificare se un articolo su eBay è ancora online.
Nerdkowski,

Per coloro che si aspettano che il codice sopra funzioni con https, prova ad aggiungere quanto segue:curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, FALSE);
Kirk Hammett,

ma ciò restituirebbe anche 404 = true se esiste un reindirizzamento 302 legittimo?
Robert Sinclair,

22

Come suggerisce Strager, cerca di usare cURL. Potresti anche essere interessato a impostare CURLOPT_NOBODY con curl_setopt per saltare il download dell'intera pagina (vuoi solo le intestazioni).


1
+1 per avermi menzionato ^ W ^ Fornendo un'alternativa più efficiente, nel caso in cui solo l'intestazione debba essere controllata. =]
strager

16

Se stai cercando una soluzione più semplice e quella che puoi provare in una volta vai su php5 do

file_get_contents('www.yoursite.com');
//and check by echoing
echo $http_response_header[0];

3
tra l'altro, se si esegue questa operazione e l'URL 404, viene generato un avviso che causa l'output.
Chris K,

più facile da fare $ isExists = @file_get_contents ('www.yoursite.com'); if ($ isExists! == true) {echo "produce 404"}
Tebe

fai

7

Ho trovato questa risposta qui :

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;
    }

In sostanza, si utilizza il metodo "file get contents" per recuperare l'URL, che popola automaticamente la variabile di intestazione della risposta http con il codice di stato.


2
Interessante: non avevo mai sentito parlare di quel magico mondo prima d'ora. php.net/manual/en/reserved.variables.httpresponseheader.php
Frank Farmer,

2
ironia - il collegamento è un 404
Hamzah Malik,

6

Questo ti darà vero se l'URL non restituisce 200 OK

function check_404($url) {
   $headers=get_headers($url, 1);
   if ($headers[0]!='HTTP/1.1 200 OK') return true; else return false;
}

Questo è molto più veloce dell'uso di cURL, se vuoi fare un semplice controllo bool su un url. Grazie.
Drmzindec,

5

addendum; testato quei 3 metodi considerando le prestazioni.

Il risultato, almeno nel mio ambiente di test:

Il ricciolo vince

Questo test viene eseguito considerando che sono necessarie solo le intestazioni (noBody). Mettiti alla prova:

$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>";

3

Come ulteriore suggerimento per la grande risposta accettata:

Quando utilizzo una variante della soluzione proposta, ho ricevuto degli errori a causa dell'impostazione php 'max_execution_time'. Quindi quello che ho fatto è stato il seguente:

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);

Innanzitutto ho impostato il limite di tempo su un numero maggiore di secondi, alla fine l'ho riportato al valore definito nelle impostazioni php.


hhhmmmm ... inoltre ... il tuo codice consuma meno risorse perché non stai restituendo il contenuto ... ancora se potessi aggiungere il trasferimento di ritorno a false allora puoi risparmiare molte risorse quando le persone che usano più chiamate ... i principianti non pensano molto e così è la ragione per 40 voti positivi ... va bene ...
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;
?>

3

Ecco una soluzione breve.

$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);

Nel tuo caso, puoi passare application/rdf+xmla qualunque cosa tu usi.


2

Questa funzione restituisce il codice di stato di un URL in PHP 7:

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

Esempio:

echo getHttpResponseCode('https://www.google.com');
//displays: 200

1

Puoi usare anche questo codice per vedere lo stato di qualsiasi link:

<?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');
?>

0

questo è solo un pezzo di codice, la speranza funziona per te

            $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'];

0

Ecco un modo!

<?php

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

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

?>

Questo semplice script fa semplicemente una richiesta all'URL per il suo codice sorgente. Se la richiesta viene completata correttamente, verrà visualizzato "URL esiste!". In caso contrario, verrà visualizzato "L'URL non esiste!".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.