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