12. Python ile web hizmetleri
![]() |
Python betikleri bir WEB sunucusu tarafından çalıştırılabilir. Müşteri isteklerini dinleyen bu sunucudur. Müşteri açısından, bir WEB hizmetini çağırmak, bu hizmetin URL'sini istemekle aynı şeydir. İstemci, Python dahil olmak üzere herhangi bir dilde yazılabilir. Bir WEB hizmetiyle “iletişim kurabilmemiz”, yani bir web sunucusu ile istemcileri arasındaki Http iletişim protokolünü anlamamız gerekir. Aşağıdaki programların amacı budur.
Web hizmeti komut dosyaları, WampServer'in Apache web sunucusu tarafından çalıştırılacaktır. Bunları belirli bir dizine yerleştirmemiz gerekir: <WampServer>\bin\apache\apachex.y.z\cgi-bin; burada <WampServer>, WampServer'in kurulum klasörüdür ve x.y.z, Apache web sunucusunun sürümüdür.
![]() |
Python betiklerini <cgi-bin> klasörüne yerleştirmek yeterli değildir. Betiğin ilk satırında kullanılacak Python yorumlayıcısının yolu belirtilmelidir. Bu yol, bir yorum satırı olarak eklenir:
Okuyucu bu yolu kendi ortamına göre uyarlayacaktır.
12.1. Tarih/saat istemci/sunucu uygulaması
İlk web hizmetimiz bir tarih ve saat hizmeti olacaktır: istemci, geçerli tarih ve saati alır.
12.1.1. Sunucu
#!D:\Programlar\ActivePython\Python2.7.2\python.exe
import time
# başlıklar
print "Content-Type: text/plain\n"
# müşteriye saat gönderimi
# yerel saat: 01/01/1970'ten bu yana geçen milisaniye sayısı
# "tarih-saat görüntüleme biçimi
# d: 2 basamaklı gün
# m: 2 basamaklı ay
# y: 2 basamaklı yıl
# H: saat 0,23
# M: dakikalar
# S: saniye
print time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
Notlar:
-
- satır: Komut dosyası, müşteriye gönderilecek yanıtın bazı HTTP başlıklarını kendisi oluşturmalıdır. Bu başlıklar, Apache sunucusu tarafından oluşturulan HTTP başlıklarına eklenecektir. 6. satırdaki HTTP başlığı, müşteriye text/plain biçiminde, yani biçimlendirilmemiş metin olarak bir kaynak gönderileceğini bildirir. Başlığın sonundaki "\n" karakterine dikkat edilmelidir; bu, başlığın arkasına boş bir satır ekleyecektir. Bu zorunludur: HTTP istemcisine yanıtın HTTP başlıklarının bittiğini bildiren işte bu boş satırdır. Ardından, istemci tarafından talep edilen kaynak gelir; burada biçimlendirilmemiş bir metindir;
-
- satır: İstemciye gönderilen kaynak, geçerli tarih ve saati gösteren bir metindir.
12.1.2. İki test
Yukarıdaki betik, şimdiye kadar yaptığımız gibi bir komut penceresinde Python yorumlayıcısı tarafından doğrudan çalıştırılabilir. Bu, olası sözdizimi veya işlevsel hataları ortadan kaldırmaya yardımcı olur. Aşağıdaki sonuç elde edilir:
Komut dosyası bu şekilde test edildikten sonra, Apache sunucusunun <cgi-bin> klasörüne yerleştirilebilir (bkz. 12. paragraf). WampServer uygulamasını çalıştıralım. Bu, hem bir Apache web sunucusunu hem de bir MySQL veritabanı sistemini başlatır. Şu an için sadece web sunucusunu kullanacağız. Ardından bir tarayıcıyla şu URL adresine erişelim: http://localhost/cgi-bin/web_02.py :
![]() |
- [1]: istenen URL;
- [2]'e: tarayıcı tarafından görüntülenen yanıt;
- [3]: web tarayıcısı tarafından alınan kaynak kodu. Bu, Python betiği tarafından gönderilen koddur.
Bazı araçlar (burada Firefox tarayıcısının bir eklentisi olan Firebug) kullanılarak sunucu ile alışverişi yapılan HTTP başlıklarına erişilebilir. Yukarıda, web tarayıcısı aşağıdaki HTTP başlıklarını almıştır:
7-8. satırlarda, Python betiği tarafından gönderilen HTTP başlığını görebiliriz. Bunların önündeki başlıklar ise Apache web sunucusu tarafından oluşturulmuştur.
12.1.3. Programlanmış bir istemci
Şimdi, önceki web hizmetinin istemcisi olacak bir komut dosyası yazacağız. HTTP istemcilerinin yazılmasını kolaylaştıran httplib modülünün özelliklerini kullanacağız.
# -*- coding=utf-8 -*-
import httplib,re
# sabitler
HOST="localhost"
URL="/cgi-bin/web_02.py"
# bağlantı
connexion=httplib.HTTPConnection(HOST)
# izleme
connexion.set_debuglevel(1)
# isteğin gönderilmesi
connexion.request("GET", URL)
# yanıtın işlenmesi
reponse=connexion.getresponse()
# içerik
contenu=reponse.read()
# bağlantının kapatılması
connexion.close()
print "------\n",contenu,"-----\n"
# saat bilgilerinin alınması
elements=re.match(r"^(\d\d)/(\d\d)/(\d\d) (\d\d):(\d\d):(\d\d)\s*$",contenu).groups()
print "Jour=%s,Mois=%s,An=%s,Heures=%s,Minutes=%s,Secondes=%s" % (elements[0],elements[1],elements[2],elements[3],elements[4],elements[5])
Notlar:
-
- satır: re modülü düzenli ifadeler için, httplib modülü ise HTTP istemci işlevleri için gereklidir;
-
- satır: 6. satırda tanımlanan HOST modülünün 80 numaralı bağlantı noktası üzerinden bir HTTP bağlantısı kurulur;
-
- satır: İzleme, istemcinin isteğinin ve sunucunun yanıtının HTTP başlıklarını görmeyi sağlar;
-
- satır: Web hizmetinin URL'i çağrılır. Bunu talep etmenin iki yolu vardır: GET veya POST HTTP komutları kullanılarak. İkisi arasındaki fark daha sonra açıklanacaktır. Burada, GET HTTP komutuyla talep edilecektir;
-
- satır: Sunucunun yanıtı okunur. Burada elde edilen yanıtın tamamıdır: HTTP başlıkları ve istemci tarafından talep edilen kaynak. Sunucu, yanıtında istemciden yeniden yönlendirilmesini isteyebilir. Bu durumda, httplib istemcisi otomatik olarak yeniden yönlendirmeyi gerçekleştirir. Dolayısıyla elde edilen yanıt, yeniden yönlendirmenin sonucunda elde edilen yanıttır;
-
- satır: Yanıt, HTTP başlıkları ve istemci tarafından istenen belgeden oluşur. Yalnızca HTTP başlıklarını almak için [reponse].getHeaders() işlevi kullanılır. Belgeyi almak için ise [reponse].read() işlevi kullanılır;
-
- satır: Web sunucusundan yanıt alındıktan sonra, sunucuyla olan bağlantı kapatılır;
- Sunucu tarafından gönderilen belgenin 15/06/11 14:56:36 biçiminde bir metin satırı olduğu bilinmektedir. 22-26. satırlarda, bu satırdaki çeşitli öğeleri almak için bir düzenli ifade kullanılır.
12.1.4. Sonuçlar
Notlar:
-
- satır: İstemci tarafından web sunucusuna gönderilen HTTP başlıkları;
- 2-6. satırlar: web sunucusunun yanıtındaki HTTP başlıkları;
-
- satır: sunucu tarafından gönderilen belge;
-
- satır: İşlenmesinin sonucu;
12.2. Sunucu tarafından istemci tarafından gönderilen parametrelerin alınması
HTTP protokolünde, bir istemcinin web sunucusuna parametreleri iletmek için iki yöntemi vardır:
- hizmetin URL'sini şu biçimde talep eder
GET url?param1=val1¶m2=val2¶m3=val3… HTTP/1.0
şeklinde hizmetin vali değerini talep eder; burada vali değerleri, belirli ayrılmış karakterlerin onaltılık değerleriyle değiştirilmesi için önceden kodlanmalıdır.
- hizmetin URL değerini şu biçimde ister:
şeklinde ister; ardından sunucuya gönderilen HTTP başlıkları arasına şu başlığı ekler:
İstemci tarafından gönderilen diğer başlıklar boş bir satırla sona erer. Ardından verilerini şu biçimde gönderebilir:
şeklinde gönderebilir; burada vali, GET yönteminde olduğu gibi önceden kodlanmalıdır. Sunucuya gönderilen karakter sayısı N olmalıdır; burada N, başlıkta bildirilen değerdir:
12.2.1. Web hizmeti
Aşağıdaki web hizmeti, istemcisinden 3 parametre alır: nom, prenom, age. Bu parametreleri, cgi modülü tarafından sağlanan cgi.FieldStorage adlı bir tür sözlükten alır. parami parametresinin vali değeri, vali=cgi.FieldStorage().getlist("parami") komutuyla elde edilir. Aşağıdaki dizi elde edilir:
- parami parametresi istemcinin isteğinde bulunmuyorsa 0 eleman;
- parami parametresi istemcinin isteğinde 1 kez mevcutsa 1 eleman;
- parami parametresi, istemcinin isteğinde n kez mevcutsa n eleman.
Parametreler alındıktan sonra, komut dosyası bunları müşteriye geri gönderir.
#!D:\Programlar\ActivePython\Python2.7.2\python.exe
import cgi
# başlıklar
print "Content-Type: text/plain\n"
# sunucu tarafından istemci tarafından gönderilen bilgilerin alınması
# burada ad=P&soyad=N&yaş=A
formulaire=cgi.FieldStorage()
# bunlar müşteriye geri gönderilir
print "informations recues du service web [prenom=%s,nom=%s,age=%s]" % (formulaire.getlist("prenom"),formulaire.getlist("nom"),formulaire.getlist("age"))
Bir web tarayıcısı ile test yapılabilir:
![]() |
[1]'te, web hizmetinin URL'i yer almaktadır. nom, prenom ve age olmak üzere üç parametrenin varlığına dikkat edilmelidir. [2]'te web hizmetinin yanıtı yer almaktadır.
12.2.2. GET istemcisi
# -*- coding=utf-8 -*-
import httplib,urllib
# sabitler
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# parametreler sunucuya gönderilmeden önce kodlanmalıdır
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# parametreler URL'in sonuna eklenir
URL+="?"+params
# bağlantısı
connexion=httplib.HTTPConnection(HOST)
# izleme
connexion.set_debuglevel(1)
# isteğin gönderilmesi
connexion.request("GET",URL)
# yanıtın işlenmesi
reponse=connexion.getresponse()
# içerik
contenu=reponse.read()
print contenu,"\n"
# bağlantının kapatılması
connexion.close()
Notlar:
- 8-10. satırlar: web hizmetine gönderilen 3 parametrenin değerleri;
-
- satır: Bu değerlerin kodlanması gerekir. Bu işlem, urllib modülünün urlencode yöntemi kullanılarak yapılır. Bu modül 3. satırda içe aktarılır. Yöntem, {param1:val1, param2:val2, ...} biçiminde bir sözlük parametre olarak kabul eder;
-
- satır: Bir GET komutunda (21. satır), istemci kodlanmış parametreleri web hizmetinin URL komutunun sonuna eklemelidir;
- Sonraki satırlar daha önce incelenmiştir.
12.2.3. Sonuçlar
Notlar:
-
- satır: Parametrelerin (soyadı, adı, yaş) kodlamasına dikkat edin;
-
- satır: web hizmetinin yanıtı.
12.2.4. POST istemcisi
POST istemcisi, kodlanmış parametrelerin artık hedef URL'in bir parçası olmaması dışında GET istemcisine benzerdir. Bu parametreler, POST isteğinin 3. argümanı olarak aktarılmıştır (19. satır).
# -*- coding=utf-8 -*-
import httplib,urllib
# sabitler
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# parametreler sunucuya gönderilmeden önce kodlanmalıdır
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# bağlantı
connexion=httplib.HTTPConnection(HOST)
# izleme
connexion.set_debuglevel(1)
# isteğin gönderilmesi
connexion.request("POST",URL,params)
# yanıtın işlenmesi
reponse=connexion.getresponse()
# içerik
contenu=reponse.read()
print contenu,"\n"
# bağlantının kapatılması
connexion.close()
12.2.5. Sonuçlar
Notlar:
-
- satırda, POST istemcisinin kodlanmış parametreleri göndermek için kullandığı yönteme dikkat edilmelidir:
- Content-Length HTTP başlığı, web hizmetine gönderilecek karakter sayısını belirtir;
- bu HTTP başlığının ardından, HTTP başlıklarının sonunu belirten boş bir satır gelir;
- ardından kodlanmış parametrelerin 39 karakteri gönderilir.
-
- satır: web hizmetinin yanıtı.
12.3. Bir web hizmetinden ortam değişkenlerinin alınması
12.3.1. Web hizmeti
Python CGI betiği, belirli özelliklere sahip bir sistem ortamında çalışır. Bu özellikler ve değerleri, os.environ adlı bir sözlükte bulunur.
#!D:\Programlar\ActivePython\Python2.7.2\python.exe
import os
# başlıklar
print "Content-Type: text/plain\n"
# ortam bilgileri
for (cle,valeur) in os.environ.items():
print "%s : %s" % (cle,valeur)
Notlar:
-
- satır: "sistem" değişkenlerini kullanabilmek için os modülünü içe aktarmak gerekir.
Yukarıdaki betiği doğrudan çalıştırırsak (c.a.d, cgi değil konsol betiği olarak), konsolda şu sonuçlar elde edilir:
Bir web tarayıcısında (bu durumda cgi betiği çalıştırılır), şu sonuçlar elde edilir:
![]() |
Yürütme bağlamına göre elde edilen ortamın aynı olmadığına dikkat edilmelidir.
12.3.2. Programlanmış müşteri
# -*- coding=utf-8 -*-
import httplib
# sabitler
HOST="localhost"
URL="/cgi-bin/web_04.py"
# bağlantı
connexion=httplib.HTTPConnection(HOST)
# isteğin gönderilmesi
connexion.request("GET", URL)
# yanıtın işlenmesi
reponse=connexion.getresponse()
# içerik
print reponse.read()
12.3.3. Sonuçlar
Programlanmış istemcinin, web tarayıcısıyla tam olarak aynı yanıtı almadığına dikkat edilmelidir. Bunun nedeni, web tarayıcısının web sunucusuna, sunucunun yanıtını oluşturmak için kullandığı bilgileri göndermiş olmasıdır. Burada ise programlanmış istemci, kendisi hakkında hiçbir bilgi göndermedi.




