Saturday, May 31, 2008

Python Sinema API'si

Vizyondaki filmleri, şehirdeki salonları , filmlerin hangi salonda saat kaçta gösterildiğini bulmaya yarayan bir API yazmaya karar verdim. (Kardeşim ne derdin var durduk yere insan API yazmaya karar verir mi diyenleriniz olabilir. Neden böyle bir API'ye ihtiyaç duyduğumu ilerleyen yazılarda anlatacağım.) Filmler ile ilgili bilgileri, seçeceğim bir sinema sitesinden HTML ayıklama yöntemi (screen scraping) ile almaya karar verdim. Daha doğrusu bu şekilde yapmak zorunda kaldım çünkü delicious, flickr, digg gibi kullanıcılarına API desteği veren türkçe sinema sitesi bulamadım.

Sinema sitelerini teker teker gezerken MyNet'in satın aldığı Beyazperde sitesi dikkatimi cekti.
Beyazperde sitesi anlaşılabilir, temiz URL'ler kullanan ender türkçe sitelerden birisi. Ne demek istediğimi aşağıdaki URL'lere bakınca anlayabilirsiniz.
  • http://beyazperde.mynet.com/salon/10
  • http://beyazperde.mynet.com/film/3945
  • http://beyazperde.mynet.com/seanslar/3945/sehir/462
    Genelde sinemaya Mecidiyekö-Profilo AFM'de gidiyorum. Profilo AFM'nin Beyazperde'deki salon numarası 10 olduğu için gösterimdeki filmleri görmek için uzun uzun ana sayfaya git, şehir seç, salon seç işlemleri yapmıyorum. Firefox'ta adres çubuğuna http://beyazperde.mynet.com/salon/10 yazman AFM Profilo'da gösterimdeki filmlere erişmeme yetiyor.

    Beyazperde sitesindeki filmleri, salonları, seanslari html içerisinden ayıklayabilmek için BeautifulSoup html-xml parser'ını kullandım. BeautifulSoup'tan bu yazımda da bahsetmiştim. Ve ortaya sinema.py adresinden indirebileceğiniz basit bir sinema sever API'si çıktı.

    Örneklerle API'nin nasıl kullanıldığına bakalım.
    Vizyondaki filmleri listelemek için aşağıdaki örnekteki gibi vizyondakiFilmler fonksiyonu kullanılıyor.

    >>> import sinema
    >>> filmler=sinema.vizyondakiFilmler()
    >>> len(filmler)
    75
    >>> filmler.keys()
    [u'3922', u'3949', u'3920', u'3711', u'3808', u'3902', u'3835', u'3869', u'3941', u'3634',
    u'3867', u'3800', u'3946', u'3947', u'3916', u'3958', u'3956', u'3926', u'3734', u'2601',
    u'3910', u'3305', u'3911', u'3934', u'3948', u'2812', u'3964', u'3793', u'3845', u'1694',
    u'3855', u'3898', u'3918', u'3862', u'3978', u'3897', u'3805', u'3844', u'3780', u'3915',
    u'3873', u'3816', u'3847', u'3809', u'3833', u'3953', u'3952', u'3872', u'3950', u'3957',
    u'3930', u'3919', u'3945', u'3870', u'3936', u'3876', u'3932', u'3959', u'3885', u'3887',
    u'3837', u'3931', u'3883', u'3882', u'3612', u'3888', u'3871', u'3758', u'1683', u'3143',
    u'3962', u'3963', u'3960', u'3961', u'3825']
    >>> filmler['1694']
    u'Indiana Jones ve Kristal Kafatas\u0131 Krall\u0131'

    Seçilen filmin gösterimde olduğuşehirlerin listesi için filminGosterildigiSehirler fonksiyonu çağırılır.

    >>> sehirler=sinema.filminGosterildigiSehirler('1694')
    >>> sehirler
    {u'216': u'\u0130stanbul Anadolu', u'318': u'K\u0131r\u0131kkale', u'212': u'\u0130stanbul
    Avrupa', u'312': u'Ankara', u'452': u'Ordu', u'242': u'Antalya', u'454': u'Giresun', u'332':
    u'Konya', u'392': u'K\u0131br\u0131s', u'258': u'Denizli', u'252': u'Mu\u011fla', u'236':
    u'Manisa', u'256': u'Ayd\u0131n', u'232': u'\u0130zmir', u'248': u'Burdur', u'322': u'Adana',
    u'288': u'K\u0131rklareli', u'346': u'Sivas', u'326': u'Hatay', u'342': u'Gaziantep', u'422':
    u'Malatya', u'362': u'Samsun', u'284': u'Edirne', u'286': u'\xc7anakkale', u'324': u'Mersin',
    u'442': u'Erzurum', u'462': u'Trabzon', u'262': u'\u0130zmit', u'246': u'Isparta', u'266':
    u'Bal\u0131kesir', u'386': u'K\u0131r\u015fehir', u'264': u'Adapazar\u0131', u'226': u'Yalova',
    u'224': u'Bursa', u'222': u'Eski\u015fehir', u'282': u'Tekirda\u011f', u'272': u'Afyon', u'352':
    u'Kayseri', u'412': u'Diyarbak\u0131r', u'372': u'Zonguldak', u'374': u'Bolu'}
    >>> sehirler['212']
    u'\u0130stanbul Avrupa'

    Seçilen filmin şehirde hangi salonlarda gösterildiğini bulmak için sehirdekiSalonlar fonksiyonu kullanılıyor.

    >>> salonlar = sinema.sehirdekiSalonlar('1694','212')
    >>> salonlar
    [(u'1120', u'Adapazar\u0131 Cinebonus (Ada)', [u'11:00', u'12:15', u'13:30', u'15:00', u'16:15',
    u'17:45', u'19:00', u'20:30', u'21:45', u'23:15']), (u'1000', u'Akatlar Finansbank AFM
    Mayadrom', [u'11:15', u'13:45', u'16:15', u'18:45', u'21:15']), (u'1081', u'Bak\u0131rk\xf6y
    Cinebonus (Capacity)', [u'11:00', u'11:00', u'12:00', u'13:30', u'13:45', u'16:15', u'16:30',
    u'19:00', u'19:15', u'21:45', u'22:00', u'24:30', u'(Cuma, Cmts 24:45']), (u'1118',
    u'Beylikd\xfcz\xfc Cine Marka', [u'11:00', u'12:15', u'13:30', u'14:45', u'16:00', u'17:15',
    u'18:30', u'19:45', u'21:00', u'22:15']), (u'1082', u'Beylikd\xfcz\xfc Fox City', [u'11:30',
    u'14:00', u'16:30', u'19:00', u'21:30']), (u'1117', u'Esentepe Cinebonus (Astoria)', [u'11:00',
    u'12:15', u'13:30', u'14:45', u'16:15', u'17:30', u'19:00', u'20:15', u'21:45', u'(Cuma, Cmts
    23:0............

    >>> salonlar[11]
    (u'1067', u'\u0130stinye AFM Park', [u'10:50', u'11:30', u'12:25', u'13:55', u'14:15', u'15:30',
    u'17:00', u'17:15', u'18:30', u'19:00', u'20:00', u'21:30', u'21:50'])
    >>> salonlar[11][2]
    [u'10:50', u'11:30', u'12:25', u'13:55', u'14:15', u'15:30', u'17:00', u'17:15', u'18:30',
    u'19:00', u'20:00', u'21:30', u'21:50']

    Ve son olarak seçilen salonda gösterilen filmleri bulmak için salondakiFilmler fonksiyonu kullanılıyor.

    >>> filmler=sinema.salondakiFilmler('10')
    >>> filmler
    [(u'3945', u'88 Dakika', [u'11:50', u'14:20', u'16:50', u'19:30', u'21:50']), (u'1694', u'Indiana
    Jones ve Kristal Kafatas\u0131 Krall\u0131\u011f\u0131', [u'10:50', u'11:10', u'13:30', u'13:50',
    u'16:20', u'16:30', u'19:00', u'19:10', u'21:30', u'21:45']), (u'3964', u'O... \xc7ocuklar
    \u0131', [u'11:20', u'14:00', u'16:45', u'19:15', u'21:55']), (u'3949', u'\xd6l\xfcmc\xfcl
    Oyunlar', [u'11:30', u'14:10', u'16:40', u'19:20', u'22:00']), (u'3800', u'Sex and the City',
    [u'10:40', u'12:15', u'13:50', u'15:30', u'17:10', u'18:50', u'21:30', u'22:10', u'(Cuma, Cmts
    23:50'])]
    >>> filmler[0][1]
    u'88 Dakika'


    Söylemeden geçemeyeceğim python ile yazılan bu API dokumantasyonu ile beraber 100 satırı geçmiyor. Beyazperde sitesi gününbirinde html arayüzünde bir değişiklik yaparsa bu API'nin de güncellenmesi gerekiyor.

  • Sunday, May 25, 2008

    Takım çalışması üzerine atıp tutmalar...

    1997 yılında Galatarasay Üniversitesin'de Bilg. Mühendisliği okumak için İstanbul'a geldim. 2001 yılından beri de (yaklaşık 7 sene olmus) yazılım işinden para kazanıyorum. Üniversite yıllarında aylık masraflarımı çıkarmasa da bütçeye katkı sağlayacak part-time bir işim vardı. Üniversiteden mezun olduktan sonra bir yıl Galatasaray Üniversitesi'nde araştırma görevlisi olarak çalıştım. Bir şey araştırdığımız yoktu ama adı öyle işte "araştırma görevlisi". Özel sektörde çalışmanın benim için daha avantajlı olacağına karar verip 2003 yılında Oksijen'de , şimdiki adı ile Vodafone IT Hizmetleri, çalışmaya başladım.
    Ekip olarak bir yazılım projesi yürütme fırsatını ilk olarak Oksijen'de yakaladım. Daha önceden yaptığım işlerde hep tek başınaydım. Tasarım ile ilgili bütün kararlari kendim verip hangi teknolojileri kullanacağıma kendim karar veriyordum. Başarı da benimdi başarısızlık da. Yazılım dünyasında tecrübe kazandıkça takım çalışmasının bireysel çalışmalardan daha zevkli ve daha fazla tatmin edici olduğunu farkettim.

    Bazıları için bireysel başarılar, takım başarılarından daha fazla tatmin edici olabilir. Klasik bir söz vardır: Mutluluk paylaştıkça artar, acılar paylaştıkça azalır. Bence takım çalışmaları için de geçerlidir bu söz. Zor bir projenin stresi takımın bütün üyelerinin omuzlarına dağıldığında tek başına katlanılamayacak stresler takım olarak üstesinden gelinebilecek bir engel halini alır. Takım başarısının verdiği haz bana göre bireysel başarılardan daha fazladır. Tenis müsabakasında bir set almış bir sporcunun sevinciyle, futbol ya da basketbol maçını kazanmış bir takım elemanının sevincini karşılaştıracak olursanız ne demek istediğimi anlarsınız. Gol atan bir futbolcunun takım arkadaşlarına koşup gol sevinci yaşamasını bireysel sporların hangisinde görebilirsiniz?

    Takdir edilme isteği insanın doğasında vardır. Yaptığı başarılı bir iş sonunda takdir edilmeyen çoğu insan motivasyonunu kaybetmeye yakındır. Takım çalışması ile kazanılan başarılardan sonra takım üyeleri, bireysel başarılarda olduğu kadar, takdir edilme ihtiyacı hissetmezler. Kendi çalıştığım projelerden örnek vermek istiyorum. Bir işi başardığımızda, zor bir işi en iyi şekilde yaptığımızı takım arkadaşlarımın da bilmesi, harcanan emekten takım arkadaşlarımın da haberdar olması beni tatmin etmeye yetiyor. Ayrıca bir takdir edilme ihtiyacı hissetmiyorum. Takımdaki diğer arkadaşlar için de böyle olduğunu tahmin ediyorum.

    İşler yetişmediğinde fazla mesai yapmak takım olarak çalışıldığında zevkli bile olabiliyor bazen. Tek kişi ile yürütülen projelerde fazla mesailerin eziyet olduğunu hepimiz biliyoruz. İşlerin yetişmediği durumlarda yöneticilerin görünmez baskısı ve işi yetiştirememenin verdiği mahçupluk hissi takım olarak çalışıldığında katlanabilir seviyelere iner.

    Takım çalışmasının bir başka avantajı da sizin yazdığınız kod parçasının, projenin detaylarından haberdar takımın başka bir üyesi tarafından gözden geçirilebilmesidir. Tek kişilik projelerde "code review" yapacak kişi yine siz olacağınız için farklı bir gözün getireceği avantajlardan yararlanamayacaksınız.

    İşte benim takim çalışması ve bireysel çalışmalar ile ilgili naçizane fikirlerim bunlardır.

    Cep Telefonu ile RSS takip etmek isteyenler için : gozgezdir.appspot.com


    İki ay kadar önce Google, Appengine'i duyurduğunda hemen bir developer hesabı almak için başvurmuştum fakat appengine'e ilginin çok fazla olması sebebiyle benim başvurumu diğer binlerce başvuru gibi beklemeye almışlardı. Neyse lafı fazla uzatmayayım geçen hafta Google'dan appengine hesabımın aktif hale getirildiğini söyleyen bir mail aldım. Artık ben de google'ın alt yapısını kullanarak fantastik web uygulamaları geliştirebilecektim (Hem de beleş, cebimden kuruş para harcamadan.)

    Yazıya devam etmeden "Appengine de nedir yav?" diyenler için ufak bir açılama yapmak gerekiyor sanırım. Google Appengine, yazdığınız web uygulamalarını Google'ın kullandığı altyapıda çalıştırmanızı sağlayan bir sistemdir. Demek istediğim şu, siz uygulamanızı yazıyorsunuz ve Google sunucularına deploy ediyorsunuz. Google'ın veritabanlarını, Google'ın CPU'larını, Google'ın disklerini ve Google'ın bantgenişliğini kullanarak kullanıcılarınıza web uygulamanızdan servis veriyorsunuz.

    Google Appengine'i tasarlayan google mühendisleri programlama dili olarak Python'u seçmişler. Yani Appengine'e deploy edilecek web uygulamasının Python ile yazılması gerekiyor. İleriki sürümlerinde Appengine'in Ruby'yi de destekleceğinden bahsediliyor. Günün birinde Java'yı da desteklerler mi bilmiyorum ama ben Python ile web uygulamalarının ne kadar kolay yazıldığını gördükten sonra java ile bu işe bir daha hayatta bulaşmam :)

    Google Appengine üzerinden çalıştırmayı düşündüğünüz web uygulamasını kafanıza göre yazamıyorsunuz. Google yazdığınız uygulamanın Google mimarisi üzerinde ölçeklenebilir olması için bazı şartlar-kısıtlar koymuş. Uygulama geliştiricilerin bu şartlara uymasını bekliyorlar. Örneğin thread'leri kullanmak yasak, Filesystem işlemleri yapmak yasak,Socket açmak yasak. Bu yasakları koymuşlar ama uygulama geliştiricilere aşağıdaki imkanları veriyorlar:

    • 500 MB'a kadar ücretsiz alan.
    • Her ay 5 milyon sayfa görüntüleme. Günde 166 bin sayfa görüntüleme demek oluyor ki zaten böyle bir web uygulamanız varsa parasını verir Appengine premium hesabı alırsınız.
    • Uygulama geliştiriciler bir Google Appengine hesabı ile en fazla 3 uygulama çalıştırabilirler.
    • Socket açmak yasak demiştik fakat Appengine URL Fetch API'sini sunuyor. HTTP ve HTTPS destekliyor.
    • Filesystem işlemleri yapmak yasak demiştik fakat uygulamalar kendi upload ettikleri dosyaları okuyabilirler (konfigurasyon dosyalarını mesela)
    • Google Accounts : uygulamanızı halihazırda google'a üye olan herkes kullanabilir. Authentication Google User API ile yapılabilir.
    • Web uygulamaları Google Appengine Email servisini kullanarak mail atabilirler.
    • Appengine servislerinin bence en canalıcı olanı Datastore servisi. "Distributed data storage service" türkçeye nasıl çevireceğimi bilemedim. Datastore API'sini kullanarak milyonlarca kayıtlı tabloları Google sunucularında saklayabilirsiniz. (500MB'ı geçmemek şartıyla)
    • Python runtime environment kırpılarak kullandırılıyor. Socket açmak, thread başlatmak gibi servisler kapatılmış. Fakat diğer yasaklanmamış standart Python modülleri bir uygulama için yeterli oluyor. 3rd Party modulleri uygulamanız ile beraber upload ederek kullanabilirsiniz. Örneğin ben Gözgezdir'de RSS parser olarak UniversalFeedParser kullandım.
    • WSGI uyumlu web framework'leri ile çalışabiliyor. Kendi içinde Appengine WebApp Framework var. İsteyenler Django'yu da kullanabiliyorlar. Ben Appengine içinde gelen WebApp framework'ü kullanmayı tercih ettim.


    Hem Google'ın bu yeni servisini denemek hem de uzun zamandır düşündüğüm bir projeyi yapmak için kolları sıvadım. Cep telefonundan RSS okumak için ufak bir uygulama hazırladım. Halen BETA sürümünde olan bu uygulamanın ana fikri GPRS ile WEB' erişen küçük ekranlı cihazlar ile hesaplı bir şekilde RSS kaynaklarını dolaşmak. Uygulamayı tamamlamadan adresini buradan vermem doğru olur mu bilmiyorum ama belki bir kaç kişi kullanırsa tasarım ve kullanılabilirlik için benim aklıma gelmeyen fikirler verebilirler.
    Google yazdığınız appengine uygulamalarını uygulamaadi.appspot.com adresinden yayınlıyor. Fakat kendi alan adınızı alıp Google üzerinden yönlendirmesini yapmanız da mümkün. Ben yine pintilik yapıp alan adına para vermedim ve http://gozgezdir.appspot.com adresinden uygulamayı yayına aldım.
    İlgilenen arkadaşlar ile kaynak kodunu paylaşabilirim. Benim gibi yıllarca Java ile yazılım geliştirmiş Java'dan başka birşeyi gözü görmeyen yazılımcı arkadaşların python ile tanışıp böyle bir dil de varmış vay be demelerini isterim açıkçası :)