Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı

Bugün çevirisini yayınladığımız makalenin yazarı, amacının Python'da Selenium kullanarak uçak bileti fiyatlarını araştıran bir web kazıyıcının geliştirilmesinden bahsetmek olduğunu söylüyor. Bilet ararken esnek tarihler kullanılır (belirtilen tarihlere göre +- 3 gün). Kazıyıcı, arama sonuçlarını bir Excel dosyasına kaydeder ve aramayı yapan kişiye bulduklarının özetini içeren bir e-posta gönderir. Bu projenin amacı gezginlerin en iyi fırsatları bulmasına yardımcı olmaktır.

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı

Materyali anlarken kendinizi kaybolmuş hissediyorsanız, bir göz atın bu makale.

Ne arıyoruz?

Burada anlatılan sistemi dilediğiniz gibi kullanmakta özgürsünüz. Örneğin memleketime hafta sonu turları ve biletleri aramak için kullandım. Eğer karlı biletler bulma konusunda ciddiyseniz, betiği sunucuda çalıştırabilirsiniz (basit sunucuAyda 130 ruble bunun için oldukça uygundur) ve günde bir veya iki kez çalıştığından emin olun. Arama sonuçları size e-postayla gönderilecektir. Ek olarak, her şeyi, komut dosyasının arama sonuçlarını içeren bir Excel dosyasını bir Dropbox klasörüne kaydedeceği şekilde ayarlamanızı öneririm; bu, bu tür dosyaları her yerden ve her zaman görüntülemenize olanak tanır.

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı
Henüz hatalı tarifeler bulamadım ama mümkün olduğunu düşünüyorum

Arama yaparken, daha önce de belirtildiği gibi, "esnek tarih" kullanılır; komut dosyası, verilen tarihlerden sonraki üç gün içinde olan teklifleri bulur. Komut dosyasını çalıştırırken yalnızca tek yöndeki teklifleri arasa da, birkaç uçuş yönünde veri toplayabilecek şekilde değiştirmek kolaydır. Onun yardımıyla hatalı tarifeleri bile arayabilirsiniz; bu tür bulgular çok ilginç olabilir.

Neden başka bir web kazıyıcıya ihtiyacınız var?

Web kazımaya ilk başladığımda, açıkçası bununla pek ilgilenmiyordum. Tahmine dayalı modelleme, finansal analiz ve muhtemelen metinlerin duygusal renklendirmesini analiz etme alanında daha fazla proje yapmak istedim. Ancak web sitelerinden veri toplayan bir programın nasıl oluşturulacağını bulmanın çok ilginç olduğu ortaya çıktı. Bu konuyu araştırdıkça web kazımanın İnternet'in "motoru" olduğunu fark ettim.

Bunun çok cesur bir ifade olduğunu düşünebilirsiniz. Ancak Google'ın Larry Page'in Java ve Python kullanarak oluşturduğu bir web kazıyıcıyla başladığını düşünün. Google robotları interneti araştırıyor, kullanıcılarına sorularına en iyi cevapları sunmaya çalışıyor. Web kazımanın sonsuz kullanım alanları vardır ve Veri Biliminde başka bir şeyle ilgileniyor olsanız bile, analiz etmeniz gereken verileri elde etmek için bazı kazıma becerilerine ihtiyacınız olacaktır.

Burada kullanılan tekniklerden bazılarını harika bir şekilde buldum. kitap yakın zamanda edindiğim web kazıma hakkında. Öğrendiklerinizi pratikte uygulamak için birçok basit örnek ve fikir içerir. Ayrıca reCaptcha kontrollerinin atlanmasıyla ilgili çok ilginç bir bölüm var. Bu bana haber gibi geldi çünkü bu tür sorunları çözmek için özel araçların ve hatta tüm hizmetlerin olduğunu bile bilmiyordum.

Seyahat etmeyi sever misin?!

Bu bölümün başlığında sorulan basit ve oldukça zararsız soruya, sorulduğu kişinin seyahatlerinden birkaç hikaye eşliğinde genellikle olumlu bir yanıt duyabilirsiniz. Çoğumuz seyahat etmenin, kendinizi yeni kültürel ortamlara kaptırmanın ve ufkunuzu genişletmenin harika bir yolu olduğu konusunda hemfikiriz. Ancak birine uçak bileti aramayı sevip sevmediğini sorarsanız eminim ki cevabı pek olumlu olmayacaktır. Aslına bakılırsa Python burada imdadımıza yetişiyor.

