Python ve Selenium ile Modern Web Kazıma

Yayınlanan: 2022-03-11

Web kazıma, neredeyse World Wide Web'in doğduğu andan itibaren web sitelerinden veri çıkarmak için kullanılmıştır. İlk günlerde, kazıma esas olarak statik sayfalarda yapıldı - bilinen öğeler, etiketler ve veriler içerenler.

Ancak daha yakın zamanlarda, web geliştirmedeki ileri teknolojiler, görevi biraz daha zorlaştırdı. Bu makalede, yeni teknolojinin ve diğer faktörlerin standart kazımayı engellemesi durumunda verileri nasıl kazıyabileceğimizi keşfedeceğiz.

Geleneksel Veri Kazıma

Çoğu web sitesi otomatik okuma yerine insan tarafından okunabilirlik için tasarlanmış sayfalar ürettiğinden, web kazıma esas olarak bir web sayfasının işaretleme verilerini programlı olarak sindirmekten (sağ tıklamayı düşünün, Kaynağı Görüntüle), ardından bu verilerde programın izin verecek statik kalıpları tespit etmekten oluşuyordu. çeşitli bilgi parçalarını “okumak” ve bir dosyaya veya veri tabanına kaydetmek için.

Veri Kazıma

Rapor verileri bulunacak olsaydı, genellikle, form değişkenleri veya URL ile parametreler geçirilerek verilere erişilebilirdi. Örneğin:

 https://www.myreportdata.com?month=12&year=2004&clientid=24823

Python, kısmen onun için oluşturulmuş çeşitli web kitaplıkları nedeniyle en popüler web kazıma dillerinden biri haline geldi. Popüler bir kitaplık olan Güzel Çorba, etiketleri (yani ayrıştırma ağacını) aramaya, gezinmeye ve değiştirmeye izin vererek HTML ve XML dosyalarından veri çekmek için tasarlanmıştır.

Tarayıcı Tabanlı Kazıma

Son zamanlarda, oldukça basit görünen bir kazıma projem vardı ve bunu halletmek için geleneksel kazımayı kullanmaya tamamen hazırdım. Ama işin içine girdikçe geleneksel yöntemlerle aşılamayacak engellerle karşılaştım.

Üç ana sorun, standart kazıma yöntemlerimi kullanmamı engelledi:

  1. Sertifika. Web sitesinin verilerin bulunduğu kısmına erişmek için yüklenmesi gereken bir sertifika vardı. İlk sayfaya erişirken, bilgisayarımda yüklü olanların uygun sertifikasını seçmemi isteyen bir istem belirdi ve Tamam'a tıklayın.
  2. iframe'ler. Site, normal kazımamı bozan iframe'leri kullandı. Evet, tüm iframe URL'lerini bulmayı deneyebilir ve ardından bir site haritası oluşturabilirdim, ancak bu iş hantallaşabilecek gibi görünüyordu.
  3. JavaScript. Verilere, parametrelerle (örn. müşteri kimliği, tarih aralığı vb.) bir form doldurulduktan sonra erişildi. Normalde, formu atlar ve form değişkenlerini (URL aracılığıyla veya gizli form değişkenleri olarak) sonuç sayfasına iletir ve sonuçları görürdüm. Ancak bu durumda, form, form değişkenlerine normal bir şekilde erişmeme izin vermeyen JavaScript içeriyordu.

Bu yüzden, geleneksel yöntemlerimi terk etmeye ve tarayıcı tabanlı kazıma için olası bir araca bakmaya karar verdim. Bu normalden farklı bir şekilde çalışırdı - doğrudan bir sayfaya gitmek, ayrıştırma ağacını indirmek ve veri öğelerini çıkarmak yerine, bunun yerine "insan gibi davranırdım" ve ihtiyacım olan sayfaya ulaşmak için bir tarayıcı kullanırdım, sonra sıyırırdım. veri - böylece, belirtilen engellerle başa çıkma ihtiyacını atlayarak.

Selenyum

Genel olarak Selenium, web uygulamaları için açık kaynaklı bir test çerçevesi olarak bilinir - QA uzmanlarının otomatik testler gerçekleştirmesine, oynatma yürütmesine ve uzaktan kontrol işlevselliği uygulamasına olanak tanır (birçok tarayıcı örneğinin yük testi ve birden çok tarayıcı türü için izin verir). Benim durumumda, bu yararlı olabilir gibi görünüyordu.

