Jak mogę się dowiedzieć, gdzie nastąpi przekierowanie przy użyciu cURL?


149

Próbuję sprawić, by curl podążał za przekierowaniem, ale nie mogę sprawić, by działało dobrze. Mam ciąg, który chcę wysłać jako parametr GET na serwer i uzyskać wynikowy adres URL.

Przykład:

Ciąg znaków = Kobold Vermin
Url = www.wowhead.com/search?q=Kobold+Worker

Jeśli przejdziesz do tego adresu URL, przekieruje Cię on do „www.wowhead.com/npc=257”. Chcę, aby curl zwrócił ten adres URL do mojego kodu PHP, abym mógł wyodrębnić „npc = 257” i użyć go.

Aktualny kod:

function npcID($name) {
    $urltopost = "http://www.wowhead.com/search?q=" . $name;
    $ch = curl_init();
    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_URL, $urltopost);
    curl_setopt($ch, CURLOPT_REFERER, "http://www.wowhead.com");
    curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type:application/x-www-form-urlencoded"));
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
    return curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
}

To jednak zwraca www.wowhead.com/search?q=Kobold+Worker, a nie www.wowhead.com/npc=257 .

Podejrzewam, że PHP wraca, zanim nastąpi zewnętrzne przekierowanie. Jak mogę to naprawić?


8
To jedno z najczęściej zadawanych pytań dotyczących „curl follow redirects”. Aby automatycznie śledzić przekierowania za pomocą curlpolecenia, przekaż flagę -Llub --location. Np.curl -L http://example.com/
Rob W

Odpowiedzi:


256

Aby cURL podążał za przekierowaniem, użyj:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

Eee ... Nie sądzę, że faktycznie wykonujesz zawijanie ... Spróbuj:

curl_exec($ch);

... po ustawieniu opcji, a przed curl_getinfo()rozmową.

EDYCJA: Jeśli chcesz tylko dowiedzieć się, dokąd przekierowuje strona, skorzystam z porady tutaj i po prostu użyj Curl, aby pobrać nagłówki i wyodrębnić z nich nagłówek Location:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
if (preg_match('~Location: (.*)~i', $result, $match)) {
   $location = trim($match[1]);
}

2
To sprawia, że ​​php podąża za przekierowaniem. Nie chcę podążać za przekierowaniem, chcę tylko znać adres URL przekierowanej strony.
Thomas Van Nuffel,

9
Och, więc tak naprawdę nie chcesz pobierać strony? Po prostu znajdź lokalizację? W takim przypadku proponuję taktykę zastosowaną tutaj: zzz.rezo.net/HowTo-Expand-Short-URLs.html - po prostu pobierz nagłówek ze strony, która przekierowuje, i pobierz z niego nagłówek Location :. Tak czy inaczej, nadal musisz wykonać exec (), aby Curl faktycznie zrobił cokolwiek ...
Matt Gibson,

1
Proponuję przyjrzeć się rozwiązaniu Luca Camillos poniżej, ponieważ to rozwiązanie nie uwzględnia wielu przekierowań.
Christian Engel

to rozwiązanie otwiera nową stronę internetową w tym samym adresie URL. Chcę zmienić adres URL również wraz z wysyłaniem parametrów do tego adresu URL. Jak mogę to osiągnąć?
amanpurohit

@MattGibson, gdy używam $ httpCode = curl_getinfo (uchwyt $, CURLINFO_HTTP_CODE); z CURLOPT_FOLLOWLOCATION ustawionym na true, jaki będzie kod http. Mam na myśli, czy będzie to dla pierwszego adresu URL, czy dla adresu przekierowania
Manigandan Arjunan

26

Dodaj tę linię, aby zwijać inicjalizację

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

i użyj getinfo przed curl_close

$redirectURL = curl_getinfo($ch,CURLINFO_EFFECTIVE_URL );

es:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT ,0); 
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
$html = curl_exec($ch);
$redirectURL = curl_getinfo($ch,CURLINFO_EFFECTIVE_URL );
curl_close($ch);

2
Myślę, że to jest lepsze rozwiązanie, ponieważ rozwija również wiele przekierowań.
Christian Engel

Pamiętaj: (ok, duh) dane POST nie zostaną przesłane ponownie po przekierowaniu. W moim przypadku tak się stało i potem poczułem się głupio, bo: po prostu użyj odpowiedniego adresu URL i to już naprawione.
twicejr

Używanie curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);jest luką w zabezpieczeniach. Zasadniczo mówi: „Ignoruj ​​błędy SSL, jeśli jest uszkodzony - ufaj tak samo, jak niezaszyfrowanemu adresowi URL”.
Finezja

8

Powyższa odpowiedź nie działa dla mnie na jednym z moich serwerów, coś do z basedir, więc trochę ją ponownie zhaszowałem. Poniższy kod działa na wszystkich moich serwerach.

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$a = curl_exec($ch);
curl_close( $ch ); 
// the returned headers
$headers = explode("\n",$a);
// if there is no redirection this will be the final url
$redir = $url;
// loop through the headers and check for a Location: str
$j = count($headers);
for($i = 0; $i < $j; $i++){
// if we find the Location header strip it and fill the redir var       
if(strpos($headers[$i],"Location:") !== false){
        $redir = trim(str_replace("Location:","",$headers[$i]));
        break;
    }
}
// do whatever you want with the result
echo redir;

Location: Nagłówek nie zawsze jest przestrzeganie przekierowanie. Zobacz również pytanie, które jest wyraźnie na ten temat: curl follow location error
hakre

5

Wybrana tutaj odpowiedź jest przyzwoita, ale uwzględniająca wielkość liter, nie chroni przed względnymi location:nagłówkami (co robią niektóre witryny) lub stronami, które mogą faktycznie zawierać frazę Location:w treści ... (co obecnie ma zillow).

