Web Scraping

Dev Ops Practice Site

Herkese merhaba. Bugün sizlerle son zamanlarda merak saldığım web-scraping ile ilgili araştırmalarımı ve çalışmalarımı paylaşacağım. Web-scraping dediğimiz olay programlarla farklı web sitelerinde olaylar tetikleyip yönetebildiğimiz bir çalışma alanı. Biraz irdelediğimde günlük yaşama entegre edilebilir birçok proje fikri geldi aklıma ve küçük çaplı denemeler yapmaya karar verdim. Böylece Selenium modülünü tanıma ve kullanma fırsatım oldu. Şimdi Selenium modülü ile ilgili küçük bilgiler, ipuçları göstereceğim ve ardından geliştirdiğim Stok Kontrolü İle Otomatik Alışveriş projemi anlatacağım.


Selenium Nedir?

Selenium farklı tarayıcılarda, farklı web sitelerini programlar aracılığıyla kontrol edebildiğimiz bir modüldür. Java, Python, CSharp, Ruby, JavaScript, Kotlin gibi dillerde yazılabilir. Yazdığımız program ile manuel olarak yaptığımız tüm işlemleri otomatik olarak halledebiliyoruz. Click eventler, form doldurma, mail Submitimi… Kısaca bir insanın bir web sitesi üzerinde yapabileceği tüm işlemleri yapabilen kullanışlı bir modüldür. Selenium kullanabilmek için hangi tarayıcı kullanıyorsak/kullanacaksak ona ait driver’ı bilgisayarımıza yüklememiz gerekiyor. İndirme işlemini buradan yapabilirsiniz. Ben Chrome kullandığım için Chrome’un driver’ını indirdim. İndirirken kendi tarayıcınızın versiyonunuza uygun versiyonu seçmeniz gerekli. İndirdikten sonra .exe dosyasını C:/Windows gibi kolay erişilebilir bir klasör içine taşıyalım. Edindiğimiz driver ile artık yeni ve tertemiz bir sayfa açabiliriz…

Şimdi adım adım ilerleyelim. Ben paket yönetimi ve diğer tüm avantajlı ayrıcalıklardan dolayı Python Anaconda kullanacağım ve geliştirmelerimi Jupyter Notebook kullanarak yapacağım. İlk olarak basit örnekler denemek için bir ortam oluşturalım ve adım adım ilerleyerek gerekli paketleri indirelim. Şimdi conda ile testenv adında bir ortam oluşturalım. Bunun için Anaconda Promt’u açalım ve aşağıdaki komutu yazalım.

conda create -n myenv python=3.6

Şimdi oluşturduğumuz ortamı aktifleştirelim:

conda activate testenv

Buradan sonra oluşturduğumuz ortamları Jupyter notebook’a eklemek için bu adımları yapalım ve geliştirmelerimize başlayalım:

conda install -c anaconda ipykernel
python -m ipykernel kurulumu --user --name = testenv

İlk önce Selenium özelliklerini kullanabilmek için modülü indirmemiz gerekiyor. Modül ve kütüphaneleri Anaconda kullandığımız için conda install ile indiriyoruz:

conda install -c conda-forge selenium

Selenium dışında ihtiyaç duyduğumuz bütün kütüphane ve paketleri conda install ile indirebiliriz. İstediğimiz pakete uygun indirme komutlarını Anaconda’nın resmi sitesinden de bulabiliriz. Daha sonra programın en başında bu modülü tanımlamamız gerek. Driver içinde tanımladığımız path indirdiğimiz driver'ın path’idir.

from selenium import webdriver
driver = webdriver.Chrome('C:\Windows\chromedriver')

Çalıştırdığımızda boş bir Chrome sekmesi açılacak. Açılan sekme üzerinde istediğimiz siteye yönlendirme yapabiliriz:

driver.get('https://www.google.com/')

Açılan sekmeyi kapatmak için de close eventini kullanabiliriz:

driver.close()

Buraya kadar sadece driver'ı nasıl çalıştıracağımızı gördük. Fakat asıl mesele açılan pencerelerde işlem yapabilmekte. Bunun için sayfalardaki elementleri yakalamamız gerek. Bunu farklı yöntemler ile yapabiliyoruz.

Açılan Pencerede Elementleri Yakalama Yöntemleri