Uçak biletlerine ilişkin bilgi arama sistemi oluşturma yolunda çözmemiz gereken ilk görev, bilgi alacağımız uygun platformu seçmek olacaktır. Bu sorunu çözmek benim için kolay olmadı ama sonunda Kayak hizmetini seçtim. Momondo, Skyscanner, Expedia ve diğer birkaç firmanın hizmetlerini denedim ancak bu kaynaklardaki robot koruma mekanizmaları aşılmazdı. Sistemleri insan olduğuma ikna etmeye çalışarak trafik ışıkları, yaya geçitleri ve bisikletlerle uğraşmak zorunda kaldığım birkaç denemeden sonra, çok fazla sayfa yüklü olmasına rağmen Kayak'ın benim için en uygun olduğuna karar verdim. Kısa sürede kontroller de başlayacak. Botun siteye 4 ila 6 saat aralıklarla istek göndermesini sağladım ve her şey yolunda gitti. Kayak ile çalışırken zaman zaman zorluklar ortaya çıkar, ancak sizi kontrollerle rahatsız etmeye başlarlarsa, o zaman ya bunlarla manuel olarak ilgilenmeniz ve ardından botu başlatmanız ya da birkaç saat beklemeniz ve kontrollerin durması gerekir. Gerekirse kodu başka bir platforma kolayca uyarlayabilirsiniz, eğer yaparsanız yorumlarda bildirebilirsiniz.

Web kazımaya yeni başlıyorsanız ve bazı web sitelerinin neden bununla mücadele ettiğini bilmiyorsanız, bu alandaki ilk projenize başlamadan önce kendinize bir iyilik yapın ve "web kazıma görgü kuralları" kelimelerini kullanarak bir Google araması yapın. . Web kazıma işlemlerini akıllıca yapmazsanız denemeleriniz düşündüğünüzden daha erken sona erebilir.

Başlarken

Web kazıyıcı kodumuzda neler olacağına dair genel bir bakış:

  • Gerekli kütüphaneleri içe aktarın.
  • Bir Google Chrome sekmesi açılıyor.
  • Bilet ararken kullanılacak şehirleri ve tarihleri ​​ona ileterek botu başlatan bir işlevi çağırın.
  • Bu işlev, en iyiye göre sıralanmış ilk arama sonuçlarını alır ve daha fazla sonuç yüklemek için bir düğmeye tıklar.
  • Başka bir işlev, sayfanın tamamından veri toplar ve bir veri çerçevesi döndürür.
  • Önceki iki adım, bilet fiyatına (ucuz) ve uçuş hızına (en hızlı) göre sıralama türleri kullanılarak gerçekleştirilir.
  • Komut dosyasının kullanıcısına bilet fiyatlarının özetini (en ucuz biletler ve ortalama fiyat) içeren bir e-posta gönderilir ve yukarıda belirtilen üç göstergeye göre sıralanan bilgileri içeren bir veri çerçevesi bir Excel dosyası olarak kaydedilir.
  • Yukarıdaki eylemlerin tümü belirli bir süre sonra bir döngüde gerçekleştirilir.

Her Selenium projesinin bir web sürücüsüyle başladığı unutulmamalıdır. kullanırım Chrome sürücüsü, Google Chrome ile çalışıyorum ancak başka seçenekler de var. PhantomJS ve Firefox da popülerdir. Sürücüyü indirdikten sonra uygun klasöre yerleştirmeniz gerekir ve bu, kullanım hazırlığını tamamlar. Komut dosyamızın ilk satırları yeni bir Chrome sekmesi açar.

Hikayemde uçak biletlerinde harika fırsatlar bulmak için yeni ufuklar açmaya çalışmadığımı unutmayın. Bu tür teklifleri aramanın çok daha gelişmiş yöntemleri vardır. Bu materyalin okuyucularına bu sorunu çözmenin basit ama pratik bir yolunu sunmak istiyorum.

Yukarıda bahsettiğimiz kod aşağıdadır.

from time import sleep, strftime
from random import randint
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import smtplib
from email.mime.multipart import MIMEMultipart

