Einfache Möglichkeit, eine URL für 404 in PHP zu testen?

152

Ich bringe mir selbst ein grundlegendes Scraping bei und habe festgestellt, dass manchmal die URLs, die ich in meinen Code eingebe, 404 zurückgeben, wodurch der gesamte Rest meines Codes zusammengefasst wird.

Ich brauche also einen Test oben im Code, um zu überprüfen, ob die URL 404 zurückgibt oder nicht.

Dies scheint eine ziemlich einfache Aufgabe zu sein, aber Google gibt mir keine Antworten. Ich mache mir Sorgen, ich suche nach den falschen Sachen.

Ein Blog empfahl mir Folgendes:

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

und testen Sie dann, ob $ gültig ist, wenn es leer ist oder nicht.

Aber ich denke, die URL, die mir Probleme bereitet, hat eine Umleitung, sodass $ valid für alle Werte leer ist. Oder vielleicht mache ich etwas anderes falsch.

Ich habe auch eine "Kopfanfrage" geprüft, aber ich habe noch keine tatsächlichen Codebeispiele gefunden, mit denen ich spielen oder ausprobieren kann.

Vorschläge? Und was ist das mit Locken?

große Nase
quelle

Antworten:

276

Wenn Sie PHP- curlBindungen verwenden , können Sie den Fehlercode folgendermaßen überprüfen curl_getinfo:

$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
quelle
1
Ich bin noch nicht mit cURL vertraut, daher fehlen mir einige Konzepte. Was mache ich mit der unten stehenden Variablen $ response? Was enthält es?
1
@bflora, ich habe einen Fehler im Code gemacht. (Wird in einer Sekunde behoben.) Sie können die Dokumentation für curl_exec auf der PHP-Site sehen.
Strager
4
@bflora $ response enthält den Inhalt der $ url, sodass Sie zusätzliche Dinge tun können, z. B. den Inhalt auf bestimmte Zeichenfolgen oder was auch immer überprüfen. In Ihrem Fall interessiert Sie nur der 404-Status, sodass Sie sich wahrscheinlich keine Gedanken über die Antwort von $ machen müssen.
Beau Simensen
5
Was ist, wenn Sie nur Header laden möchten, anstatt die gesamte Datei herunterzuladen?
Patrick
13
@patrick dann müssen Sie angeben, curl_setopt($handle, CURLOPT_NOBODY, true);bevor Sie ausführencurl_exec
Benutzer
101

Wenn Sie PHP5 ausführen, können Sie Folgendes verwenden:

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

Alternativ hat ein Benutzer mit php4 Folgendes beigetragen:

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

Beide hätten ein ähnliches Ergebnis wie:

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
)

Daher können Sie einfach überprüfen, ob die Header-Antwort in Ordnung war, z.

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

W3C-Codes und Definitionen

Aufsteigend
quelle
Ich habe einige Formatierungsverbesserungen an Ihrer Antwort vorgenommen und die Fähigkeit für https hinzugefügt: get_headers($https_url,1,443);Ich bin sicher, dass es funktionieren wird, obwohl es nicht in der Standardfunktion enthalten get_headers()ist. Sie können es gerne testen und mit einem Status dafür antworten.
JamesM-SiteGen
1
nette Umgehung für PHP4, aber für Fälle wie diesen haben wir die HEAD http-Methode.
vidstige
Das wäre also tatsächlich schneller als die Curl-Methode?
Fliegen Sie
4
Diese Lösung ist nicht gültig, wenn die Ziel-URL auf 404 umgeleitet wird. In diesem Fall ist $ headers [0] ein Umleitungscode, und der endgültige 404-Code wird später im zurückgegebenen Array angehängt.
Roomcays
1
Dies ist mehr Mühe als es in PHP wert ist, den tatsächlichen Code aus der resultierenden Zeichenfolge herauszufiltern, wenn versucht wird, einfach mit dem Statuscode in einem Skript umzugehen, anstatt das Ergebnis zum Lesen wiederzugeben.
Kzqai
37

Mit dem Code von strager können Sie auch CURLINFO_HTTP_CODE auf andere Codes überprüfen. Einige Websites melden keine 404, sondern leiten einfach auf eine benutzerdefinierte 404-Seite um und geben 302 (Weiterleitung) oder ähnliches zurück. Ich habe dies verwendet, um zu überprüfen, ob eine tatsächliche Datei (z. B. robots.txt) auf dem Server vorhanden ist oder nicht. Natürlich würde diese Art von Datei keine Umleitung verursachen, wenn sie existiert, aber wenn sie nicht vorhanden wäre, würde sie auf eine 404-Seite umleiten, die, wie ich bereits sagte, möglicherweise keinen 404-Code hat.

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
quelle
5
+1 für die Verwendung von "Erfolg" HTTP-Codes anstelle von 404 ... Der Benutzer kann eine 408 Request Timeout, nicht eine404
Guillaume
Arbeitete wie ein Zauber. Ich benutze dies, um zu überprüfen, ob ein Artikel bei ebay noch online ist.
Nerdkowski
Für diejenigen, die erwarten, dass der obige Code mit https funktioniert, versuchen Sie Folgendes hinzuzufügen:curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, FALSE);
Kirk Hammett
Dies würde aber auch 404 = true zurückgeben, wenn es eine legitime 302-Weiterleitung gibt.
Robert Sinclair
22