Sayfalar üzerinde çalışırken sayfa üzerindeki elementleri yakalamak için yöntemler vardır. Bu ifadeler sayfa tamamen yüklendiğinde çalışır. Elementleri yakalamak için kullanılan yöntemler;

Kullanım şekillerine bakmak için örneğimize devam edecek olursak Google sayfasını açınca arama yapacağımız alan search-box alanıdır ve bizim bu search-box alanını bulup text girmemiz gerekiyor. Bunun için sitenin ihtiyacımız olan elementine tıklayarak sayfa kaynağını yip alanla ilgili bilgileri bulabiliriz. Search alanını bulduktan sonra dilediğim text’i girerek arama yapabilirim.


search_box = driver.find_element_by_name('q')

Burada search-box’ı belirtildiği tag’da name benzersiz özelliği ile yakalayabiliyoruz. Bu elementi yakaladıktan sonra artık herhangi bir arama yapabiliyorum.

search_box.send_keys('Paul M. Done')
search_box.submit()

Herhangi bir aramada en ilgili olan ilk arama sonucuna baktığımızda cite tagları arasında olduğunu görüyoruz. İlk sekmeye erişmek için bu yolu kullanabiliriz:

results = driver.find_element_by_tag_name("cite").click() 

Buraya kadar bir web sitesini arama ve click eventleri neye göre yaptığımızı bir nebze görmüş olduk. Şimdi kodumuzun tamamını çalıştıraralım ve işleyişe bakalım:

from selenium import webdriver
driver = webdriver.Chrome('C:\Windows\chromedriver')
driver.get('https://www.google.com/')
search_box = driver.find_element_by_name('q')
search_box.send_keys('Paul M. Done')
search_box.submit()
results = driver.find_element_by_tag_name("cite").click()

Aslında bir site ile ilgili işlemleri yaptığımız aramalar ve içindeki tıklamalarla hallettiğimizi göz önüne alınca doğru yönlendirmeler ile iş yükünün çoğunu yapmış oluyoruz. Bunların haricinde kendi projemde kullanmış olduğum mail Submitme işlemine bakalım.

Mail özelliklerini kullanabilmek için ilk olarak smtplib kütüphanesini import ediyoruz:

import smtplib

Daha sonra bir Submitici ve alıcı bildirmemiz gerek. Ben ikisine de kendi mailimi yazarak testlerimi yapıyorum:

gmail_user = "xxxxx@xxx.com"
gmail_pwd = "xxxxx"
TO = 'xxxxx@xxx.com'

Burdan sonra da mail konusunu ve içeriğini belirttiğimiz bloğu tamamlıyoruz:

SUBJECT = "Test Subject"
TEXT = This is test message.
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(gmail_user, gmail_pwd)
BODY = '\r\n'.join(['To: %s' % TO,
    'From: %s' % gmail_user,
    'Subject: %s' % SUBJECT,
    '', TEXT])

Mail objelerinde Türkçe karakterler kullanabilmek için encode yapmamız gerekli:

server.sendmail(gmail_user, [TO], BODY.encode('utf-8')) 

Bu kod bloğunu çalıştırdığımızda resimdeki gibi mail sorunsuz şekilde gelecektir.


Wait Türleri: Implicit – Explicit Wait

Bir sayfa yüklenirken bazen gecikmeler olabiliyor. Beklenen sürenin üstünde yükleme gerçekleşmezse web driver hata verebilir. Implicit wait ile bir elementi ararken hata atmadan önce web driver’ı verdiğimiz zaman kadar bekletebiliriz. Verilen zamanın üstünde sayfa yüklenmezse ancak o zaman hata alırız. Implicit Wait metotu iki parametre alır. İlk parametre zamanın tamsayı olarak değeri, ikinci parametre ise zamanın saniye, dakika, milisaniye cinsinden türüdür.

Implicit Wait Syntax:
driver.manage().timeouts.implicitWait(10, TimeUnit.seconds) 

Explicit wait ise bizim erişmek istediğimiz öğeye uygulanır. Bu tarz zaman alacak sayfa yüklemelerinde genel olarak explicit wait kullanılır. Bulunmasını istediğimiz element için Expected Conditions belirterek bu metodu kullanıyoruz:

Explicit Wait Syntax:
wait.until(EC.visibility_of_element_located(By.XPATH, "//someXPATH"))