# Используйте тут ваш путь к chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # Этой командой открывается окно Chrome
sleep(2)

Kodun başında projemiz boyunca kullanılan package import komutlarını görebilirsiniz. Bu yüzden, randint Yeni bir arama işlemine başlamadan önce botun rastgele sayıda saniye boyunca "uykuya dalmasını" sağlamak için kullanılır. Genellikle tek bir bot bu olmadan yapamaz. Yukarıdaki kodu çalıştırırsanız, botun sitelerle çalışmak için kullanacağı bir Chrome penceresi açılacaktır.

Küçük bir deney yapalım ve kayak.com sitesini ayrı bir pencerede açalım. Uçacağımız şehri, varmak istediğimiz şehri ve uçuş tarihlerini seçeceğiz. Tarih seçerken +-3 gün aralığının kullanılmasına dikkat edin. Kodu sitenin bu tür isteklere yanıt olarak ürettiklerini dikkate alarak yazdım. Örneğin, yalnızca belirli tarihler için bilet aramanız gerekiyorsa, bot kodunu değiştirmeniz gerekme olasılığı yüksektir. Kod hakkında konuştuğumda uygun açıklamalar yapıyorum ancak kafanız karışırsa bana bildirin.

Şimdi arama butonuna tıklayın ve adres çubuğundaki bağlantıya bakın. Değişkenin bildirildiği aşağıdaki örnekte kullandığım bağlantıya benzer olmalıdır kayakURL'yi saklayan ve yöntemin kullanıldığı get ağ sürücüsü. Arama butonuna tıkladıktan sonra sonuçlar sayfada görünmelidir.

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı
Komutu kullandığımda get Birkaç dakika içinde iki veya üç defadan fazla doğrulama işlemini reCaptcha kullanarak tamamlamam istendi. Bu kontrolü manuel olarak geçebilir ve sistem yeni bir kontrol yapmaya karar verene kadar denemeye devam edebilirsiniz. Komut dosyasını test ettiğimde, ilk arama oturumu her zaman sorunsuz geçmiş gibi görünüyordu; bu nedenle, kodu denemek istiyorsanız, arama oturumları arasında uzun aralıklar kullanarak yalnızca periyodik olarak manuel olarak kontrol etmeniz ve kodun çalıştırılmasına izin vermeniz yeterliydi. Ve eğer düşünürseniz, bir kişinin arama işlemleri arasında 10 dakikalık aralıklarla alınan bilet fiyatları hakkında bilgiye ihtiyaç duyması pek olası değildir.

XPath kullanarak bir sayfayla çalışma

Böylece bir pencere açtık ve siteyi yükledik. Fiyatlandırma ve diğer bilgileri almak için XPath teknolojisini veya CSS seçicilerini kullanmamız gerekir. XPath'a bağlı kalmaya karar verdim ve CSS seçicileri kullanma ihtiyacını hissetmedim, ancak bu şekilde çalışmak oldukça mümkün. XPath kullanarak bir sayfada gezinmek zor olabilir ve yukarıda anlattığım teknikleri kullansanız bile bu İlgili tanımlayıcıların sayfa kodundan kopyalanmasını içeren makalemde bunun aslında gerekli öğelere erişmenin en uygun yolu olmadığını fark ettim. Bu arada, bu Kitap, XPath ve CSS seçicileri kullanarak sayfalarla çalışmanın temellerinin mükemmel bir tanımını sağlar. İlgili web sürücüsü yöntemi böyle görünür.

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı
O halde bot üzerinde çalışmaya devam edelim. En ucuz biletleri seçmek için programın yeteneklerini kullanalım. Aşağıdaki resimde XPath seçici kodu kırmızı renkle vurgulanmıştır. Kodu görüntülemek için ilgilendiğiniz sayfa öğesine sağ tıklayıp açılan menüden İncele komutunu seçmeniz gerekir. Bu komut, kodu kod görüntüleyicide görüntülenecek ve vurgulanacak farklı sayfa öğeleri için çağrılabilir.

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı
Sayfa kodunu görüntüle

Seçicileri koddan kopyalamanın dezavantajları hakkındaki mantığımın onayını bulmak için aşağıdaki özelliklere dikkat edin.

Kodu kopyaladığınızda şunu elde edersiniz:

