То, что я хотел бы сделать, это узнать, что является последним / окончательным URL после перенаправления .
Я бы предпочел не использовать cURL. Я хотел бы придерживаться чистого PHP (обтекатели потоков).
Прямо сейчас у меня есть URL (скажем, http: //domain.test ), и я использую get_headers () для получения определенных заголовков с этой страницы. get_headers также возвратит несколько заголовков Location:
(см. раздел «Редактирование» ниже). Есть ли способ использовать эти заголовки для создания конечного URL-адреса? или есть функция PHP, которая автоматически сделает это?
Изменить: get_headers () следует перенаправлениям и возвращает все заголовки для каждого ответа / перенаправления, поэтому у меня есть все заголовки Location:
/** * get_redirect_url() * Gets the address that the provided URL redirects to, * or FALSE if there's no redirect. * * @param string $url * @return string */ function get_redirect_url($url){ $redirect_url = null; $url_parts = @parse_url($url); if (!$url_parts) return false; if (!isset($url_parts['host'])) return false; //can't process relative URLs if (!isset($url_parts['path'])) $url_parts['path'] = '/'; $sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30); if (!$sock) return false; $request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1\r\n"; $request .= 'Host: ' . $url_parts['host'] . "\r\n"; $request .= "Connection: Close\r\n\r\n"; fwrite($sock, $request); $response = ''; while(!feof($sock)) $response .= fread($sock, 8192); fclose($sock); if (preg_match('/^Location: (.+?)$/m', $response, $matches)){ if ( substr($matches[1], 0, 1) == "/" ) return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]); else return trim($matches[1]); } else { return false; } } /** * get_all_redirects() * Follows and collects all redirects, in order, for the given URL. * * @param string $url * @return array */ function get_all_redirects($url){ $redirects = array(); while ($newurl = get_redirect_url($url)){ if (in_array($newurl, $redirects)){ break; } $redirects[] = $newurl; $url = $newurl; } return $redirects; } /** * get_final_url() * Gets the address that the URL ultimately leads to. * Returns $url itself if it isn't a redirect. * * @param string $url * @return string */ function get_final_url($url){ $redirects = get_all_redirects($url); if (count($redirects)>0){ return array_pop($redirects); } else { return $url; } }
И, как всегда, отдавайте должное:
http://w-shadow.com/blog/2008/07/05/how-to-get-redirect-url-in-php/
function getRedirectUrl ($url) { stream_context_set_default(array( 'http' => array( 'method' => 'HEAD' ) )); $headers = get_headers($url, 1); if ($headers !== false && isset($headers['Location'])) { return $headers['Location']; } return false; }
Дополнительно…
Как уже упоминалось в комментарии, конечный элемент в $headers['Location']
станет вашим окончательным URL после всех перенаправлений. Однако важно отметить, что он не всегда будет массивом. Иногда это просто переменная, отличная от массива. В этом случае попытка доступа к последнему элементу массива скорее всего вернет один символ. Не идеально.
Если вас интересует только конечный URL-адрес, после всех переадресаций я предлагаю изменить
return $headers['Location'];
в
return is_array($headers['Location']) ? array_pop($headers['Location']) : $headers['Location'];
… это просто короткая рука для
if(is_array($headers['Location'])){ return array_pop($headers['Location']); }else{ return $headers['Location']; }
Это исправление позаботится о любом случае (массив, не-массив) и устранит необходимость отсеивания конечного URL после вызова функции.
В случае отсутствия переадресаций функция возвращает false
. Аналогично, функция также вернет false
для недопустимых URL-адресов (по какой-либо причине недействительна). Поэтому важно проверить URL-адрес на достоверность перед запуском этой функции или включить проверку перенаправления где-нибудь в вашу проверку.
xaav ответ очень хорош; за исключением следующих двух вопросов:
Некоторые сайты не будут работать, поскольку они не будут распознавать базовый пользовательский агент (клиентский браузер) => Это просто исправлено добавлением поля заголовка User-agent: я добавил пользовательский агент Android (вы можете найти здесь http: // www. useragentstring.com/pages/useragentstring.php другие примеры агента пользователя в соответствии с вашими потребностями):
$ request. = «User-Agent: Mozilla / 5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build / IML74K) AppleWebkit / 534.30 (KHTML, например, Gecko) Версия / 4.0 Mobile Safari / 534.30 \ г \ п ";
Вот измененный ответ:
/** * get_redirect_url() * Gets the address that the provided URL redirects to, * or FALSE if there's no redirect. * * @param string $url * @return string */ function get_redirect_url($url){ $redirect_url = null; $url_parts = @parse_url($url); if (!$url_parts) return false; if (!isset($url_parts['host'])) return false; //can't process relative URLs if (!isset($url_parts['path'])) $url_parts['path'] = '/'; $sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30); if (!$sock) return false; $request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1\r\n"; $request .= 'Host: ' . $url_parts['host'] . "\r\n"; $request .= "User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\r\n"; $request .= "Connection: Close\r\n\r\n"; fwrite($sock, $request); $response = ''; while(!feof($sock)) $response .= fread($sock, 8192); fclose($sock); if (preg_match('/^Location: (.+?)$/m', $response, $matches)){ if ( substr($matches[1], 0, 1) == "/" ) return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]); else return trim($matches[1]); } else { return false; } } /** * get_all_redirects() * Follows and collects all redirects, in order, for the given URL. * * @param string $url * @return array */ function get_all_redirects($url){ $redirects = array(); while ($newurl = get_redirect_url($url)){ if (in_array($newurl, $redirects)){ break; } $redirects[] = $newurl; $url = $newurl; } return $redirects; } /** * get_final_url() * Gets the address that the URL ultimately leads to. * Returns $url itself if it isn't a redirect. * * @param string $url * @return string */ function get_final_url($url){ $redirects = get_all_redirects($url); if (count($redirects)>0){ return array_pop($redirects); } else { return $url; }
В то время как OP хотел избежать cURL
, лучше использовать его, когда он доступен. Вот решение, которое имеет следующие преимущества:
location
(оба ответа xaav и webjay не справляются с этим) Вот функция:
function findUltimateDestination($url, $maxRequests = 10) { $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRequests); curl_setopt($ch, CURLOPT_TIMEOUT, 15); //customize user agent if you desire... curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Link Checker)'); curl_setopt($ch, CURLOPT_URL, $url); curl_exec($ch); $url=curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); curl_close ($ch); return $url; }
Вот более подробная версия, которая позволяет вам проверять цепочку перенаправления, а не следить за ней.
function findUltimateDestination($url, $maxRequests = 10) { $ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 15); //customize user agent if you desire... curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Link Checker)'); while ($maxRequests--) { //fetch curl_setopt($ch, CURLOPT_URL, $url); $response = curl_exec($ch); //try to determine redirection url $location = ''; if (in_array(curl_getinfo($ch, CURLINFO_HTTP_CODE), [301, 302, 303, 307, 308])) { if (preg_match('/Location:(.*)/i', $response, $match)) { $location = trim($match[1]); } } if (empty($location)) { //we've reached the end of the chain... return $url; } //build next url if ($location[0] == '/') { $u = parse_url($url); $url = $u['scheme'] . '://' . $u['host']; if (isset($u['port'])) { $url .= ':' . $u['port']; } $url .= $location; } else { $url = $location; } } return null; }
В качестве примера цепочки перенаправления, которую обрабатывает эта функция, а другие нет, попробуйте следующее:
echo findUltimateDestination('http://dx.doi.org/10.1016/j.infsof.2016.05.005')
На момент написания это включает 4 запроса, в которых участвуют заголовки location
и location
.