Tekniker för att mastera cURL

cURL är ett verktyg för överföring av filer och data med URL-syntax, som stöder många protokoll, inklusive HTTP, FTP, TELNET och mer. Ursprungligen utformades cURL för att vara ett kommandoradsverktyg. Lyckligt för oss, cURL-biblioteket stöds också av PHP. I den här artikeln kommer vi att titta på några av de avancerade funktionerna i cURL, och hur vi kan använda dem i våra PHP-skript.

Varför cURL?

Det är sant att det finns andra sätt att hämta innehållet på en webbsida. Många gånger, mest på grund av latskap, har jag just använt enkla PHP-funktioner i stället för cURL:

 $ content = file_get_contents ("http://www.nettuts.com"); // eller $ lines = file ("http://www.nettuts.com"); // eller readfile ("http://www.nettuts.com");

Men de har praktiskt taget ingen flexibilitet och saknar tillräcklig felhantering. Det finns också vissa uppgifter som du helt enkelt inte kan göra, som att hantera cookies, autentisering, formulär inlägg, filuppladdningar mm.

cURL är ett kraftfullt bibliotek som stöder många olika protokoll, alternativ och ger detaljerad information om URL-förfrågningarna.

Grundläggande struktur

Innan vi går vidare till mer komplicerade exempel, låt oss granska den grundläggande strukturen för en cURL-förfrågan i PHP. Det finns fyra huvudsteg:

  1. initiera
  2. Ange alternativ
  3. Kör och hämta resultat
  4. Ladda upp cURL-handtaget
 // 1. initiera $ ch = curl_init (); // 2. Ange alternativen, inklusive url curl_setopt ($ ch, CURLOPT_URL, "http://www.nettuts.com"); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ ch, CURLOPT_HEADER, 0); // 3. exekvera och hämta den resulterande HTML-utgången $ output = curl_exec ($ ch); // 4. frigör krullhandtaget curl_close ($ ch);

Steg # 2 (det vill säga curl_setopt () samtal) kommer att bli en stor del av den här artikeln, för det är där all magi händer. Det finns en lång lista över cURL-alternativ som kan ställas in, vilket kan konfigurera URL-förfrågan i detalj. Det kan vara svårt att gå igenom hela listan och smälta allt på en gång. Så idag kommer vi bara att använda några av de mer vanliga och användbara alternativen i olika kodexempel.

Kollar efter fel

Eventuellt kan du också lägga till felkontroll:

 // ... $ output = curl_exec ($ ch); om ($ output === FALSE) echo "cURL Error:". curl_error ($ ch);  // ... 

Observera att vi måste använda "=== FALSE" för jämförelse istället för "== FALSE". Eftersom vi måste skilja mellan tomgång vs det booleska värdet FALSE, vilket indikerar ett fel.

Skaffa information

Ett annat valfritt steg är att få information om cURL-förfrågan, efter att den har genomförts.

 // ... curl_exec ($ ch); $ info = curl_getinfo ($ ch); ekot "tog". $ info ['total_time']. 'sekunder för url'. $ Info [ 'url']; // ... 

Följande information ingår i den returnerade matrisen:

  • "URL"
  • "innehållstyp"
  • "HTTP_CODE"
  • "Header_size"
  • "Request_size"
  • "FILETIME"
  • "Ssl_verify_result"
  • "Redirect_count"
  • "total tid"
  • "Namelookup_time"
  • "Connect_time"
  • "Pretransfer_time"
  • "Size_upload"
  • "Size_download"
  • "Speed_download"
  • "Speed_upload"
  • "Download_content_length"
  • "Upload_content_length"
  • "Starttransfer_time"
  • "Redirect_time"

Upptäck omdirigering baserat på webbläsare

I det här första exemplet skriver vi ett skript som kan detektera URL-omdirigeringar baserat på olika webbläsarinställningar. Till exempel, vissa webbplatser omdirigerar mobila webbläsare, eller till och med surfare från olika länder.

