Łatwy sposób na przetestowanie adresu URL dla 404 w PHP?


152

Uczę się podstawowych zasad skrobania i odkryłem, że czasami adresy URL, które wprowadzam do mojego kodu, zwracają 404, co powoduje utratę całej reszty kodu.

Więc potrzebuję testu na górze kodu, aby sprawdzić, czy adres URL zwraca 404, czy nie.

Wydawałoby się to dość prostym zadaniem, ale Google nie daje mi żadnych odpowiedzi. Martwię się, że szukam niewłaściwych rzeczy.

Jeden blog polecił mi to:

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

a następnie sprawdź, czy $ ważne, jeśli jest puste, czy nie.

Ale myślę, że adres URL, który sprawia mi problemy, ma przekierowanie, więc $ valid jest puste dla wszystkich wartości. A może robię coś złego.

Przyjrzałem się również „żądaniu głównemu”, ale nie znalazłem jeszcze żadnych przykładów kodu, którymi mógłbym się bawić lub wypróbować.

Propozycje? A o co chodzi z curl?

Odpowiedzi:


276

Jeśli używasz curlpowiązań PHP , możesz sprawdzić kod błędu za pomocą 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. */

1
Nie znam jeszcze cURL, więc brakuje mi kilku pojęć. Co mam zrobić ze zmienną $ response poniżej? Co zawiera?

1
@bflora, popełniłem błąd w kodzie. (Naprawi się za sekundę.) Możesz zobaczyć dokumentację curl_exec na stronie PHP.
strager

4
Odpowiedź @bflora $ będzie zawierała zawartość $ url, dzięki czemu możesz wykonywać dodatkowe czynności, takie jak sprawdzanie zawartości pod kątem określonych ciągów lub cokolwiek innego. W twoim przypadku zależy Ci tylko na stanie 404, więc prawdopodobnie nie musisz się martwić o odpowiedź $.
Beau Simensen

5
Co jeśli chcesz po prostu załadować nagłówki zamiast pobierać cały plik?
patrick

13
@patrick musisz określić curl_setopt($handle, CURLOPT_NOBODY, true);przed uruchomieniemcurl_exec
użytkownik

101

Jeśli masz uruchomione php5, możesz użyć:

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

Alternatywnie w przypadku php4 użytkownik dodał:

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

Oba miałyby wynik podobny do:

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
)

Dlatego możesz po prostu sprawdzić, czy odpowiedź nagłówka była OK, np .:

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

Kody i definicje W3C


Zrobiłem kilka poprawek formatujących twoją odpowiedź, dodałem również możliwość https: get_headers($https_url,1,443);jestem pewien, że będzie działać, chociaż nie jest w standardowej get_headers()funkcji. Zapraszam do przetestowania i nadania statusu.
JamesM-SiteGen

1
niezłe obejście dla php4, ale w takich przypadkach mamy metodę HEAD http.
vidstige

Więc to faktycznie byłoby szybsze niż metoda zwijania?
FLY

4
To rozwiązanie nie jest poprawne, gdy docelowy adres URL przekierowuje na 404. W tym przypadku $ headers [0] będzie kodem przekierowania, a końcowy kod 404 zostanie dołączony gdzieś później w zwracanej tablicy.
roomcays

1
Kończy się to większym kłopotem niż jest to warte w php, aby odfiltrować rzeczywisty kod z wynikowego ciągu, gdy próbujesz po prostu poradzić sobie z kodem stanu w skrypcie, w przeciwieństwie do powtórzenia wyniku do odczytu.
Kzqai,

37

Za pomocą kodu stragera możesz również sprawdzić CURLINFO_HTTP_CODE pod kątem innych kodów. Niektóre witryny nie zgłaszają błędu 404, a raczej po prostu przekierowują na niestandardową stronę 404 i zwracają 302 (przekierowanie) lub coś podobnego. Użyłem tego do sprawdzenia, czy rzeczywisty plik (np. Robots.txt) istnieje na serwerze, czy nie. Oczywiście ten rodzaj pliku nie spowodowałby przekierowania, gdyby istniał, ale gdyby nie, przekierowałby do strony 404, która, jak powiedziałem wcześniej, może nie mieć kodu 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 za użycie kodów HTTP „sukcesu” zamiast 404 ... Użytkownik może otrzymać a 408 Request Timeout, a nie404
guillaume