//*[@id="wtKI-price_aTab"]/div[1]/div/div/div[1]/div/span/span

Böyle bir şeyi kopyalamak için kodun ilgilendiğiniz bölümüne sağ tıklayıp açılan menüden Kopyala > XPath'ı Kopyala komutunu seçmeniz gerekir.

İşte En Ucuz düğmesini tanımlamak için kullandığım şey:

cheap_results = ‘//a[@data-code = "price"]’

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı
Komutu Kopyala > XPath'ı Kopyala

İkinci seçeneğin çok daha basit göründüğü oldukça açık. Kullanıldığında, özniteliğe sahip bir a öğesini arar. data-code, eşit price. İlk seçeneği kullanırken eleman aranır id hangisi eşittir wtKI-price_aTabve öğenin XPath yolu şöyle görünüyor /div[1]/div/div/div[1]/div/span/span. Bir sayfaya bunun gibi bir XPath sorgusu işe yarayacaktır, ancak yalnızca bir kez. şu anda şunu söyleyebilirim id sayfanın bir sonraki yüklenmesinde değişecektir. Karakter dizisi wtKI sayfa her yüklendiğinde dinamik olarak değişir, dolayısıyla onu kullanan kod bir sonraki sayfa yeniden yüklendikten sonra kullanılamaz hale gelir. Bu yüzden XPath'ı anlamak için biraz zaman ayırın. Bu bilgi işinize yarayacaktır.

Ancak şunu da belirtmek gerekir ki XPath seçicilerini kopyalamak oldukça basit sitelerle çalışırken faydalı olabilir ve eğer bu konuda rahatsanız bunda bir sakınca yoktur.

Şimdi tüm arama sonuçlarını bir liste içinde birkaç satırda almanız gerekiyorsa ne yapacağınızı düşünelim. Çok basit. Her sonuç bir sınıfa sahip bir nesnenin içindedir resultWrapper. Tüm sonuçların yüklenmesi aşağıda gösterilene benzer bir döngüde yapılabilir.

Yukarıdakileri anladıysanız analiz edeceğimiz kodun çoğunu da kolayca anlamış olmanız gerektiğini belirtmekte fayda var. Bu kod çalışırken, bir tür yol belirleme mekanizması (XPath) kullanarak ihtiyacımız olan şeye (aslında sonucun sarıldığı öğeye) erişiriz. Bu, öğenin metnini almak ve onu verilerin okunabileceği bir nesneye yerleştirmek için yapılır (ilk kez kullanıldı). flight_containers, Daha sonra - flights_list).

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı
İlk üç satır görüntüleniyor ve ihtiyacımız olan her şeyi açıkça görebiliyoruz. Ancak bilgi edinmenin daha ilginç yollarına sahibiz. Her elementten ayrı ayrı veri almamız gerekiyor.

İşe başlamak!

Bir fonksiyon yazmanın en kolay yolu ek sonuçları yüklemektir, o yüzden başlayacağız yer burasıdır. Hizmette incelemeye yol açacak şüpheler yaratmadan, programın bilgi aldığı uçuş sayısını en üst düzeye çıkarmak istiyorum, bu nedenle sayfa her görüntülendiğinde Daha fazla sonuç yükle düğmesine bir kez tıklıyorum. Bu kodda bloğa dikkat etmelisiniz tryBunu ekledim çünkü bazen düğme düzgün şekilde yüklenmiyor. Bununla da karşılaşırsanız, bu işleve yapılan çağrıları işlev kodunda yorumlayın start_kayakaşağıda inceleyeceğimiz şey.

# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
def load_more():
    try:
        more_results = '//a[@class = "moreButton"]'
        driver.find_element_by_xpath(more_results).click()
        # Вывод этих заметок в ходе работы программы помогает мне быстро выяснить то, чем она занята
        print('sleeping.....')
        sleep(randint(45,60))
    except:
        pass

Artık bu fonksiyonun uzun bir analizinden sonra (bazen kendimi kaptırabiliyorum), sayfayı sıyıracak bir fonksiyon ilan etmeye hazırız.