Pencereler Arasında Nasıl Geçiş Yaparım?

Bir web sitesinde çalışırken, işlemler yapacağımız yeni pencereler açmamız gerekebilir. Bunun için pencereler arasında geçiş yapmalıyız. Olay kontrollerini değiştirmemiz ve gerekli işlemi yapmamız gerektiği halde işlem odağı ana pencerede kalıyor. Ana pencereden yönlendiğimiz bir sayfada işlem yapamıyorsak sonraki adımı yeni bir pencere açarak devam edebilmemiz için pencereler arası geçiş yöntemini kullanırız:

#Go to new window
new_window = driver.window_handles[-1]
driver.switch_to_window(new_window)

#To get back to the first window
old_window = driver.window_handles[0]
driver.switch_to_window(old_window)

Stok Kontrolü İle Otomatik Alışveriş Uygulaması

Buraya kadar edindiğimiz bilgiler ile yaptığım projeyi kısaca tanıtmak istiyorum. Projenin temel amacı; istenilen ürün stokta yoksa kontrol ediyor ve stoğun yenilendiği durumda tespit ederek manuel işlem yapmadan direkt satın alıyor. İşlemin tamamlanması halinde kullanıcı mail ile bilgilendiriliyor. Şimdi adım adım aşamalar ile uygulamaya ait ri inceleyelim.

İlk olarak driver'ı başlatıyorum ve ilgili alışveriş sitesinin sayfasını açıyorum:

from selenium import webdriver
import smtplib
driver = webdriver.Chrome('C:\Windows\chromedriver')
driver.get('https://www.trendyol.com/butik/liste/1/kadin');

Elementleri doğru bir şekilde yakalayabilmek için sayfayı maximum boyuta ayarlamamız uygun olacaktır.

driver.maximize_window()

Açılan sayfanın arama kutusunu yakalıyorum. Arama yapmadan önce kutucuğu temizlemekte fayda var.

search_bar = driver.find_element_by_class_name("search-box")
search_bar.clear()

Benzersiz bir sonuç elde etmek için istediğim ürünün kodunu ve rengini yazarak arama yapıyorum.

search_bar.send_keys("04751303 Bej")
search_bar.send_keys(Keys.RETURN)

productResult = driver.find_element_by_class_name("prdct-desc-cntnr-name").click()

İstediğim bedene ait path’i bulup tıklıyorum.

value = wait.until(EC.visibility_of_element_located((By.XPATH, "//div[@class='variants']//div[text()='S']")))
value.click()

Sepete eklemek için butonu yakalıyorum. Farklı bir pencere açtığım için kontrolü Explicit Wait ile yapıyorum. Wait özelliği kullanabilmek için gerekli kütüphaneleri eklemeyi unutmuyorum.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
button = wait.until(EC.visibility_of_element_located((By.XPATH, "//*[@id='product-detail-app']/div/div[3]/div[1]/div[2]/div[5]/button")))

Sonrasında ürünün stokta olup olmadığını öğrenmem gerek. Eğer ürün stokta varsa “Sepete Ekle” butonu çıkıyor. Ürün stokta yoksa farklı bir buton çıkıyor. Ben de buradan ilerleyerek buton ismine göre bir koşul belirledim.

if button.text == "Sepete Ekle":
  print("Ürün stokta var!")
else:
  print("Ürün stokta yok!")

Buradan sonra ürün stokta yoksa stok yenilenip satın alma işlemi tamamlanana kadar program belli aralıklarla devam edecek. Ürün stokta ise sırayla kullanıcıya mail gidecek, sipariş bilgileri girilecek ve satın alma işlemi tamamlanacak.

Burada ürün stokta var ise satın alma işlemi kullanıcıya mail olarak bildiriliyor. Submiticiye de alıcıya da kendi mailimi girerek testlerimi yapıyorum.

gmail_user = "xxx@xxx.com"
gmail_pwd = "xxx"
TO = 'xxx@xxx.com'
SUBJECT = "Trendyol seçili ürün stoğu hakkında"
TEXT = "Trendyol'da seçmiş olduğunuz ürünün stoğu güncellenmiştir. Talebiniz doğrultusunda satın alınacaktır."
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(gmail_user, gmail_pwd)
BODY = '\r\n'.join(['To: %s' % TO,
        'From: %s' % gmail_user,
        'Subject: %s' % SUBJECT,
        '', TEXT])
    