Wie strager vorschlägt, sollten Sie cURL verwenden. Möglicherweise möchten Sie auch CURLOPT_NOBODY mit curl_setopt so einstellen , dass das Herunterladen der gesamten Seite übersprungen wird (Sie möchten nur die Überschriften).

Beau Simensen
quelle
1
+1 für die Erwähnung von mir ^ W ^ Bietet eine effizientere Alternative für den Fall, dass nur der Header überprüft werden muss. =]
Strager
16

Wenn Sie nach einer einfachsten Lösung suchen und die, die Sie auf einmal ausprobieren können, tun Sie dies auf PHP5

file_get_contents('www.yoursite.com');
//and check by echoing
echo $http_response_header[0];
Nasaralla
quelle
3
Übrigens, wenn Sie dies und die URL 404 tun, wird eine Warnung ausgelöst, die eine Ausgabe verursacht.
Chris K
einfacher zu machen $ isExists = @file_get_contents ('www.yoursite.com'); if ($ isExists! == true) {echo "ergibt 404"}
Tebe
Versuchen Sie es mit einem Fang und behandeln Sie den 404 mit catch
Garet Claborn
7

Ich habe diese Antwort hier gefunden :

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

Im Wesentlichen verwenden Sie die Methode "Datei abrufen von Inhalten", um die URL abzurufen, die die http-Antwortheadervariable automatisch mit dem Statuscode auffüllt.

Ross
quelle
2
Interessant - ich hatte noch nie von dieser globalen Magie gehört. php.net/manual/en/reserved.variables.httpresponseheader.php
Frank Farmer
2
Ironie - der Link ist ein 404
Hamzah Malik
6

Dies gibt Ihnen true, wenn die URL nicht 200 OK zurückgibt

function check_404($url) {
   $headers=get_headers($url, 1);
   if ($headers[0]!='HTTP/1.1 200 OK') return true; else return false;
}
Jürgen
quelle
Dies ist viel schneller als die Verwendung von cURL, wenn Sie eine einfache Bool-Überprüfung einer URL durchführen möchten. Danke dir.
Drmzindec
5

Nachtrag, testete diese 3 Methoden unter Berücksichtigung der Leistung.

Das Ergebnis, zumindest in meiner Testumgebung:

Curl gewinnt

Dieser Test wird unter der Berücksichtigung durchgeführt, dass nur die Header (noBody) benötigt werden. Teste dich selbst:

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

Als zusätzlichen Hinweis auf die sehr akzeptierte Antwort:

Bei der Verwendung einer Variation der vorgeschlagenen Lösung wurden Fehler aufgrund der PHP-Einstellung 'max_execution_time' angezeigt. Also habe ich Folgendes getan:

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

Zuerst habe ich das Zeitlimit auf eine höhere Anzahl von Sekunden eingestellt, am Ende habe ich es auf den in den PHP-Einstellungen definierten Wert zurückgesetzt.

Markus
quelle
hhhmmmm ... außerdem ... verbraucht Ihr Code weniger Ressourcen, weil Sie den Inhalt nicht zurückgeben ... Wenn Sie jedoch die Rückübertragung auf false setzen könnten, können Sie viel Ressourcen sparen, wenn Benutzer mehrere Anrufe verwenden ... Anfänger denken nicht viel und so ist es der Grund für 40 Stimmen ... das ist in Ordnung ...
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
quelle
3

Hier ist eine kurze Lösung.

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

In Ihrem Fall können Sie zu dem wechseln application/rdf+xml, was Sie verwenden.

Andreas
quelle
2

Diese Funktion gibt den Statuscode einer URL in PHP 7 zurück:

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

Beispiel:

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

Sie können diesen Code auch verwenden, um den Status eines Links anzuzeigen:

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

Dies ist gerecht und ein Stück Code, Hoffnung funktioniert für Sie

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

quelle
0

Hier ist ein Weg!

<?php

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

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

?>

Dieses einfache Skript fordert einfach die URL nach ihrem Quellcode an. Wenn die Anforderung erfolgreich abgeschlossen wurde, wird "URL Exists!" Ausgegeben. Wenn nicht, wird "URL existiert nicht!" Ausgegeben.

Hayden Frobenius
quelle