Bagaimana saya bisa menemukan tempat saya akan diarahkan menggunakan CURL?

149

Saya mencoba membuat ikal mengikuti pengalihan tetapi saya tidak bisa membuatnya berfungsi dengan benar. Saya memiliki string yang ingin saya kirim sebagai param GET ke server dan dapatkan URL yang dihasilkan.

Contoh:

String = Kobold Vermin
Url = www.wowhead.com/search?q=Kobold+Worker

Jika Anda membuka url itu, ia akan mengarahkan Anda ke "www.wowhead.com/npc=257". Saya ingin curl mengembalikan URL ini ke kode PHP saya sehingga saya dapat mengekstrak "npc = 257" dan menggunakannya.

Kode saat ini:

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

Namun ini mengembalikan www.wowhead.com/search?q=Kobold+Worker dan bukan www.wowhead.com/npc=257 .

Saya menduga PHP akan kembali sebelum pengalihan eksternal terjadi. Bagaimana saya bisa memperbaikinya?

Thomas Van Nuffel
sumber
8
Ini adalah salah satu pertanyaan teratas untuk "curl follow redirect". Untuk secara otomatis mengikuti arahan ulang menggunakan curlperintah, lewati tanda -Latau --location. Misalnyacurl -L http://example.com/
Rob W

Jawaban:

256

Untuk membuat cURL mengikuti arahan ulang, gunakan:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

Erm ... Saya tidak berpikir Anda benar-benar mengeksekusi curl ... Coba:

curl_exec($ch);

... setelah mengatur opsi, dan sebelum curl_getinfo()panggilan.

EDIT: Jika Anda hanya ingin mencari tahu ke mana halaman dialihkan ke, saya akan menggunakan saran di sini , dan hanya menggunakan Curl untuk mengambil header dan mengekstrak Lokasi: header dari mereka:

$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]);
}
Matt Gibson
sumber
2
Ini membuat php mengikuti arahan ulang. Saya tidak ingin mengikuti arahan ulang, saya hanya ingin tahu url dari halaman arahan ulang.
Thomas Van Nuffel
9
Oh, jadi Anda sebenarnya tidak ingin mengambil halaman itu? Cari tahu lokasinya? Dalam hal ini, saya akan menyarankan taktik yang digunakan di sini: zzz.rezo.net/HowTo-Expand-Short-URLs.html - pada dasarnya hanya ambil header dari halaman yang dialihkan, dan ambil Location: header dari itu. Bagaimanapun, Anda masih perlu melakukan exec () agar Curl benar - benar melakukan apa saja ...
Matt Gibson
1
Saya sarankan untuk melihat solusi Luca Camillos di bawah ini, karena solusi ini tidak mempertimbangkan banyak pengalihan.
Christian Engel
solusi ini membuka halaman web baru di dalam url yang sama. Saya ingin mengubah url juga bersama dengan memposting parameter ke url itu. Bagaimana saya bisa mencapainya?
amanpurohit
@MattGibson ketika saya menggunakan $ httpCode = curl_getinfo ($ handle, CURLINFO_HTTP_CODE); dengan CURLOPT_FOLLOWLOCATION disetel ke true apa yang akan menjadi httpcode. Maksud saya apakah itu untuk url pertama atau untuk redirect url
Manigandan Arjunan
26

Tambahkan baris ini ke curl inisialisasi

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

dan gunakan getinfo sebelum 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);
Luca Camillo
sumber
2
Saya pikir ini adalah solusi yang lebih baik, karena itu juga membuka beberapa pengalihan.
Christian Engel
Ingat: (ok, duh) Data POST tidak akan dikirimkan kembali setelah redirect. Dalam kasus saya ini terjadi dan saya merasa bodoh setelah itu karena: cukup gunakan url yang sesuai dan sudah diperbaiki.
dua kali
Penggunaan curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);adalah kerentanan keamanan. Itu pada dasarnya mengatakan "Abaikan kesalahan SSL jika rusak - percayai sama seperti Anda akan URL tidak terenkripsi.".
Finesse
8

Jawaban di atas tidak bekerja untuk saya di salah satu server saya, sesuatu dengan to basedir, jadi saya kembali hash sedikit. Kode di bawah ini berfungsi di semua server saya.

$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;
GR1NN3R
sumber
The Location: header tidak selalu mengikuti redirect. Silakan juga lihat pertanyaan yang secara eksplisit tentang ini: curl follow location error
hakre
5

Jawaban yang dipilih di sini layak tetapi sensitif hurufnya, tidak melindungi terhadap location:tajuk relatif (yang dilakukan beberapa situs) atau laman yang mungkin memiliki frasaLocation: di kontennya ... (yang saat ini memang dimiliki zillow).

Agak ceroboh, tetapi beberapa suntingan cepat untuk membuat ini sedikit lebih pintar adalah:

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

Perhatikan bahwa ini masih berlangsung hanya 1 redirection. Untuk lebih dalam, Anda benar-benar perlu mendapatkan konten dan mengikuti arahan ulang.

broox
sumber
5

Terkadang Anda perlu mendapatkan tajuk HTTP tetapi pada saat yang sama Anda tidak ingin mengembalikan tajuk itu. **

Kerangka ini menangani cookie dan pengalihan HTTP menggunakan rekursi. Gagasan utama di sini adalah untuk menghindari mengembalikan tajuk HTTP ke kode klien.

Anda dapat membangun kelas keriting yang sangat kuat di atasnya. Tambahkan fungsionalitas POST, dll.

<?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]);
  }

}
Igor Parra
sumber
0

Lot of of regex di sini, meskipun sebenarnya saya sangat menyukai mereka dengan cara ini mungkin lebih stabil bagi saya:

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

Bagian lokasi adalah tautan dalam HTML yang dikirim oleh apache. Jadi Xpath sempurna untuk memulihkannya.

Patrick Valibus
sumber
-1

Kamu bisa memakai:

$redirectURL = curl_getinfo($ch,CURLINFO_REDIRECT_URL);
Abhilash Nayak
sumber