Aşağıdaki fonksiyonda ihtiyaç duyulanların çoğunu zaten topladım. page_scrape. Bazen döndürülen yol verileri birleştirilir, bu yüzden onu ayırmak için basit bir yöntem kullanırım. Örneğin değişkenleri ilk kez kullandığımda section_a_list и section_b_list. Fonksiyonumuz bir veri çerçevesi döndürür flights_dfBu, farklı veri sıralama yöntemlerinden elde edilen sonuçları ayırmamıza ve daha sonra birleştirmemize olanak tanır.

def page_scrape():
    """This function takes care of the scraping part"""
    
    xp_sections = '//*[@class="section duration"]'
    sections = driver.find_elements_by_xpath(xp_sections)
    sections_list = [value.text for value in sections]
    section_a_list = sections_list[::2] # так мы разделяем информацию о двух полётах
    section_b_list = sections_list[1::2]
    
    # Если вы наткнулись на reCaptcha, вам может понадобиться что-то предпринять.
    # О том, что что-то пошло не так, вы узнаете исходя из того, что вышеприведённые списки пусты
    # это выражение if позволяет завершить работу программы или сделать ещё что-нибудь
    # тут можно приостановить работу, что позволит вам пройти проверку и продолжить скрапинг
    # я использую тут SystemExit так как хочу протестировать всё с самого начала
    if section_a_list == []:
        raise SystemExit
    
    # Я буду использовать букву A для уходящих рейсов и B для прибывающих
    a_duration = []
    a_section_names = []
    for n in section_a_list:
        # Получаем время
        a_section_names.append(''.join(n.split()[2:5]))
        a_duration.append(''.join(n.split()[0:2]))
    b_duration = []
    b_section_names = []
    for n in section_b_list:
        # Получаем время
        b_section_names.append(''.join(n.split()[2:5]))
        b_duration.append(''.join(n.split()[0:2]))

    xp_dates = '//div[@class="section date"]'
    dates = driver.find_elements_by_xpath(xp_dates)
    dates_list = [value.text for value in dates]
    a_date_list = dates_list[::2]
    b_date_list = dates_list[1::2]
    # Получаем день недели
    a_day = [value.split()[0] for value in a_date_list]
    a_weekday = [value.split()[1] for value in a_date_list]
    b_day = [value.split()[0] for value in b_date_list]
    b_weekday = [value.split()[1] for value in b_date_list]
    
    # Получаем цены
    xp_prices = '//a[@class="booking-link"]/span[@class="price option-text"]'
    prices = driver.find_elements_by_xpath(xp_prices)
    prices_list = [price.text.replace('$','') for price in prices if price.text != '']
    prices_list = list(map(int, prices_list))

    # stops - это большой список, в котором первый фрагмент пути находится по чётному индексу, а второй - по нечётному
    xp_stops = '//div[@class="section stops"]/div[1]'
    stops = driver.find_elements_by_xpath(xp_stops)
    stops_list = [stop.text[0].replace('n','0') for stop in stops]
    a_stop_list = stops_list[::2]
    b_stop_list = stops_list[1::2]

    xp_stops_cities = '//div[@class="section stops"]/div[2]'
    stops_cities = driver.find_elements_by_xpath(xp_stops_cities)
    stops_cities_list = [stop.text for stop in stops_cities]
    a_stop_name_list = stops_cities_list[::2]
    b_stop_name_list = stops_cities_list[1::2]
    
    # сведения о компании-перевозчике, время отправления и прибытия для обоих рейсов
    xp_schedule = '//div[@class="section times"]'
    schedules = driver.find_elements_by_xpath(xp_schedule)
    hours_list = []
    carrier_list = []
    for schedule in schedules:
        hours_list.append(schedule.text.split('n')[0])
        carrier_list.append(schedule.text.split('n')[1])
    # разделяем сведения о времени и о перевозчиках между рейсами a и b
    a_hours = hours_list[::2]
    a_carrier = carrier_list[1::2]
    b_hours = hours_list[::2]
    b_carrier = carrier_list[1::2]

    
    cols = (['Out Day', 'Out Time', 'Out Weekday', 'Out Airline', 'Out Cities', 'Out Duration', 'Out Stops', 'Out Stop Cities',
            'Return Day', 'Return Time', 'Return Weekday', 'Return Airline', 'Return Cities', 'Return Duration', 'Return Stops', 'Return Stop Cities',
            'Price'])

    flights_df = pd.DataFrame({'Out Day': a_day,
                               'Out Weekday': a_weekday,
                               'Out Duration': a_duration,
                               'Out Cities': a_section_names,
                               'Return Day': b_day,
                               'Return Weekday': b_weekday,
                               'Return Duration': b_duration,
                               'Return Cities': b_section_names,
                               'Out Stops': a_stop_list,
                               'Out Stop Cities': a_stop_name_list,
                               'Return Stops': b_stop_list,
                               'Return Stop Cities': b_stop_name_list,
                               'Out Time': a_hours,
                               'Out Airline': a_carrier,
                               'Return Time': b_hours,
                               'Return Airline': b_carrier,                           
                               'Price': prices_list})[cols]
    
    flights_df['timestamp'] = strftime("%Y%m%d-%H%M") # время сбора данных
    return flights_df