Trochę niechlujnie, ale kilka szybkich zmian, aby uczynić to nieco mądrzejszym, to:

function getOriginalURL($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $result = curl_exec($ch);
    $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    // if it's not a redirection (3XX), move along
    if ($httpStatus < 300 || $httpStatus >= 400)
        return $url;

    // look for a location: header to find the target URL
    if(preg_match('/location: (.*)/i', $result, $r)) {
        $location = trim($r[1]);

        // if the location is a relative URL, attempt to make it absolute
        if (preg_match('/^\/(.*)/', $location)) {
            $urlParts = parse_url($url);
            if ($urlParts['scheme'])
                $baseURL = $urlParts['scheme'].'://';

            if ($urlParts['host'])
                $baseURL .= $urlParts['host'];

            if ($urlParts['port'])
                $baseURL .= ':'.$urlParts['port'];

            return $baseURL.$location;
        }

        return $location;
    }
    return $url;
}

Zauważ, że to nadal idzie tylko 1 przekierowanie. Aby wejść głębiej, musisz pobrać zawartość i postępować zgodnie z przekierowaniami.


5

Czasami musisz pobrać nagłówki HTTP, ale jednocześnie nie chcesz zwracać tych nagłówków. **

Ten szkielet zajmuje się plikami cookie i przekierowaniami HTTP przy użyciu rekursji. Głównym pomysłem jest unikanie zwracania nagłówków HTTP do kodu klienta.

Możesz na nim zbudować bardzo mocną klasę loków. Dodaj funkcję POST itp.

<?php

class curl {

  static private $cookie_file            = '';
  static private $user_agent             = '';  
  static private $max_redirects          = 10;  
  static private $followlocation_allowed = true;

  function __construct()
  {
    // set a file to store cookies
    self::$cookie_file = 'cookies.txt';

    // set some general User Agent
    self::$user_agent = 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)';

    if ( ! file_exists(self::$cookie_file) || ! is_writable(self::$cookie_file))
    {
      throw new Exception('Cookie file missing or not writable.');
    }

    // check for PHP settings that unfits
    // correct functioning of CURLOPT_FOLLOWLOCATION 
    if (ini_get('open_basedir') != '' || ini_get('safe_mode') == 'On')
    {
      self::$followlocation_allowed = false;
    }    
  }

  /**
   * Main method for GET requests
   * @param  string $url URI to get
   * @return string      request's body
   */
  static public function get($url)
  {
    $process = curl_init($url);    

    self::_set_basic_options($process);

    // this function is in charge of output request's body
    // so DO NOT include HTTP headers
    curl_setopt($process, CURLOPT_HEADER, 0);

    if (self::$followlocation_allowed)
    {
      // if PHP settings allow it use AUTOMATIC REDIRECTION
      curl_setopt($process, CURLOPT_FOLLOWLOCATION, true);
      curl_setopt($process, CURLOPT_MAXREDIRS, self::$max_redirects); 
    }
    else
    {
      curl_setopt($process, CURLOPT_FOLLOWLOCATION, false);
    }

    $return = curl_exec($process);

    if ($return === false)
    {
      throw new Exception('Curl error: ' . curl_error($process));
    }

    // test for redirection HTTP codes
    $code = curl_getinfo($process, CURLINFO_HTTP_CODE);
    if ($code == 301 || $code == 302)
    {
      curl_close($process);

      try
      {
        // go to extract new Location URI
        $location = self::_parse_redirection_header($url);
      }
      catch (Exception $e)
      {
        throw $e;
      }

      // IMPORTANT return 
      return self::get($location);
    }

    curl_close($process);

    return $return;
  }

  static function _set_basic_options($process)
  {

    curl_setopt($process, CURLOPT_USERAGENT, self::$user_agent);
    curl_setopt($process, CURLOPT_COOKIEFILE, self::$cookie_file);
    curl_setopt($process, CURLOPT_COOKIEJAR, self::$cookie_file);
    curl_setopt($process, CURLOPT_RETURNTRANSFER, 1);
    // curl_setopt($process, CURLOPT_VERBOSE, 1);
    // curl_setopt($process, CURLOPT_SSL_VERIFYHOST, false);
    // curl_setopt($process, CURLOPT_SSL_VERIFYPEER, false);
  }

  static function _parse_redirection_header($url)
  {
    $process = curl_init($url);    

    self::_set_basic_options($process);

    // NOW we need to parse HTTP headers
    curl_setopt($process, CURLOPT_HEADER, 1);

    $return = curl_exec($process);

    if ($return === false)
    {
      throw new Exception('Curl error: ' . curl_error($process));
    }

    curl_close($process);

    if ( ! preg_match('#Location: (.*)#', $return, $location))
    {
      throw new Exception('No Location found');
    }

    if (self::$max_redirects-- <= 0)
    {
      throw new Exception('Max redirections reached trying to get: ' . $url);
    }

    return trim($location[1]);
  }

}

0

Wiele wyrażeń regularnych tutaj, mimo że bardzo je lubię, ten sposób może być dla mnie bardziej stabilny:

$resultCurl=curl_exec($curl); //get curl result
//Optional line if you want to store the http status code
$headerHttpCode=curl_getinfo($curl,CURLINFO_HTTP_CODE);

//let's use dom and xpath
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($resultCurl, LIBXML_HTML_NODEFDTD);
libxml_use_internal_errors(false);
$xpath = new \DOMXPath($dom);
$head=$xpath->query("/html/body/p/a/@href");

$newUrl=$head[0]->nodeValue;

Część lokalizacji to łącze w kodzie HTML wysłane przez apache. Dlatego Xpath jest idealny do jego odzyskania.


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.