¿Una manera fácil de probar una URL para 404 en PHP?

152

Me estoy enseñando un poco de raspado básico y descubrí que a veces las URL que introduzco en mi código devuelven 404, lo que pega todo el resto de mi código.

Por lo tanto, necesito una prueba en la parte superior del código para verificar si la URL devuelve 404 o no.

Esto parecería una tarea bastante directa, pero Google no me da ninguna respuesta. Me preocupa que estoy buscando las cosas equivocadas.

Un blog me recomendó usar esto:

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

y luego prueba para ver si $ válido si está vacío o no.

Pero creo que la URL que me está dando problemas tiene una redirección, por lo que $ valid está vacío para todos los valores. O tal vez estoy haciendo algo más mal.

También examiné una "solicitud principal" pero aún no he encontrado ningún ejemplo de código real con el que pueda jugar o probar.

Sugerencias? ¿Y de qué se trata el rizo?

nariz grande
fuente

Respuestas:

276

Si está utilizando curlenlaces de PHP , puede verificar el código de error usando curl_getinfocomo tal:

$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. */
extraño
fuente
1
Todavía no estoy familiarizado con cURL, así que me faltan algunos conceptos. ¿Qué hago con la variable $ response a continuación? Que contiene
1
@bflora, cometí un error en el código. (Se solucionará en un segundo). Puede ver la documentación de curl_exec en el sitio de PHP.
extraño
44
@bflora $ response contendrá el contenido de $ url para que pueda hacer cosas adicionales como verificar el contenido para cadenas específicas o lo que sea. En su caso, solo le importa el estado 404, por lo que probablemente no necesite preocuparse por $ respuesta.
Beau Simensen el
55
¿Qué sucede si solo desea cargar los encabezados en lugar de descargar todo el archivo?
patrick
13
@patrick, entonces debe especificar curl_setopt($handle, CURLOPT_NOBODY, true);antes de ejecutarcurl_exec
usuario
101

Si estás ejecutando php5 puedes usar:

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

Alternativamente, con php4, un usuario ha contribuido lo siguiente:

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

Ambos tendrían un resultado similar 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
)

Por lo tanto, puede verificar si la respuesta del encabezado fue correcta, por ejemplo:

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

Códigos y definiciones del W3C

Asciant
fuente
Hice algunas mejoras de formato de su respuesta, también agregué la capacidad para https: get_headers($https_url,1,443);estoy seguro de que funcionará aunque no esté en la get_headers()función estándar . Siéntase libre de probarlo y responder con un estado para ello.
JamesM-SiteGen
1
buena solución para php4, pero para casos como este tenemos el método HEAD http.
vidstige
Entonces, ¿esto sería realmente más rápido que el método curl?
VUELO
44
Esta solución no es válida cuando la URL de destino redirige a 404. En este caso, $ headers [0] será un código de redireccionamiento, y el código 404 final se agregará en algún lugar más adelante en la matriz de retorno.
roomcays
1
Esto termina siendo más problemático de lo que vale en php para filtrar el código real de la cadena resultante, cuando se trata de tratar simplemente con el código de estado en un script, en lugar de hacer eco del resultado para la lectura.
Kzqai
37

Con el código de strager, también puede consultar el CURLINFO_HTTP_CODE para ver otros códigos. Algunos sitios web no informan un 404, sino que simplemente redirigen a una página 404 personalizada y devuelven 302 (redireccionar) o algo similar. Utilicé esto para verificar si un archivo real (por ejemplo, robots.txt) existía en el servidor o no. Claramente, este tipo de archivo no causaría una redirección si existiera, pero si no fuera así, redirigiría a una página 404, que como dije antes puede no tener un código 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
fuente
55
+1 por el uso de códigos HTTP "exitosos" en lugar de 404 ... El usuario puede obtener un 408 Request Timeout, no un404
guillaume
Trabajó lika un encanto. Lo uso para verificar si un artículo en eBay todavía está en línea.
Nerdkowski
Para aquellos que esperan que el código anterior funcione con https, intente agregar lo siguiente:curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, FALSE);
Kirk Hammett
pero esto también devolvería 404 = verdadero si hay una redirección 302 legítima?
Robert Sinclair el
22

Como sugiere Strager, considera el uso de cURL. También puede estar interesado en configurar CURLOPT_NOBODY con curl_setopt para omitir la descarga de toda la página (solo desea los encabezados).

Beau Simensen
fuente
1
+1 por mencionarme ^ W ^ Wproporcionando una alternativa más eficiente, en el caso de que solo sea necesario verificar el encabezado. =]
extraño
16

Si está buscando una solución más fácil y la que puede probar de una vez, vaya a php5 do

file_get_contents('www.yoursite.com');
//and check by echoing
echo $http_response_header[0];
Nasaralla
fuente
3
por cierto, si se hace esto y la URL 404, se genera una advertencia que genera una salida.
Chris K
más fácil de hacer $ isExists = @file_get_contents ('www.yoursite.com'); if ($ isExists! == true) {echo "produce 404"}
Tebe
poner una prueba de captura, luego manejar el 404 con captura
Garet Claborn
7

Encontré esta respuesta aquí :

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

Básicamente, utiliza el método "obtener contenido del archivo" para recuperar la URL, que rellena automáticamente la variable de encabezado de respuesta http con el código de estado.

Ross
fuente
2
Interesante: nunca antes había oído hablar de esa magia global. php.net/manual/en/reserved.variables.httpresponseheader.php
Frank Farmer
2
ironía - el enlace es un 404
Hamzah Malik
6

Esto le dará verdadero si la url no devuelve 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
fuente
Esto es mucho más rápido que usar cURL, si desea hacer una simple verificación de bool en una url. Gracias.
Drmzindec
5

anexo; probó esos 3 métodos considerando el rendimiento.

El resultado, al menos en mi entorno de prueba:

Curl gana

Esta prueba se realiza bajo la consideración de que solo se necesitan los encabezados (noBody). Pruébate:

$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
fuente
3

Como pista adicional a la gran respuesta aceptada:

Al usar una variación de la solución propuesta, recibí errores debido a la configuración de php 'max_execution_time'. Entonces, lo que hice fue lo siguiente:

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

Primero configuré el límite de tiempo a un número mayor de segundos, al final lo configuré nuevamente al valor definido en la configuración de php.

Markus
fuente
hhhmmmm ... además ... tu código consume menos recursos porque no estás devolviendo el contenido ... aún si pudieras agregar la transferencia de retorno a falso, entonces puedes ahorrar muchos recursos cuando las personas que usan múltiples llamadas ... los principiantes no piensan mucho y por lo que la razón de 40 votos positivos ... que ya 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
fuente
3

Aquí hay una pequeña solución.

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

En su caso, puede cambiar application/rdf+xmla lo que sea que use.

Andreas
fuente
2

Esta función devuelve el código de estado de una URL en PHP 7:

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

Ejemplo:

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

También puede usar este código para ver el estado de cualquier enlace:

<?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
fuente
0

esto es solo una porción de código, la esperanza funciona para ti

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

fuente
0

¡Aquí hay un camino!

<?php

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

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

?>

Este simple script simplemente hace una solicitud a la URL de su código fuente. Si la solicitud se completa con éxito, generará "¡URL existe!". Si no, generará "¡La URL no existe!".

Hayden Frobenius
fuente