Vi ska använda alternativet CURLOPT_HTTPHEADER för att ställa in våra utgående HTTP-huvuden, inklusive användaragentsträngen och de accepterade språken. Slutligen kommer vi att kontrollera om dessa webbplatser försöker omdirigera oss till olika webbadresser.

 // testadresser $ urls = array ("http://www.cnn.com", "http://www.mozilla.com", "http://www.facebook.com"); // webbläsare $ browsers = array ("standard" => array ("user_agent" => "Mozilla / 5.0 (Windows; U; Windows NT 6.1; En-US; Rv: 1.9.1.6) Gecko / 20091201 Firefox / 3,5 .6 (.NET CLR 3.5.30729) "," language "=>" en-us, en; q = 0.5 ")," iphone "=> array (" user_agent "=>" Mozilla / 5.0 ; CPU som Mac OS X; en) AppleWebKit / 420 + (KHTML, som Gecko) Version / 3.0 Mobile / 1A537a Safari / 419.3 "," language "=>" en ")," french "=> array (" user_agent " => "Mozilla / 4.0 (kompatibel; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)"; "language" => "fr, fr-FR; q = 0,5")); foreach ($ urls som $ url) echo "URL: $ url \ n"; foreach ($ webbläsare som $ test_name => $ webbläsare) $ ch = curl_init (); // Ange url curl_setopt ($ ch, CURLOPT_URL, $ url); // Ange webbläsarens specifika rubriker curl_setopt ($ ch, CURLOPT_HTTPHEADER, array ("Användaragent: $ browser ['user_agent']", "Accept-Language: $ browser ['language']")); // vi vill inte ha sidinnehållet curl_setopt ($ ch, CURLOPT_NOBODY, 1); // Vi behöver HTTP-rubriken returnerad curl_setopt ($ ch, CURLOPT_HEADER, 1); // returnera resultaten istället för att mata ut curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); $ output = curl_exec ($ ch); curl_close ($ ch); // var det en omdirigering HTTP-rubrik? om (preg_match ("! Plats: (. *)!", $ utdata, $ matchningar)) echo "$ testnamn: omdirigerar till $ matchningar [1] \ n";  annars echo "$ testnamn: ingen omdirigering \ n";  eko "\ n \ n"; 

Först har vi en uppsättning webbadresser som ska testas, följt av en uppsättning webbläsarinställningar för att testa var och en av dessa webbadresser mot. Sedan gick vi igenom dessa testfall och gjorde en cURL-förfrågan för var och en.

På grund av hur man konfigurerar cURL-alternativen, kommer den returnerade utmatningen bara att innehålla HTTP-rubrikerna (sparade i $ -utmatning). Med en enkel regex kan vi se om det fanns en "Plats:" rubrik.

När du kör det här skriptet bör du få en sådan produktion:

POSTar till en URL

På en GET-förfrågan kan data skickas till en URL via "frågesträngen". När du till exempel gör en sökning på Google, ligger sökordet i frågesträngdelen av webbadressen:

http://www.google.com/search?q=nettuts

Du kanske inte behöver cURL för att simulera detta i ett webbschript. Du kan bara vara lat och slå den där URL-adressen med "file_get_contents ()" för att få resultatet.

Men vissa HTML-formulär är inställda på POST-metoden. När dessa formulär skickas via webbläsaren skickas data via HTTP-begäran, snarare än frågesträngen. Om du till exempel söker på CodeIgniter-forumen kommer du att POSTa din sökfråga till:

http://codeigniter.com/forums/do_search/

Vi kan skriva ett PHP-skript för att simulera den här typen av URL-förfrågan. Låt oss först skapa en enkel fil för att acceptera och visa POST-data. Låt oss kalla det post_output.php:

 print_r ($ _ POST);