server.sendmail(gmail_user, [TO], BODY.encode('utf-8'))
print ('Mail Submitildi!')

Sepete eklemek için butonun benzersiz name’ini belirten yolu kullandım.

addBasket = driver.find_element_by_class_name("add-to-basket-button-text").click()

Eklediğim ürünü görmek ve onaylamak için sepete gidiyorum. Buralarda da ilgili butonların path’lerini kullanarak ilerliyorum.

basket = wait.until(EC.visibility_of_element_located((By.XPATH, "//*[@id='account-navigation-container']/div/div[2]/a/p"))).click()
basketConfirm = wait.until(EC.visibility_of_element_located((By.XPATH, "//*[@id='pb-container']/aside/div/div[1]/a/span"))).click()

Sepeti onayladığım anda sayfa bizi kullanıcı giriş ekranına yönlendirecek. Burada site kullanıcı giriş bilgilerini giriyorum.

mail_box = driver.find_element_by_id("login-email")
mail_box.clear()
mail_box.send_keys('xxx@xxx.com')
mail_box.submit()
pass_box = driver.find_element_by_id("login-password-input")
pass_box.clear()
pass_box.send_keys('xxx')
pass_box.submit()

İstediğimiz adres bilgisinin path’ini işaretliyip kaydediyorum.

radioAddress = wait.until(EC.visibility_of_element_located((By.XPATH, "//*[@id='shippingAddress']/ul/li[5]/h3"))).click()
save = wait.until(EC.visibility_of_element_located((By.XPATH, "//*[@id='CheckoutAside']/section[5]/a"))).click()

Ödeme için daha önceden kayıtlı istediğim kartın bilgilerinin olduğu radio button’u ve sözleşme şartlarını kabul edeceğim chechbox’ı işaretleyip siparişi tamamlıyorum. Böylece satın alma işlemini tamamlamış oluyorum.

radioCard = wait.until(EC.visibility_of_element_located((By.XPATH, "//*[@id='creditCardPage']/div[1]/div[3]/div[2]/div/label/div[2]"))).click()
checkbox = driver.find_element_by_xpath("//*[@id='CheckoutAside']/section[3]/div[1]/label").click()
pay = wait.until(EC.visibility_of_element_located((By.XPATH, "//*[@id='CheckoutAside']/section[6]/a"))).click()

Uygulamadaki temel adımları tek tek gösterdim. Şimdi Windows Task Scheduler ile uygulamamızı belirli süre aralıklarla otomatik olarak nasıl çalıştıracağımıza bakalım.

İlk olarak Windows Task Scheduler’ı açalım.


Açtığımız ekranda Görev Oluştur seçeneğine tıklayacağız. Görev adı girerek sırayla aşağıdaki adımları gerçekleştireceğiz.


Tetikleyiciler:



Eylemler’de doldurulması gereken bazı alanlar var. Onları bu şekilde doluduruyoruz:

Program/komut dosyasına python.exe dosyasının yolunu yazalım:

C:\Users\seher\AppData\Local\Programs\Python\Python37\python.exe

Bağımsız değişkenler ekle(isteğe bağlı) alanına projenin adını yazalım:

stock_check.py

Başlangıç(isteğe bağlı) alanına projemizin dosya yolunu yazalım:

C:\Users\seher\OneDrive\Belgeler\Python


Tamam dedikten sonra açılan pop-up’da Microsoft hesabımızın şifresini yazalım:


Bu işlemler bitince oluşturduğumuz Task Scheduler’i etkinleştirip çalıştıralım:




Böylece bilgisayarımızı her açtığımızda belirli zaman aralıklarında uygulamamız çalışacak ve ürün stoğu güncellendiğinde otomatik olarak satın alma işlemini bizim yerimize yapacaktır. Task Schedule oluşturma yöntemlerinden birisi bu şekilde. Tabi ki çok daha farklı yöntemler ile zamanlayıcı oluşturarak uygulamamızı belirli zaman aralıklarında çalıştırabiliriz.

Bugün sizlere Python, Selenium Module ve Windows Task Schedule kullanarak geliştirdiğim uygulamam hakkında bilgiler verdim. Umarım faydalı bir yazı olmuştur. Farklı zamanda, yeni bilgilerle görüşmek üzere.

← Bloglara geri dön