Bu kılavuz, çalışan bir Guzzle proxy yapılandırmasının her aşamasını adım adım açıklamaktadır: proxy istek seçeneğinin dize ve dizi biçimleri, kimlik doğrulamalı proxy URL’leri, CONNECT tüneli üzerinden HTTPS hedefleri, zaman aşımları ve sertifika doğrulaması ile proxy reddedildiğinde Guzzle’ın attığı istisnalar. Kılavuzu tamamladığınızda, bir ev tipi proxy üzerinden yönlendirme yapan, oturum boyunca tek bir çıkış IP'si kullanan, bu IP'yi doğrulayan ve istek başarısız olduğunda yeni bir oturumla yeniden deneme yapan, yeniden kullanılabilir bir PHP istemcisine sahip olacaksınız.
Tüm örnekler Guzzle 7 ve PHP 8.1 veya sonraki sürümleri için hazırlanmıştır. composer require guzzlehttp/guzzle:^7 ile kurun.
proxy seçeneği: dize veya dizi
Guzzle, docs.guzzlephp.org adresinde belgelenen proxy istek seçeneği aracılığıyla bir proxy yapılandırır. Bunu istek başına veya istemci oluşturucusunda varsayılan olarak aktarabilirsiniz. En basit biçim, her protokole uygulanan bir dizedir.
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'proxy' => 'http://USERNAME:PASSWORD@premium-residential.geonode.com:9000',
'timeout' => 30,
]);
$response = $client->get('https://api.ipify.org?format=json');
echo $response->getBody(), PHP_EOL;
Dizi biçimi, URI şeması başına farklı bir proxy seçmenize ve ana bilgisayarları hariç tutmanıza olanak tanır. Üç anahtar tanınır: http, https ve no.
<?php
$proxyUrl = 'http://USERNAME:PASSWORD@premium-residential.geonode.com:9000';
$client = new GuzzleHttp\Client([
'proxy' => [
'http' => $proxyUrl, // used for http:// targets
'https' => $proxyUrl, // used for https:// targets (CONNECT tunnel)
'no' => ['localhost', '.internal.example'], // never proxied
],
]);
Her şeyin tek bir proxy üzerinden geçmesi gerektiğinde dize biçimini kullanın. no listesine ihtiyacınız olduğunda veya düz HTTP isteklerinin bir veri merkezi havuzundan, HTTPS isteklerinin ise ev ağı üzerinden geçmesini istediğinizde dizi biçimini kullanın.
no listesi ve NO_PROXY uyarısı
proxy seçeneğini ayarlamadığınızda Guzzle, ortam değişkenlerinden HTTP_PROXY, HTTPS_PROXY ve NO_PROXY değerlerini okur. Bir seçenek ayarladığınız anda, hariç tutma listesi de dahil olmak üzere bu otomatik işlem durur. Resmi belgelerde, no değerini kendiniz aktarmanız gerektiği açıkça belirtilmiştir. Konteyneriniz iç hizmetler için NO_PROXY ayarlıyorsa, bunu unutmanız durumunda bu çağrılar aniden ev ağı proxy’si üzerinden yönlendirilir.
$no = getenv('NO_PROXY') ? explode(',', getenv('NO_PROXY')) : [];
$client = new GuzzleHttp\Client([
'proxy' => [
'http' => $proxyUrl,
'https' => $proxyUrl,
'no' => $no,
],
]);
Guzzle proxy kimlik doğrulaması: URL’de kimlik bilgileri
Guzzle’da ayrı bir proxy_auth seçeneği yoktur. Kimlik bilgileri, proxy URL'sine userinfo (http://username:password@host:port) olarak eklenir ve cURL işleyicisi bunları Proxy-Authorization: Basic olarak gönderir. Bundan iki pratik kural çıkar.
İlk olarak, şifrede URL'ye uygun olmayan her şeyi yüzde kodlayın. Ham şifrede yer alan bir @, : veya /, URL ayrıştırmasını bozar ve kafa karıştırıcı bir 407 hatası ya da yanlış ana bilgisayara bağlantı ile sonuçlanır. URL'yi dize birleştirme yerine rawurlencode() kullanarak oluşturun.
function proxyUrl(string $user, string $pass, int $port = 9000): string
{
return sprintf(
'http://%s:%s@premium-residential.geonode.com:%d',
rawurlencode($user),
rawurlencode($pass),
$port
);
}
İkincisi, kimlik bilgilerini kodun dışında tutun. Bunları ortam değişkenlerinden veya bir gizli bilgi yöneticisinden okuyun ve asla tam proxy URL'sini günlüğe kaydetmeyin; Guzzle'ın debug çıktısı Proxy-Authorization başlığını içerir, bu nedenle günlükleri herhangi bir yere göndermeden önce bu bilgiyi gizleyin.
HTTP CONNECT proxy'si aracılığıyla HTTPS hedefleri
Sıkça karışıklığa yol açan bir nokta: Proxy URL şeması, proxy ile nasıl iletişim kuracağınızı açıklar; hedefin ne kullandığını değil. Bir https:// hedefi için Guzzle (cURL aracılığıyla) proxy'ye düz bir HTTP bağlantısı açar, CONNECT host:443'yu gönderir, ardından bu tünel içinde hedefle TLS el sıkışmasını gerçekleştirir. Bu nedenle, dizi içindeki hem http hem de https anahtarları normalde bir http:// proxy URL'sini gösterir. https://premium-residential.geonode.com:9000 yazmak, cURL'a proxy atlamasını TLS ile sarmalamasını söyler; bu ise uç noktanın beklemediği bir durumdur.
TLS, tünel içinde uçtan uca çalıştığı için proxy, yanıt gövdesini asla görmez veya değiştirmez ve normal sertifika doğrulama işlemi, proxy olmasaydı olduğu gibi hedef siteye tam olarak uygulanır.
verify ve sertifika sorunları
verify seçeneği, hedef için sertifika kontrollerini yönetir. Sistem CA paketini kullanan varsayılan true ayarında bırakın. PHP bir paket bulamazsa (minimal Docker görüntülerinde yaygındır), verify'yu devre dışı bırakmak yerine bir PEM dosyasına yönlendirin.
$client = new GuzzleHttp\Client([
'proxy' => $proxyUrl,
'verify' => '/etc/ssl/certs/ca-certificates.crt', // or true for the system default
]);
Yalnızca bir proxy eklendikten sonra ortaya çıkan bir hatayı bastırmak için verify => false'yu ayarlamak bir hatadır; doğru şekilde yapılandırılmış bir CONNECT tüneli sertifika hatalarına neden olmaz, bu nedenle asıl sorun başka bir yerdedir, genellikle yukarıdaki https:// şeması karışıklığıdır.
Zaman Aşımları: timeout ve connect_timeout
Guzzle’ın varsayılan timeout değeri 0’dur; bu, “sonsuza kadar bekle” anlamına gelir. Yavaş bir çıkış cihazı bir işçiyi süresiz olarak durdurabileceğinden, bu varsayılan ayar bir konut proxy'si üzerinden kullanıldığında sorun yaratır. İki değer belirleyin: connect_timeout, proxy'ye olan TCP bağlantısını sınırlar ve yalnızca cURL işleyicisi tarafından desteklenir; timeout ise tüm aktarımı saniye cinsinden sınırlar.
$client = new GuzzleHttp\Client([
'proxy' => $proxyUrl,
'connect_timeout' => 10, // seconds to reach the proxy
'timeout' => 45, // total seconds for the request
]);
Ev tipi çıkışlar, veri merkezi çıkışlarına göre daha yavaştır; bu nedenle, toplam zaman aşımı süresini doğrudan bir istek için ayarlayacağınız süreden daha uzun tutun. Sürekli tetiklenen bir bağlantı zaman aşımı, hedeften ziyade ağ yolu sorununa işaret eder.
Guzzle proxy'sinden gelen istisnaların işlenmesi
Guzzle, hepsi GuzzleHttp\Exception\RequestException sınıfından türetilmiş türlü istisnalar atar; hangi istisnanın oluştuğunu bilmek, hatanın nerede meydana geldiğini gösterir.
| İstisna | Ne zaman atılır | Tipik proxy nedeni |
|---|
ConnectException | Hiç HTTP yanıtı yok | Proxy sunucusuna ulaşılamıyor, yanlış bağlantı noktası, bağlantı zaman aşımı, DNS hatası |
ClientException | 4xx yanıtı | 407: yanlış kimlik bilgileri; hedeften gelen 403/429 |
ServerException | 5xx yanıtı | Hedef hatası veya proxy'nin hedefe ulaşamaması |
RequestException | Diğer tüm durumlar | Başlıkların işlenmesinden sonra toplam zaman aşımı, TLS hatası |
407 hatası ayrı olarak ele alınmalıdır. İsteği reddeden hedef değil, proxy'dir; bu nedenle kimlik bilgileri değiştirilene kadar yeniden denemenin bir faydası olmayacaktır.
<?php
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Exception\RequestException;
try {
$response = $client->get('https://example.com/');
} catch (ConnectException $e) {
// Could not reach the proxy or the connect_timeout expired.
error_log('connect: ' . $e->getMessage());
} catch (ClientException $e) {
$status = $e->getResponse()->getStatusCode();
if ($status === 407) {
// Proxy rejected credentials: check username grammar and password.
throw new RuntimeException('proxy auth failed', 407, $e);
}
// 403, 404, 429 etc. come from the target site.
} catch (ServerException $e) {
// 5xx from the target (or the proxy failing to reach it).
} catch (RequestException $e) {
// Timeouts after connect, TLS errors and other transfer failures.
}
http_errors ile istisnaları devre dışı bırakma
İstisnaları yakalamaktansa durum koduna göre dallanmayı tercih ediyorsanız, http_errors => false'yu ayarlayın. Böylece Guzzle, 407 dahil her durum kodu için yanıtı döndürür ve getStatusCode()'yu kendiniz kontrol edersiniz. Geri döndürülecek bir yanıt olmadığı için ConnectException istisnası yine de atılır.
$response = $client->get('https://example.com/', ['http_errors' => false]);
if ($response->getStatusCode() === 407) {
// handle auth failure without an exception
}
Tanılama: on_stats ve debug
Bir Guzzle proxy isteği yavaşladığında veya aralıklı olarak başarısız olduğunda, kabloda neler olduğunu öğrenmenizi sağlayan iki seçenek vardır. on_stats, her aktarımdan sonra toplam süreyi ve ad araması, bağlantı ve TLS zamanlamaları gibi cURL işleyici istatistiklerini içeren bir GuzzleHttp\TransferStats nesnesi alır.
use GuzzleHttp\TransferStats;
$client->get('https://example.com/', [
'on_stats' => function (TransferStats $stats) {
printf("%s took %.3fs\n", $stats->getEffectiveUri(), $stats->getTransferTime());
if (!$stats->hasResponse()) {
var_dump($stats->getHandlerErrorData());
}
},
]);
debug => true, CURLOPT_VERBOSE'yu etkinleştirir ve proxy'nin yanıt satırı da dahil olmak üzere CONNECT alışverişini yazdırır. Tünelin kurulduğunu doğrulamak için bunu bir kez kullanın, ardından kaldırın: kimlik bilgilerini STDOUT'a yazar. true yerine bir fopen() akışı iletmek, çıktıyı bir dosyaya yönlendirir.
10000 numaralı bağlantı noktasında sabit oturumlar
9000 numaralı bağlantı noktası, her istekte çıkış IP adresini değiştirir. Çok adımlı akışlarda (giriş yapma, ardından sayfaları getirme, sonra sayfalandırma) aynı IP adresine ihtiyacınız vardır; 10000 numaralı bağlantı noktası da bunu sağlar. Oturum, başlık değil kullanıcı adı aracılığıyla seçilir: -session-XXXXXXXX'ya 8 karakterlik alfasayısal bir kimlik ekleyin ve isteğe bağlı olarak 180 ile 86400 arasında bir değerle -lifetime-SECONDS'yu ekleyin. -country-us gibi hedefleme sonekleri aynı dizeye eklenir.
<?php
function stickyProxyUrl(string $baseUser, string $pass, string $country = 'us', int $lifetime = 600): string
{
$session = substr(bin2hex(random_bytes(4)), 0, 8); // 8 alphanumeric chars
$user = sprintf('%s-country-%s-session-%s-lifetime-%d', $baseUser, $country, $session, $lifetime);
return sprintf(
'http://%s:%s@premium-residential.geonode.com:10000',
rawurlencode($user),
rawurlencode($pass)
);
}
$client = new GuzzleHttp\Client([
'proxy' => stickyProxyUrl(getenv('GEONODE_USER'), getenv('GEONODE_PASS')),
'timeout' => 45,
]);
$client'dan gelen her istek artık on dakika boyunca tek bir çıkış IP'sini paylaşır. Farklı bir IP istediğinizde yeni bir istemci oluşturun (veya yeni bir proxy seçeneği belirtin).
Çıkış IP'sini Doğrulama
Bir oturuma güvenmeden önce, hedefin ne göreceğini doğrulayın. Aynı istemci üzerinden bir IP yankı hizmetine iki kez erişin: yapışkan oturumda her iki çağrı da aynı adresi döndürürken, 9000 numaralı bağlantı noktasında farklı adresler döndürür.
$first = json_decode((string) $client->get('https://api.ipify.org?format=json')->getBody(), true);
$second = json_decode((string) $client->get('https://api.ipify.org?format=json')->getBody(), true);
printf("exit IP: %s (stable: %s)\n", $first['ip'], $first['ip'] === $second['ip'] ? 'yes' : 'no');
Oturumu değiştiren küçük bir yeniden deneme yardımcısı
Konut çıkışları oturumun ortasında kaybolabilir ve hedefler bazen belirli bir IP adresini engelleyebilir. Pratik bir yaklaşım, başarısız olan bir isteği yeni bir oturum kimliği ile yeniden denemektir; bu, hem çıkış IP'sini değiştirir hem de zaten kötü durumda olan bir bağlantının yeniden kullanılmasını önler. Aşağıdaki yardımcı program, her deneme için istemciyi yeniden oluşturur ve yalnızca bağlantı hataları, zaman aşımları ve engellemeyi gösteren durum kodları durumunda yeniden deneme yapar.
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
function fetchWithNewSession(string $url, int $attempts = 3): ResponseInterface
{
$lastError = null;
for ($i = 1; $i <= $attempts; $i++) {
$client = new Client([
'proxy' => stickyProxyUrl(getenv('GEONODE_USER'), getenv('GEONODE_PASS')),
'connect_timeout' => 10,
'timeout' => 45,
'http_errors' => false,
]);
try {
$response = $client->get($url);
$status = $response->getStatusCode();
if ($status === 407) {
throw new RuntimeException('proxy credentials rejected'); // do not retry
}
if (!in_array($status, [403, 429, 502, 503, 504], true)) {
return $response;
}
$lastError = new RuntimeException("status $status");
} catch (ConnectException | RequestException $e) {
$lastError = $e;
}
usleep(500_000 * $i); // simple linear backoff
}
throw new RuntimeException("gave up after $attempts attempts", 0, $lastError);
}
Oturum sonekini kaldırırsanız aynı kod 9000 numaralı bağlantı noktasında da çalışır; zira her denemede zaten yeni bir IP alınır. Deneme sayısını düşük tutun: Arka arkaya üç kez 403 yanıtı veren bir hedef, proxy ile ilgili değil, istek parmak izi hakkında bir şey söylemektedir.
Sık yapılan hatalar
Proxy URL’sinde https:// şemasının kullanılması
Proxy geçişi düz HTTP’dir; hedefin HTTPS’si CONNECT tüneli içinde çalışır. Her iki dizi anahtarı da bir http:// proxy URL’si içermelidir. Genellikle görülen belirti, proxy sunucusuyla bir SSL el sıkışmasından bahseden bir ConnectException'dur.
proxy ayarlandıktan sonra no listesinin unutulması
proxy'yu geçtikten sonra, NO_PROXY göz ardı edilir. HTTP üzerinden erişilen dahili durum kontrolleri, meta veri uç noktaları ve veritabanları, konut proxy'sinden geçecek ve başarısız olacak ya da daha kötüsü, bilgi sızıntısına neden olacaktır. no'yu her zaman açıkça geçirin.
timeout'yu sıfırda bırakma
Guzzle varsayılan olarak sonsuza kadar bekler. Tek bir tıkanmış konut bağlantısı, bir denetleyici onu sonlandırana kadar bir kuyruk işçisini bekletmeye devam edebilir. Proxy'ye bağlanan her istemcide hem connect_timeout hem de timeout değerlerini ayarlayın.
407 Hatası için Yeniden Deneme
407 hatası, proxy'nin kimlik bilgilerini reddettiği anlamına gelir. Aynı kullanıcı adı ve şifreyle yeniden denemek sadece zaman kaybına neden olur. Hemen başarısız olarak kabul edin, kullanıcı adını (asla şifreyi değil) günlüğe kaydedin ve dilbilgisi soneklerini kontrol edin.
Sonraki adım
Yukarıdaki yapılandırma, Geonode ev tipi proxy’lerle herhangi bir değişiklik yapılmaksızın çalışır: 195’ten fazla ülkede 2,5 milyon çevrimiçi IP, HTTP, HTTPS ve aynı sunucudaki SOCKS5, ayrıca tamamen kullanıcı adı üzerinden seçilebilen 24 saate kadar kalıcı oturumlar. Ülke, eyalet, şehir ve ASN hedeflemesi burada gösterilen aynı dilbilgisini kullanır; bu nedenle çıkış konumunu daraltırken herhangi bir kod değişikliği gerekmez. Planlar, sözleşmesiz pay-as-you-go'dur; aylık 7,92 $ karşılığında 10 GB'dan başlar ve 3 günlük deneme sürümü 5 $'dır. Tüm ayrıntılar için ev tipi proxy'lere bakın veya fiyatlandırma sayfasında paketleri karşılaştırın. Proxy kurulumunun ötesindeki veri toplama modelleri için, web veri toplama kullanım örneği iş akışının geri kalanını kapsamaktadır.
SSS
Bir istemciden gelen her istek için Guzzle proxy'sini nasıl ayarlayabilirim?
İstek bazında değil, istemci oluşturucusunda proxy seçeneğini geçirin. new Client(['proxy' => 'http://user:pass@host:port']), istemcinin gönderdiği her isteğe proxy'yi uygular ve istek düzeyindeki herhangi bir proxy seçeneği bunu geçersiz kılar. Bu, bir işçiden gelen tüm trafiğin aynı sağlayıcıdan geçmesi gerektiğinde kullanılan normal kalıptır.
Guzzle proxy'm neden http:// URL'lerinde çalışıyor da https:// hedeflerinde çalışmıyor?
Genellikle dizi biçimindeki https anahtarı eksiktir veya bir https:// şemasına işaret eder. Guzzle, HTTPS hedeflerini CONNECT ile bir HTTP proxy üzerinden tüneller; bu nedenle https anahtarı için proxy URL'si yine de http:// ile başlamalıdır. Dizi biçimini kontrol edin ve proxy bağlantı noktasının CONNECT'i kabul ettiğini doğrulayın; Geonode bağlantı noktaları bunu kabul eder.
Guzzle'da 407 Proxy Kimlik Doğrulaması Gerekiyor hatası ne anlama gelir?
Proxy, herhangi bir veri iletmeden önce kimlik bilgilerinizi reddetti. Guzzle, bunun bir 4xx yanıtı olması nedeniyle durumu 407 olan bir ClientException hatası olarak bildirir. Kullanıcı adı biçimini, şifreyi ve şifredeki özel karakterlerin URL'de yüzde kodlamasına tabi tutulduğunu kontrol edin. http_errors değeri false olarak ayarlandığında, istisna yerine 407 yanıt nesnesi alırsınız.
Guzzle, HTTP_PROXY ve NO_PROXY ortam değişkenlerini dikkate alır mı?
Evet, bir proxy seçeneği belirtmediğinizde Guzzle, ortamdan HTTP_PROXY, HTTPS_PROXY ve NO_PROXY değerlerini okur. Proxy seçeneğini kendiniz belirlediğiniz anda otomatik NO_PROXY işleme durur; bu nedenle no anahtarını (genellikle explode(',', getenv('NO_PROXY'))) açıkça belirtmeniz gerekir.
Birkaç Guzzle isteğinde aynı çıkış IP'sini koruyabilir miyim?
Evet, sabit oturum kullanın. Guzzle proxy'sini 10000 numaralı bağlantı noktasına yönlendirin ve kullanıcı adına -session-XXXXXXXX (8 alfasayısal karakter) ile isteğe bağlı bir -lifetime-SECONDS soneki ekleyin. Bu kullanıcı adını yeniden kullanan her istek, 180 saniye ile 24 saat arasında belirlediğiniz süre boyunca tek bir çıkış IP'sini paylaşır.
Guzzle proxy'si aracılığıyla veri toplarken dikkate almam gereken sınırlamalar var mı?
Ev tipi bant genişliği gigabayt başına ölçülür, bu nedenle büyük HTML sayfaları ve görseller bant genişliği tüketimini artırır. Kalıcı oturumlar en fazla 24 saat sürer ve yukarı akış cihazının bağlantısı kesilirse daha erken sona erebilir; bu nedenle kod, çıkış IP adresindeki değişikliklere uyum sağlamalıdır. Ayrıca hedef sitenin kullanım koşullarına, robot kurallarına ve hız sınırlamalarına da uyun; bir proxy, yasal yükümlülüklerinizi değiştirmez.