Nästa skapar vi ett PHP-skript för att utföra en cURL-förfrågan:

 $ url = "http: //localhost/post_output.php"; $ post_data = array ("foo" => "bar", "query" => "Webuts", "action" => "Submit"); $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // vi gör en POST-förfrågan curl_setopt ($ ch, CURLOPT_POST, 1); // lägga postvariablerna till begäran curl_setopt ($ ch, CURLOPT_POSTFIELDS, $ post_data); $ output = curl_exec ($ ch); curl_close ($ ch); echo $ output;

När du kör det här skriptet bör du få en sådan produktion:

Den skickade en POST till post_output.php-skriptet, som dumpade variabeln $ _POST och vi fångade den utmatningen via cURL.

Filuppladdning

Uppladdning av filer fungerar på samma sätt som föregående POST-exempel, eftersom alla filuppladdningsformulär har POST-metoden.

Låt oss först skapa en fil för att ta emot begäran och kalla den upload_output.php:

 print_r ($ _ FILER);

Och här är det faktiska skriptet som utför filuppladdningen:

 $ url = "http: //localhost/upload_output.php"; $ post_data = array ("foo" => "bar", // fil som ska laddas upp "upload" => "@C: /wamp/www/test.zip"); $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ ch, CURLOPT_POST, 1); curl_setopt ($ ch, CURLOPT_POSTFIELDS, $ post_data); $ output = curl_exec ($ ch); curl_close ($ ch); echo $ output;

När du vill ladda upp en fil är allt du behöver göra att skicka sin filväg precis som en postvariabel och sätt @ -symbolen framför den. Nu när du kör det här skriptet bör du få en sådan produktion:

Multi cURL

En av de mer avancerade funktionerna i cURL är möjligheten att skapa ett "multi" cURL-handtag. Detta låter dig öppna anslutningar till flera webbadresser samtidigt och asynkront.

Vid en vanlig cURL-förfrågan slutar manuskriptet körning och väntar på att webbadressförfrågan ska slutföras innan den kan fortsätta. Om du tänker slå flera webbadresser kan det ta lång tid eftersom du bara kan begära en webbadress åt gången. Vi kan övervinna denna begränsning genom att använda multi-handtaget.

Låt oss titta på denna provkod från php.net:

 // skapa båda cURL-resurserna $ ch1 = curl_init (); $ ch2 = curl_init (); // Ange webbadress och andra lämpliga alternativ curl_setopt ($ ch1, CURLOPT_URL, "http://lxr.php.net/"); curl_setopt ($ ch1, CURLOPT_HEADER, 0); curl_setopt ($ ch2, CURLOPT_URL, "http://www.php.net/"); curl_setopt ($ ch2, CURLOPT_HEADER, 0); // skapa flera cURL-handtag $ mh = curl_multi_init (); // Lägg till de två handtagen curl_multi_add_handle ($ mh, $ ch1); curl_multi_add_handle ($ mh, $ ch2); $ aktiv = null; // utföra handtagen gör $ mrc = curl_multi_exec ($ mh, $ aktiv);  medan ($ mrc == CURLM_CALL_MULTI_PERFORM); medan ($ aktiv && $ mrc == CURLM_OK) om (curl_multi_select ($ mh)! = -1) gör $ mrc = curl_multi_exec ($ mh, $ aktiv);  medan ($ mrc == CURLM_CALL_MULTI_PERFORM);  // stäng handtagen curl_multi_remove_handle ($ mh, $ ch1); curl_multi_remove_handle ($ mh, $ ch2); curl_multi_close ($ mh);

Tanken är att du kan öppna flera cURL-handtag och tilldela dem till ett enda multihandtag. Då kan du vänta på att de ska slutföra körning i en loop.

Det finns två huvudsakliga loopar i det här exemplet. Den första undergången kallas upprepade gånger curl_multi_exec (). Denna funktion är icke-blockering. Den kör så lite som möjligt och returnerar ett statusvärde. Så länge som det returnerade värdet är konstant "CURLM_CALL_MULTI_PERFORM", betyder det att det fortfarande finns mer omedelbart arbete att göra (till exempel, att skicka http-rubriker till webbadresserna.) Därför fortsätter vi att ringa till det återvändande värdet är något annat.

I den följande undergången fortsätter vi så länge som aktiv aktiv variabel är "sann". Detta överfördes som det andra argumentet till curl_multi_exec () -samtalet. Den är inställd på "true" så länge som det finns aktiva anslutningar med multihandtaget. Nästa sak vi gör är att ringa curl_multi_select (). Den här funktionen blockerar tills det finns någon anslutningsaktivitet, till exempel att ta emot ett svar. När det händer går vi in ​​ännu en gång till för att fortsätta att genomföra.

Låt oss se om vi kan skapa ett fungerande exempel oss själva, det har ett praktiskt syfte.

Wordpress Link Checker

Föreställ dig en blogg med många inlägg som innehåller länkar till externa webbplatser. Några av dessa länkar kan sluta döda efter ett tag av olika anledningar. Kanske är sidan längre där, eller hela webbplatsen är borta.

Vi ska bygga ett manus som analyserar alla länkar och hittar icke-laddningswebbplatser och 404 sidor och returnerar en rapport till oss.

Observera att det här inte kommer att vara en verklig WordPress plug-in. Det är bara ett fristående verktygsskript, och det är bara för demonstration.

Så låt oss börja. Först måste vi hämta länkarna från databasen:

 // CONFIG $ db_host = 'localhost'; $ db_user = 'root'; $ db_pass = "; $ db_name = 'wordpress'; $ excluded_domains = array ('localhost', 'www.mydomain.com'); $ max_connections = 10; // initiera några variabler $ url_list = array (); $ working_urls = array (); $ dead_urls = array (); $ not_found_urls = array (); $ aktiv = null; // ansluta till MySQL om (! mysql_connect ($ db_host, $ db_user, $ db_pass)) die : '. mysql_error ()); if (! mysql_select_db ($ db_name)) die (' Kunde inte välja db: '. mysql_error ()); // få alla publicerade inlägg som har länkar $ q = "VÄLJ post_content FRÅN wp_posts WHERE post_content LIKE '% href =%' OCH post_status = 'publicera' OCH post_type = 'post' "; $ r = mysql_query ($ q) eller dö (mysql_error ()); medan ($ d = mysql_fetch_assoc ($ r ) // få alla länkar via regex om (preg_match_all ("! href = \" (. *?) \ "!", $ d ['post_content'], $ matchningar)) foreach ($ matchningar [1] $ url // utesluta några domäner $ tmp = parse_url ($ url); om (in_array ($ tmp ['host'], $ excluded_domains)) forts; // lagra url $ url_list [] = $ url; // re flytta dubbletter $ url_list = array_values ​​(array_unique ($ url_list)); om (! $ url_list) die ('Ingen URL att kontrollera'); 

Först har vi en viss databaskonfiguration, följt av en rad domännamn som vi kommer att ignorera ($ excluded_domains). Vi ställer också ett tal för maximal samtidiga anslutningar som vi kommer att använda senare ($ max_connections). Då kopplar vi till databasen, hämtar inlägg som innehåller länkar och samlar dem in i en array ($ url_list).

Följande kod kan vara lite komplex, så jag kommer att försöka förklara det i små steg.

 // 1. multihandtag $ mh = curl_multi_init (); // 2. Lägg till flera webbadresser till multihandtaget för ($ i = 0; $ i < $max_connections; $i++)  add_url_to_multi_handle($mh, $url_list);  // 3. initial execution do  $mrc = curl_multi_exec($mh, $active);  while ($mrc == CURLM_CALL_MULTI_PERFORM); // 4. main loop while ($active && $mrc == CURLM_OK)  // 5. there is activity if (curl_multi_select($mh) != -1)  // 6. do work do  $mrc = curl_multi_exec($mh, $active);  while ($mrc == CURLM_CALL_MULTI_PERFORM); // 7. is there info? if ($mhinfo = curl_multi_info_read($mh))  // this means one of the requests were finished // 8. get the info on the curl handle $chinfo = curl_getinfo($mhinfo['handle']); // 9. dead link? if (!$chinfo['http_code'])  $dead_urls []= $chinfo['url']; // 10. 404?  else if ($chinfo['http_code'] == 404)  $not_found_urls []= $chinfo['url']; // 11. working  else  $working_urls []= $chinfo['url'];  // 12. remove the handle curl_multi_remove_handle($mh, $mhinfo['handle']); curl_close($mhinfo['handle']); // 13. add a new url and do work if (add_url_to_multi_handle($mh, $url_list))  do  $mrc = curl_multi_exec($mh, $active);  while ($mrc == CURLM_CALL_MULTI_PERFORM);     // 14. finished curl_multi_close($mh); echo "==Dead URLs==\n"; echo implode("\n",$dead_urls) . "\n\n"; echo "==404 URLs==\n"; echo implode("\n",$not_found_urls) . "\n\n"; echo "==Working URLs==\n"; echo implode("\n",$working_urls); // 15. adds a url to the multi handle function add_url_to_multi_handle($mh, $url_list)  static $index = 0; // if we have another url to get if ($url_list[$index])  // new curl handle $ch = curl_init(); // set the url curl_setopt($ch, CURLOPT_URL, $url_list[$index]); // to prevent the response from being outputted curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // follow redirections curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // do not need the body. this saves bandwidth and time curl_setopt($ch, CURLOPT_NOBODY, 1); // add it to the multi handle curl_multi_add_handle($mh, $ch); // increment so next url is used next time $index++; return true;  else  // we are done adding new URLs return false;  

Och här är förklaringen till koden ovan. Nummer i listan motsvarar numren i kodkommentaren.

  1. Skapat ett multihandtag.
  2. Vi skapar funktionen add_url_to_multi_handle () senare. Varje gång det heter, kommer det att lägga till en url till multihandtaget. Initialt lägger vi till 10 (baserat på $ max_connections) webbadresser till multihandtaget.
  3. Vi måste köra curl_multi_exec () för det första arbetet. Så länge den returnerar CURLM_CALL_MULTI_PERFORM finns det arbete att göra. Detta är främst för att skapa anslutningarna. Det väntar inte på hela URL-svaret.
  4. Denna huvudslinga löper så länge som det finns någon aktivitet i multihandtaget.
  5. curl_multi_select () väntar på skriptet tills en aktivitet händer med någon av URL-uppdrag.
  6. Återigen måste vi låta cURL göra lite arbete, främst för att hämta svardata.
  7. Vi söker efter information. Det finns en array returnerad om en URL-förfrågan var klar.
  8. Det finns ett cURL-handtag i den returnerade matrisen. Vi använder det för att hämta information om den enskilda cURL-förfrågan.
  9. Om länken var död eller tidsbestämd kommer det inte att finnas någon http-kod.
  10. Om länken var en 404-sida kommer http-koden att sättas till 404.
  11. Annars antar vi att det var en fungerande länk. (Du kan lägga till ytterligare kontroller för 500 felkoder etc ...)
  12. Vi tar bort cURL-handtaget från multihandtaget eftersom det inte längre behövs och stänger det.
  13. Vi kan nu lägga till en annan webbadress till flerhandtaget och göra det första arbetet innan vi fortsätter.
  14. Allt är klart. Vi kan stänga multihandtaget och skriva ut en rapport.
  15. Detta är den funktion som lägger till en ny webbadress till flerhandtaget. Det statiska variabel $ indexet ökas varje gång den här funktionen heter, så vi kan hålla reda på var vi slutade.

Jag sprang skriptet på min blogg (med några brutna länkar tillagt med avsikt, för testning) och här såg det ut:

Det tog bara mindre än 2 sekunder att gå igenom cirka 40 webbadresser. Prestationsvinsterna är signifikanta när det gäller att hantera ännu större uppsättningar webbadresser. Om du öppnar tio anslutningar samtidigt kan det gå upp till tio gånger snabbare. Också du kan bara utnyttja den icke-blockerande karaktären hos multi-kretshandtaget för att göra URL-förfrågningar utan att ställa in ditt webbscript.

Några andra användbara cURL-alternativ

HTTP-autentisering

Om det finns HTTP-baserad autentisering på en webbadress kan du använda följande:

 $ url = "http://www.somesite.com/members/"; $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // skicka användarnamnet och lösenordet curl_setopt ($ ch, CURLOPT_USERPWD, "myusername: mypassword"); // om du tillåter omdirigeringar curl_setopt ($ ch, CURLOPT_FOLLOWLOCATION, 1); // det här låter cURL fortsätta att skicka användarnamnet och lösenordet // efter omdirigering curl_setopt ($ ch, CURLOPT_UNRESTRICTED_AUTH, 1); $ output = curl_exec ($ ch); curl_close ($ ch);

FTP-uppladdning

PHP har ett FTP-bibliotek, men du kan också använda cURL:

 // öppna en filpekare $ file = fopen ("/ path / to / file", "r"); // url innehåller det mesta av den information som behövs $ url = "ftp: // användarnamn: [email protected]: 21 / path / to / new / file"; $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, $ url); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // ladda upp relaterade alternativ curl_setopt ($ ch, CURLOPT_UPLOAD, 1); curl_setopt ($ ch, CURLOPT_INFILE, $ fp); curl_setopt ($ ch, CURLOPT_INFILESIZE, filstorlek ("/ path / to / file")); // inställt för ASCII-läge (t ex textfiler) curl_setopt ($ ch, CURLOPT_FTPASCII, 1); $ output = curl_exec ($ ch); curl_close ($ ch);

Använda en proxy

Du kan utföra din webbadressförfrågan via en proxy:

 $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, "http: //www.example.com"); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, 1); // Ange proxyadressen för att använda curl_setopt ($ ch, CURLOPT_PROXY, '11 .11.11.11: 8080 '); // om proxyen kräver ett användarnamn och lösenord curl_setopt ($ ch, CURLOPT_PROXYUSERPWD, 'user: pass'); $ output = curl_exec ($ ch); curl_close ($ ch);