Kodun anlaşılır olması için değişkenleri isimlendirmeye çalıştım. ile başlayan değişkenleri unutmayın a yolun ilk aşamasına aittir ve b - ikinciye. Bir sonraki fonksiyona geçelim.

Destek mekanizmaları

Artık ek arama sonuçları yüklememize olanak tanıyan bir fonksiyonumuz ve bu sonuçları işleyecek bir fonksiyonumuz var. Bu iki işlev, kendi başınıza açabileceğiniz sayfaları kazımak için ihtiyacınız olan her şeyi sağladığından, bu makale burada bitebilirdi. Ancak yukarıda tartışılan yardımcı mekanizmaların bazılarını henüz dikkate almadık. Örneğin, bu e-posta ve diğer bazı şeyleri göndermek için kullanılan koddur. Bütün bunlar fonksiyonda bulunabilir start_kayakşimdi bunu ele alacağız.

Bu işlevin çalışması için şehirler ve tarihler hakkında bilgiye ihtiyacınız var. Bu bilgiyi kullanarak değişkende bir bağlantı oluşturur kayak, sizi sorguyla en iyi eşleşmelerine göre sıralanmış arama sonuçlarını içeren bir sayfaya götürmek için kullanılır. İlk kazıma seansından sonra sayfanın üst kısmındaki tabloda yer alan fiyatlar ile çalışacağız. Yani minimum bilet fiyatını ve ortalama fiyatı bulacağız. Tüm bunlar, site tarafından yayınlanan tahminle birlikte e-postayla gönderilecektir. Sayfada ilgili tablo sol üst köşede bulunmalıdır. Bu arada, bu tabloyla çalışmak, kesin tarihler kullanılarak arama yaparken hataya neden olabilir, çünkü bu durumda tablo sayfada görüntülenmez.

def start_kayak(city_from, city_to, date_start, date_end):
    """City codes - it's the IATA codes!
    Date format -  YYYY-MM-DD"""
    
    kayak = ('https://www.kayak.com/flights/' + city_from + '-' + city_to +
             '/' + date_start + '-flexible/' + date_end + '-flexible?sort=bestflight_a')
    driver.get(kayak)
    sleep(randint(8,10))
    
    # иногда появляется всплывающее окно, для проверки на это и его закрытия можно воспользоваться блоком try
    try:
        xp_popup_close = '//button[contains(@id,"dialog-close") and contains(@class,"Button-No-Standard-Style close ")]'
        driver.find_elements_by_xpath(xp_popup_close)[5].click()
    except Exception as e:
        pass
    sleep(randint(60,95))
    print('loading more.....')
    