Pracował lika urok. Używam tego, aby sprawdzić, czy artykuł w serwisie eBay jest nadal dostępny online.
Nerdkowski

Dla tych, którzy oczekują, że powyższy kod będzie działał z https, spróbuj dodać następujące elementy:curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, FALSE);
Kirk Hammett,

ale zwróci to również 404 = true, jeśli istnieje uzasadnione przekierowanie 302?
Robert Sinclair

22

Jak sugeruje strager, przyjrzyj się używaniu cURL. Możesz również być zainteresowany ustawieniem CURLOPT_NOBODY za pomocą curl_setopt, aby pomijał pobieranie całej strony (chcesz tylko nagłówków).


1
+1 za wzmiankę o mnie ^ W ^ W zapewniając bardziej wydajną alternatywę w przypadku, gdy trzeba sprawdzić tylko nagłówek. =]
strager

16

Jeśli szukasz najłatwiejszego rozwiązania i tego, które możesz wypróbować za jednym razem, php5 zrób

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

3
btw, jeśli to zrobisz i adresy URL 404, zostanie wyświetlone ostrzeżenie, powodując wyjście.
Chris K

łatwiejsze do zrobienia $ isExists = @file_get_contents ('www.yoursite.com'); if ($ isExists! == true) {echo "daje 404"}
Tebe,

spróbuj złapać, a następnie
obsłuż

7

Znalazłem tę odpowiedź tutaj :

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

Zasadniczo do pobierania adresu URL służy metoda „pobierz zawartość pliku”, która automatycznie wypełnia zmienną nagłówka odpowiedzi http kodem stanu.


2
Interesujące - nigdy wcześniej nie słyszałem o tej magii globalnej. php.net/manual/en/reserved.variables.httpresponseheader.php
Frank Farmer

2
ironia - link to 404
Hamzah Malik

6

To da ci prawdę, jeśli url nie zwraca 200 OK

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

Jest to znacznie szybsze niż użycie cURL, jeśli chcesz wykonać proste sprawdzenie bool na adresie URL. Dziękuję Ci.
Drmzindec

5

addendum; przetestowałem te 3 metody pod kątem wydajności.

Wynik, przynajmniej w moim środowisku testowym:

Curl wygrywa

Ten test jest wykonywany z uwzględnieniem, że potrzebne są tylko nagłówki (noBody). Sprawdź się:

$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

Jako dodatkowa wskazówka do świetnie przyjętej odpowiedzi:

Podczas korzystania z odmiany proponowanego rozwiązania otrzymałem błędy z powodu ustawienia php „max_execution_time”. Oto co zrobiłem:

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

Najpierw ustawiłem limit czasu na większą liczbę sekund, na koniec ustawiłem go z powrotem na wartość zdefiniowaną w ustawieniach php.


hhhmmmm ... poza tym ... twój kod zużywa mniej zasobów, ponieważ nie zwracasz treści ... nadal, jeśli możesz dodać transfer zwrotny do false, możesz zaoszczędzić wiele zasobów, gdy ludzie używają wielu połączeń ... początkujący nie myślą dużo i to jest powód dla 40 głosów w górę ... w porządku ...
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

Oto krótkie rozwiązanie.

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

W twoim przypadku możesz zmienić application/rdf+xmlto, czego używasz.


2

Ta funkcja zwraca kod stanu adresu URL w PHP 7:

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

Przykład:

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

1

Możesz również użyć tego kodu, aby zobaczyć stan dowolnego linku:

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

to tylko kawałek kodu, nadzieja działa dla Ciebie

            $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

Oto sposób!

<?php

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

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

?>

Ten prosty skrypt po prostu wysyła żądanie do adresu URL w celu uzyskania kodu źródłowego. Jeśli żądanie zakończy się pomyślnie, wyświetli komunikat „URL istnieje!”. Jeśli nie, wyświetli komunikat „URL nie istnieje!”.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.