Kirjoittaja: Metabolix
Kirjoitettu: 05.01.2011 – 13.10.2019
Tagit: kirjaston käyttö, koodi näytille, vinkki
PHP:llä on usein hyödyllistä hakea tietoa muilta sivuilta tai lähettää sitä. Mutta mitä tehdä, jos tutkittava sivusto vaatii kirjautumisen? Vaihtoehtoja on muutama: Jos kirjautuminen on toteutettu sopivalla tavalla, tavallinen file_get_contents
höystettynä stream_context_create
-funktiolla riittää. Toinen mahdollisuus on yhdistää suoraan palvelimeen fsockopen
-funktiolla ja tehtailla HTTP-pyynnöt itse. Kolmas mahdollisuus on valmiin kirjaston käyttö, jolloin kyseinen kirjasto täytyy toki ensin aktivoida PHP:n asetuksista. Yleensä valmiiden kirjastojen käyttö säästää vaivaa.
Yksi tehokas kirjasto on cURL. Se osaa muun muassa lähettää POST-dataa ja käsitellä evästeitä; näillä yleensä pääseekin jo pitkälle. Jos palvelimella on openssl-laajennos, cURL tukee myös suojattua HTTPS-protokollaa.
PHP:llä cURLin käyttö on melko suoraviivaista: Ensin cURL alustetaan funktiolla curl_init
. Sitten asetetaan tarvittavat asetukset funktiolla curl_setopt
. Sivu haetaan funktiolla curl_exec
, ja lopuksi cURL suljetaan funktiolla curl_close
.
Tässä vinkissä kirjaudutaan cURL-kirjastolla Helmet-palveluun eli pääkaupunkiseudun kirjastojen sivuille ja haetaan sieltä lista omista lainoista.
<?php // Tämä luokka sisältää muutamia perustoimintoja cURL-yhteyttä varten. class CurlIstunto { // cURL-yhteys public $ch; // Alkutoimet public function __construct() { // cURLin alustus $this->ch = curl_init(); $this->setOpt(CURLOPT_USERAGENT, "PHP"); // Käsketään cURLin palauttaa data muuttujaan eikä tiedostoon. $this->setOpt(CURLOPT_RETURNTRANSFER, 1); // Käsketään cURLin palauttaa virhetilanteessa false. $this->setOpt(CURLOPT_FAILONERROR, 1); // Annetaan cURLin käyttää pakkausta. $this->setOpt(CURLOPT_ENCODING, ""); // Laitetaan evästeet käyttöön. Tällä asetuksella evästeitä // ei tallenneta levylle ollenkaan; sen sijaan asetuksella // CURLOPT_COOKIEJAR voisi myös tallentaa evästeet tiedostoon. $this->setOpt(CURLOPT_COOKIEFILE, ""); // Seuraavilla riveillä saa SSL-sertifikaatin tarkistuksen pois: // $this->setOpt(CURLOPT_SSL_VERIFYHOST, 0); // $this->setOpt(CURLOPT_SSL_VERIFYPEER, 0); } // Lopetus public function sulje() { curl_close($this->ch); $this->ch = null; } // Asetuksen muuttaminen public function setOpt($opt, $val) { return curl_setopt($this->ch, $opt, $val); } // Nykyisillä asetuksilla sivun lataaminen protected function lataa() { return curl_exec($this->ch); } // GET-pyyntö public function get($url, $data = []) { $this->setOpt(CURLOPT_POST, 0); if ($data) { $data = http_build_query($data); $q = strpos($url, "?") ? "&" : "?"; $url = $url . $q . $data; } $this->setOpt(CURLOPT_URL, $url); $this->setOpt(CURLOPT_HTTPHEADER, []); return $this->lataa(); } // POST-pyyntö public function post($url, $data, $type) { $this->setOpt(CURLOPT_POST, 1); $this->setOpt(CURLOPT_POSTFIELDS, $data); $this->setOpt(CURLOPT_HTTPHEADER, ["Content-Type: {$type}", "Content-Length: ".strlen($data)]); $this->setOpt(CURLOPT_URL, $url); return $this->lataa(); } // POST-pyyntö lomakedatalla public function postForm($url, $data) { return $this->post($url, http_build_query($data), "application/x-www-form-urlencoded"); } // POST-pyyntö JSON-datalla public function postJson($url, $data) { return $this->post($url, json_encode($data), "application/json"); } } class HelmetIstunto extends CurlIstunto { // Oma id Helmetin järjestelmässä. private $patronId; // Kirjautuminen Helmet-tunnuksilla public function __construct($kortti, $tunnusluku) { parent::__construct(); // Haetaan kirjautumissivu, niin saadaan istunto aloitettua. $html = $this->get("https://luettelo.helmet.fi/iii/cas/login"); // Luodaan kirjautumislomakkeen data. $login_data = ["code" => $kortti, "pin" => $tunnusluku]; // Luetaan kirjautumissivulta lomakkeeseen tarvittavia tietoja. $doc = new DOMDocument(); @$doc->loadHTML($html); $xpath = new DOMXPath($doc); // Luetaan lomakkeen osoite. $url = $xpath->evaluate("string(//form[@id='fm1']/@action)"); if (substr($url, 0, 1) !== "/") { throw new RuntimeException("Helmet on muuttunut, koodia pitää korjata"); } $url = "https://luettelo.helmet.fi".$url; // Luetaan lomakkeen piilokentät. foreach ($xpath->query("//input[@type='hidden']") as $input) { $login_data[$input->getAttribute("name")] = $input->getAttribute("value"); } // Lähetetään kirjautumistiedot. $html = $this->postForm($url, $login_data); if (strpos($html, "Log In Successful") === false) { throw new RuntimeException("Helmet-kirjautuminen epäonnistui"); } // Tarvitaan muutama hyppy, jotta kirjautuminen siirtyy myös // domainista luettelo.helmet.fi domainiin haku.helmet.fi $this->setOpt(CURLOPT_FOLLOWLOCATION, 1); $html = $this->get("https://haku.helmet.fi/iii/encore/myaccount?lang=fin"); $this->setOpt(CURLOPT_FOLLOWLOCATION, 0); // Haetaan oma asiakastunnus; löytyy sivulta iframen osoitteesta. $doc = new DOMDocument(); @$doc->loadHTML($html); $xpath = new DOMXPath($doc); $iframe_url = $xpath->evaluate("string(//iframe[@id='accountContentIframe']/@src)"); if (!preg_match('#/patroninfo.*?/(\d+)/#', $iframe_url, $m)) { throw new RuntimeException("Helmet on muuttunut, koodia pitää korjata"); } $this->patronId = $m[1]; } // Omien lainojen hakeminen public function lainat() { // Osoitteeseen tulee oma id. $url = "https://luettelo.helmet.fi/dp/patroninfo*fin/{$this->patronId}/items"; $html = $this->get($url); // Dokumentista puuttuu charset, lisätään se. $html = str_replace("</head>", "<meta charset='UTF-8' /></head>", $html); // Haetaan taulukosta kirjojen nimet ja tilat. $doc = new DOMDocument(); @$doc->loadHTML($html); $xpath = new DOMXPath($doc); foreach ($xpath->query("//tr[@class='patFuncEntry']") as $tr) { $title = $xpath->evaluate("string(.//*[@class='patFuncTitleMain'])", $tr); $status = $xpath->evaluate("string(.//*[@class='patFuncStatus'])", $tr); $lainat[] = ["nimi" => trim($title), "tila" => trim($status)]; } return $lainat; } }
Koodia voi käyttää näin:
// Haetaan lainat ja näytetään ne JSON-muodossa. // Huomio! Tarvitset tietenkin omat tunnukset tähän. $h = new HelmetIstunto("20000000000000", "0000"); $lainat = $h->lainat(); echo json_encode($lainat, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), "\n"; $h->sulje();