#     load_more()
    
    print('starting first scrape.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Возьмём самую низкую цену из таблицы, расположенной в верхней части страницы
    matrix = driver.find_elements_by_xpath('//*[contains(@id,"FlexMatrixCell")]')
    matrix_prices = [price.text.replace('$','') for price in matrix]
    matrix_prices = list(map(int, matrix_prices))
    matrix_min = min(matrix_prices)
    matrix_avg = sum(matrix_prices)/len(matrix_prices)
    
    print('switching to cheapest results.....')
    cheap_results = '//a[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting second scrape.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('switching to quickest results.....')
    quick_results = '//a[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting third scrape.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Сохранение нового фрейма в Excel-файл, имя которого отражает города и даты
    final_df = df_flights_cheap.append(df_flights_best).append(df_flights_fast)
    final_df.to_excel('search_backups//{}_flights_{}-{}_from_{}_to_{}.xlsx'.format(strftime("%Y%m%d-%H%M"),
                                                                                   city_from, city_to, 
                                                                                   date_start, date_end), index=False)
    print('saved df.....')
    
    # Можно следить за тем, как прогноз, выдаваемый сайтом, соотносится с реальностью
    xp_loading = '//div[contains(@id,"advice")]'
    loading = driver.find_element_by_xpath(xp_loading).text
    xp_prediction = '//span[@class="info-text"]'
    prediction = driver.find_element_by_xpath(xp_prediction).text
    print(loading+'n'+prediction)
    
    # иногда в переменной loading оказывается эта строка, которая, позже, вызывает проблемы с отправкой письма
    # если это прозошло - меняем её на "Not Sure"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'Not sure'
    
    username = '[email protected]'
    password = 'YOUR PASSWORD'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Subject: Flight Scrapernn
Cheapest Flight: {}nAverage Price: {}nnRecommendation: {}nnEnd of message'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
    message = MIMEMultipart()
    message['From'] = '[email protected]'
    message['to'] = '[email protected]'
    server.sendmail('[email protected]', '[email protected]', msg)
    print('sent email.....')

Bu betiği bir Outlook hesabı (hotmail.com) kullanarak test ettim. Bir Gmail hesabıyla düzgün çalışıp çalışmadığını test etmedim, bu e-posta sistemi oldukça popüler, ancak birçok olası seçenek var. Bir Hotmail hesabı kullanıyorsanız, her şeyin çalışması için verilerinizi koda girmeniz yeterlidir.

Bu işlev için kodun belirli bölümlerinde tam olarak ne yapıldığını anlamak istiyorsanız bunları kopyalayıp denemeler yapabilirsiniz. Kodu gerçekten anlamanın tek yolu kodla denemeler yapmaktır.

Hazır sistem

Artık konuştuğumuz her şeyi yaptığımıza göre, fonksiyonlarımızı çağıran basit bir döngü oluşturabiliriz. Komut dosyası kullanıcıdan şehirler ve tarihler hakkında veri ister. Komut dosyasının sürekli olarak yeniden başlatılmasıyla test yaparken, bu verileri her seferinde manuel olarak girmek istemeniz olası değildir; bu nedenle, test sırasında karşılık gelen satırlar, altlarında bulunan satırların yorumlarını kaldırarak yorumlanabilir. komut dosyası sabit kodlanmıştır.

city_from = input('From which city? ')
city_to = input('Where to? ')
date_start = input('Search around which departure date? Please use YYYY-MM-DD format only ')
date_end = input('Return when? Please use YYYY-MM-DD format only ')

# city_from = 'LIS'
# city_to = 'SIN'
# date_start = '2019-08-21'
# date_end = '2019-09-07'

for n in range(0,5):
    start_kayak(city_from, city_to, date_start, date_end)
    print('iteration {} was complete @ {}'.format(n, strftime("%Y%m%d-%H%M")))
    
    # Ждём 4 часа
    sleep(60*60*4)
    print('sleep finished.....')

Komut dosyasının test çalıştırması böyle görünüyor.
Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı
Komut dosyasının test çalıştırması

sonuçlar

Eğer bu noktaya kadar geldiyseniz tebrikler! Artık çalışan bir web kazıyıcınız var, ancak bunu geliştirmenin birçok yolunu zaten görebiliyorum. Örneğin Twilio ile entegre olarak e-posta yerine kısa mesaj gönderebiliyor. Birkaç sunucudan aynı anda sonuç almak için bir VPN veya başka bir şey kullanabilirsiniz. Site kullanıcısının kişi olup olmadığının kontrol edilmesinde de periyodik olarak ortaya çıkan bir sorun var ama bu sorun da çözülebilir. Her durumda, artık isterseniz genişletebileceğiniz bir tabanınız var. Örneğin, kullanıcıya bir e-posta eki olarak bir Excel dosyasının gönderildiğinden emin olun.

Python - seyahat etmeyi sevenler için ucuz uçak bileti bulmada yardımcı

Ankete sadece kayıtlı kullanıcılar katılabilir. Giriş yapLütfen.

Web kazıma teknolojilerini kullanıyor musunuz?

  • Evet

  • Hayır

8 kullanıcı oy kullandı. 1 kişi çekimser kaldı.

Kaynak: habr.com

Yorum ekle