Återuppringningsfunktioner

Det är möjligt att få CURL-samtal ges återuppringningsfunktioner under URL-förfrågan innan den är klar. När innehållet i svaret till exempel laddas ner kan du börja använda data utan att vänta på att hela nedladdningen ska slutföras.

 $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, 'http: //net.tutsplus.com'); curl_setopt ($ ch, CURLOPT_WRITEFUNCTION, "progress_function"); curl_exec ($ ch); curl_close ($ ch); funktion progress_function ($ ch, $ str) echo $ str; returnera strlen ($ str); 

Återuppringningsfunktionen SKAL återvända längden på strängen, vilket är ett krav på att detta ska fungera korrekt.

När URL-svaret hämtas, kallas återuppringningsfunktionen varje gång ett datapaket tas emot.

Slutsats

Vi har utforskat kraften och flexibiliteten hos cURL-biblioteket idag. Jag hoppas att du njöt av och lärt mig av den här artikeln. Nästa gång du behöver göra en webbadressförfrågan i din webbapplikation, överväg att använda cURL.

Tack och ha en bra dag!

Skriv en Plus-handledning

Visste du att du kan tjäna upp till $ 600 för att skriva en PLUS-handledning och / eller screencast för oss? Vi letar efter djupgående och välskrivna handledning på HTML, CSS, PHP och JavaScript. Om du har förmåga, kontakta Jeffrey på [email protected].

Observera att den faktiska ersättningen kommer att vara beroende av kvaliteten på den slutliga handledningen och screencast.

  • Följ oss på Twitter, eller prenumerera på Nettuts + RSS-flödet för de bästa webbutvecklingsstudierna på webben.