Genel olarak gerekli tüm işlevleri yerine getirebilen iyi entegre kitaplıklara sahip olduğundan, web kazıma için başvuracağım dilim Python'dur. Ve tabii ki Python için bir Selenium kütüphanesi var. Bu, bir "tarayıcı" (Chrome, Firefox, IE, vb.) başlatmama ve ardından aradığım verilere erişmek için tarayıcıyı kendim kullanıyormuşum gibi davranmama izin verecekti. Ve tarayıcının gerçekten görünmesini istemezsem, tarayıcıyı "başsız" modda oluşturarak herhangi bir kullanıcı için görünmez hale getirebilirdim.

Proje Kurulumu

Denemeye başlamak için projemi kurmam ve ihtiyacım olan her şeyi almam gerekiyordu. Bir Windows 10 makinesi kullandım ve nispeten güncellenmiş bir Python sürümüne sahip olduğumdan emin oldum (v. 3.7.3 idi). Boş bir Python betiği oluşturdum, sonra gerekli olabileceğini düşündüğüm kitaplıkları yükledim, kitaplığı zaten yüklememişsem PIP (Python için paket yükleyici) kullanarak. Bunlar başladığım ana kütüphaneler:

  1. İstekler (HTTP istekleri yapmak için)
  2. URLLib3 (URL işleme)
  3. Güzel Çorba (Selenium'un her şeyi halledememesi durumunda)
  4. Selenyum (tarayıcı tabanlı gezinme için)

Ayrıca komut dosyasına bazı çağrı parametreleri ekledim (argparse kitaplığını kullanarak), böylece komut satırından farklı seçeneklerle komut dosyasını çağırarak çeşitli veri kümeleriyle oynayabilirim. Bunlara Müşteri Kimliği, aydan/yıldan ve aydan/yıla kadar dahildir.

Problem 1 – Sertifika

Yapmam gereken ilk seçim, Selenium'a hangi tarayıcıyı kullanmasını söyleyeceğimdi. Genelde Chrome kullandığım ve açık kaynaklı Chromium projesi (Edge, Opera ve Amazon Silk tarayıcıları tarafından da kullanılır) üzerine kurulduğu için, önce bunu deneyeceğimi düşündüm.

İhtiyacım olan kitaplık bileşenlerini ekleyerek ve ardından birkaç basit komut vererek Chrome'u komut dosyasında başlatabildim:

 # Load selenium components from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException # Establish chrome driver and go to report site URL url = "https://reportdata.mytestsite.com/transactionSearch.jsp" driver = webdriver.Chrome() driver.get(url)

Tarayıcıyı başsız modda başlatmadığım için tarayıcı gerçekten göründü ve ne yaptığını görebiliyordum. Hemen (daha önce yüklediğim) bir sertifika seçmemi istedi.

Çözülmesi gereken ilk sorun sertifikaydı. Web sitesine girmek için uygun olanı nasıl seçer ve kabul edersiniz? Senaryonun ilk testinde şu istemi aldım:

Veri Kazıma

Bu iyi değildi. Komut dosyamı her çalıştırdığımda Tamam düğmesini manuel olarak tıklamak istemedim.

Görünüşe göre, bunun için bir geçici çözüm bulabildim - programlama yapmadan. Chrome'un başlangıçta bir sertifika adı iletme yeteneğine sahip olduğunu ummuş olsam da, bu özellik mevcut değildi. Ancak, Windows kayıt defterinizde belirli bir giriş varsa, Chrome bir sertifikayı otomatik olarak seçme yeteneğine sahiptir. Gördüğü ilk sertifikayı seçecek veya daha spesifik olacak şekilde ayarlayabilirsiniz. Yüklü yalnızca bir sertifikam olduğundan, genel biçimi kullandım.

Veri Kazıma

Böylece bu set ile Selenium'a Chrome'u başlatmasını söylediğimde ve bir sertifika istemi geldiğinde, Chrome sertifikayı "Otomatik Seç" ve devam edecekti.

Sorun 2 – Iframe'ler

Tamam, şimdi sitedeydim ve müşteri kimliğini ve raporun tarih aralığını yazmamı isteyen bir form belirdi.

Veri Kazıma

Geliştirici araçlarında (F12) formu incelerken, formun bir iframe içinde sunulduğunu fark ettim. Bu nedenle, formu doldurmaya başlamadan önce, formun bulunduğu uygun iframe'e "geçiş" yapmam gerekiyordu. Bunu yapmak için Selenium'un geçiş özelliğini şu şekilde çağırdım:

 # Switch to iframe where form is frame_ref = driver.find_elements_by_tag_name("iframe")[0] iframe = driver.switch_to.frame(frame_ref)

Güzel, şimdi doğru çerçevede bileşenleri belirleyebildim, müşteri kimliği alanını doldurabildim ve tarih açılır listelerini seçebildim:

 # Find the Customer ID field and populate it element = driver.find_element_by_name("custId") element.send_keys(custId) # send a test id # Find and select the date drop-downs select = Select(driver.find_element_by_name("fromMonth")) select.select_by_visible_text(from_month) select = Select(driver.find_element_by_name("fromYear")) select.select_by_visible_text(from_year) select = Select(driver.find_element_by_name("toMonth")) select.select_by_visible_text(to_month) select = Select(driver.find_element_by_name("toYear")) select.select_by_visible_text(to_year)

Sorun 3 – JavaScript

Formda kalan tek şey, aramayı başlatmak için Bul düğmesini "tıklamak"tı. Bul düğmesi JavaScript tarafından kontrol ediliyor gibi göründüğü ve normal bir “Gönder” türü düğme olmadığı için bu biraz zordu. Geliştirici araçlarında inceleyerek düğme görüntüsünü buldum ve sağ tıklayarak XPath'ini elde edebildim.

Veri Kazıma

Daha sonra, bu bilgilerle donanmış olarak sayfada öğeyi buldum ve tıkladım.

 # Find the 'Find' button, then click it driver.find_element_by_xpath("/html/body/table/tbody/tr[2]/td[1]/table[3]/tbody/tr[2]/td[2]/input").click()

Ve işte, form gönderildi ve veriler ortaya çıktı! Şimdi, sonuç sayfasındaki tüm verileri sıyırıp gerektiği gibi kaydedebilirim. Ya da yapabilir miyim?

Verileri Alma

İlk olarak, aramanın hiçbir şey bulamadığı davayı halletmem gerekiyordu. Bu oldukça basitti. Arama formunda ayrılmadan "Kayıt bulunamadı" gibi bir mesaj görüntüler. Sadece o diziyi aradım ve bulursam orada durdum.

Ancak sonuçlar gelirse, bir işlemi açmak ve tüm ayrıntılarını göstermek için veriler div'lerde artı işareti (+) ile sunuldu. Açılan bir işlem, tıklandığında div'i kapatacak bir eksi işareti (-) gösteriyordu. Bir artı işaretine tıklamak, div'ini açmak ve açık olanı kapatmak için bir URL'yi çağırır.

Veri Kazıma

Bu nedenle, sayfada herhangi bir artı işareti bulmak, her birinin yanındaki URL'yi toplamak, ardından her işlem için tüm verileri almak için her biri arasında döngü yapmak gerekliydi.

 # Loop through transactions and count links = driver.find_elements_by_tag_name('a') link_urls = [link.get_attribute('href') for link in links] thisCount = 0 isFirst = 1 for url in link_urls: if (url.find("GetXas.do?processId") >= 0): # URL to link to transactions if isFirst == 1: # already expanded + isFirst = 0 else: driver.get(url) # collapsed +, so expand # Find closest element to URL element with correct class to get tran type tran_type=driver.find_element_by_xpath("//*[contains(@href,'/retail/transaction/results/GetXas.do?processId=-1')]/following::td[@class='txt_75b_lmnw_T1R10B1']").text # Get transaction status status = driver.find_element_by_class_name('txt_70b_lmnw_t1r10b1').text # Add to count if transaction found if (tran_type in ['Move In','Move Out','Switch']) and (status == "Complete"): thisCount += 1

Yukarıdaki kodda, aldığım alanlar işlem türü ve durumuydu, ardından belirtilen kurallara kaç işlemin uyduğunu belirlemek için bir sayıma eklendi. Ancak, tarih ve saat, alt tür vb. gibi işlem ayrıntılarındaki diğer alanları alabilirdim.

Bu proje için sayı, çağıran bir uygulamaya geri döndürüldü. Ancak, o ve diğer kazınmış veriler düz bir dosyada veya bir veritabanında da saklanabilirdi.

Ek Olası Engeller ve Çözümler

Modern web sitelerini kendi tarayıcı örneğinizle kazırken çok sayıda başka engel ortaya çıkabilir, ancak çoğu çözülebilir. Burda biraz var:

  • Bir şeyi ortaya çıkmadan önce bulmaya çalışmak

    Kendinize göz atarken, ne sıklıkla, bazen birkaç saniye boyunca bir sayfanın gelmesini beklediğinizi fark ediyorsunuz? Aynı şey programlı olarak gezinirken de olabilir. Bir sınıf veya başka bir öğe arıyorsunuz - ve orada değil!

    Neyse ki, Selenium belirli bir öğeyi görene kadar bekleme yeteneğine sahiptir ve öğe görünmezse zaman aşımına uğrayabilir, örneğin:

 element = WebDriverWait(driver, 10). until(EC.presence_of_element_located((By.ID, "theFirstLabel")))


  • Captcha'dan geçmek

    Bazı siteler, istenmeyen robotları (sizi düşünebilecekleri) önlemek için Captcha veya benzerini kullanır. Bu, ağ kazıma işlemine bir engel oluşturabilir ve onu yavaşlatabilir.

Basit istemler için ("2 + 3 nedir?" gibi), bunlar genellikle kolayca okunabilir ve anlaşılabilir. Ancak, daha gelişmiş engeller için, onu kırmaya çalışmanıza yardımcı olabilecek kitaplıklar vardır. Bazı örnekler 2Captcha, Death by Captcha ve Bypass Captcha'dır.

  • Web sitesi yapısal değişiklikleri

    Web siteleri değişmek içindir - ve sıklıkla yaparlar. Bu nedenle, bir kazıma komut dosyası yazarken bunu akılda tutmak en iyisidir. Verileri bulmak için hangi yöntemleri kullanacağınızı ve hangilerini kullanmayacağınızı düşünmek isteyeceksiniz. Tüm bir ifadeyi eşleştirmeye çalışmak yerine kısmi eşleştirme tekniklerini düşünün. Örneğin, bir web sitesi "Kayıt bulunamadı" mesajını "Kayıt bulunamadı" olarak değiştirebilir - ancak eşleşmeniz "Kayıt yok" ise sorun olmaz. Ayrıca, XPATH, kimlik, ad, bağlantı metni, etiket veya sınıf adı veya CSS seçicide eşleşme olup olmayacağını ve hangisinin değişme olasılığının en düşük olduğunu düşünün.

Özet: Python ve Selenyum

Bu, hangi teknolojiler kullanılırsa kullanılsın ve hangi karmaşıklıklar söz konusu olursa olsun, hemen hemen her web sitesinin kazınabileceğini gösteren kısa bir gösteriydi. Temel olarak, siteye kendiniz göz atabilirseniz, genellikle kazınabilir.

Şimdi, bir uyarı olarak, her web sitesinin kazınması gerektiği anlamına gelmez. Bazılarının yasal kısıtlamaları vardır ve belirli siteleri kazımanın yasallığına karar veren çok sayıda mahkeme davası olmuştur. Öte yandan, bazı siteler web sitelerinden veri alınmasını memnuniyetle karşılar ve teşvik eder ve bazı durumlarda işleri kolaylaştırmak için bir API sağlar.

Her iki durumda da, herhangi bir projeye başlamadan önce şartlar ve koşulları kontrol etmek en iyisidir. Ancak devam ederseniz, işi halledebileceğinizden emin olun.

Karmaşık Web Kazıma için Önerilen Kaynaklar:

  • Gelişmiş Python Web Kazıma: En İyi Uygulamalar ve Geçici Çözümler
  • Ölçeklenebilir kendin yap kazıma: Büyük ölçekte kazıyıcılar nasıl oluşturulur ve çalıştırılır