Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong

Pengarang artikel, terjemahan yang kami terbitkan hari ini, mengatakan bahawa matlamatnya adalah untuk bercakap tentang pembangunan pengikis web dalam Python menggunakan Selenium, yang mencari harga tiket penerbangan. Semasa mencari tiket, tarikh fleksibel digunakan (+- 3 hari berbanding tarikh yang ditentukan). Pengikis menyimpan hasil carian dalam fail Excel dan menghantar e-mel kepada orang yang menjalankannya dengan ringkasan perkara yang mereka temui. Matlamat projek ini adalah untuk membantu pelancong mencari tawaran terbaik.

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong

Jika, semasa memahami bahan, anda berasa hilang, lihat ini artikel.

Apa yang kita akan cari?

Anda bebas menggunakan sistem yang diterangkan di sini mengikut kehendak anda. Sebagai contoh, saya menggunakannya untuk mencari lawatan hujung minggu dan tiket ke kampung halaman saya. Jika anda serius untuk mencari tiket yang menguntungkan, anda boleh menjalankan skrip pada pelayan (simple pelayan, untuk 130 rubel sebulan, agak sesuai untuk ini) dan pastikan ia berjalan sekali atau dua kali sehari. Hasil carian akan dihantar kepada anda melalui e-mel. Di samping itu, saya mengesyorkan menetapkan segala-galanya supaya skrip menyimpan fail Excel dengan hasil carian dalam folder Dropbox, yang akan membolehkan anda melihat fail tersebut dari mana-mana dan pada bila-bila masa.

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong
Saya belum menemui tarif dengan ralat lagi, tetapi saya fikir ia mungkin

Apabila mencari, seperti yang telah disebutkan, "tarikh fleksibel" digunakan; skrip mencari tawaran dalam tempoh tiga hari dari tarikh yang diberikan. Walaupun semasa menjalankan skrip, ia mencari tawaran dalam satu arah sahaja, ia mudah untuk mengubah suai supaya ia boleh mengumpul data pada beberapa arah penerbangan. Dengan bantuannya, anda juga boleh mencari tarif yang salah; penemuan sedemikian boleh menjadi sangat menarik.

Mengapa anda memerlukan pengikis web lain?

Apabila saya mula-mula mula mengikis web, sejujurnya saya tidak begitu berminat dengannya. Saya ingin melakukan lebih banyak projek dalam bidang pemodelan ramalan, analisis kewangan, dan, mungkin, dalam bidang menganalisis pewarnaan emosi teks. Tetapi ternyata sangat menarik untuk mengetahui cara membuat program yang mengumpul data dari laman web. Semasa saya mendalami topik ini, saya menyedari bahawa pengikisan web ialah "enjin" Internet.

Anda mungkin berfikir bahawa ini adalah kenyataan yang terlalu berani. Tetapi pertimbangkan bahawa Google bermula dengan pengikis web yang dibuat oleh Larry Page menggunakan Java dan Python. Robot Google telah meneroka Internet, cuba memberikan penggunanya jawapan terbaik kepada soalan mereka. Pengikisan web mempunyai kegunaan yang tidak berkesudahan, dan walaupun anda berminat dengan sesuatu yang lain dalam Sains Data, anda memerlukan beberapa kemahiran mengikis untuk mendapatkan data yang anda perlukan untuk dianalisis.

Saya dapati beberapa teknik yang digunakan di sini dengan indah buku itu tentang pengikisan web, yang saya perolehi baru-baru ini. Ia mengandungi banyak contoh dan idea mudah untuk aplikasi praktikal apa yang telah anda pelajari. Di samping itu, terdapat bab yang sangat menarik untuk memintas semakan reCaptcha. Ini datang sebagai berita kepada saya, kerana saya tidak tahu bahawa terdapat alat khas dan juga keseluruhan perkhidmatan untuk menyelesaikan masalah sedemikian.

Adakah awak suka melancong?!

Kepada soalan mudah dan agak tidak berbahaya yang dikemukakan dalam tajuk bahagian ini, anda sering boleh mendengar jawapan yang positif, disertai dengan beberapa cerita dari perjalanan orang yang kepadanya soalan itu ditanya. Kebanyakan kita bersetuju bahawa melancong ialah cara terbaik untuk melibatkan diri dalam persekitaran budaya baharu dan meluaskan pandangan anda. Walau bagaimanapun, jika anda bertanya kepada seseorang sama ada mereka suka mencari tiket penerbangan, saya pasti jawapannya tidak begitu positif. Sebenarnya, di sinilah Python membantu kami.

Tugas pertama yang perlu kami selesaikan semasa membuat sistem untuk mencari maklumat mengenai tiket penerbangan ialah memilih platform yang sesuai dari mana kami akan mengambil maklumat. Menyelesaikan masalah ini bukanlah mudah bagi saya, tetapi akhirnya saya memilih perkhidmatan Kayak. Saya mencuba perkhidmatan Momondo, Skyscanner, Expedia dan beberapa yang lain, tetapi mekanisme perlindungan robot pada sumber ini tidak dapat ditembusi. Selepas beberapa percubaan, di mana saya terpaksa berurusan dengan lampu isyarat, lintasan pejalan kaki dan basikal, cuba meyakinkan sistem bahawa saya adalah manusia, saya memutuskan bahawa Kayak paling sesuai untuk saya, walaupun pada hakikatnya walaupun Terlalu banyak halaman dimuatkan dalam masa yang singkat, dan semakan juga bermula. Saya berjaya membuat bot menghantar permintaan ke tapak pada selang 4 hingga 6 jam, dan semuanya berfungsi dengan baik. Dari semasa ke semasa, kesukaran timbul apabila bekerja dengan Kayak, tetapi jika mereka mula mengganggu anda dengan cek, maka anda perlu sama ada menanganinya secara manual dan kemudian melancarkan bot, atau tunggu beberapa jam dan pemeriksaan akan berhenti. Jika perlu, anda boleh dengan mudah menyesuaikan kod untuk platform lain, dan jika anda berbuat demikian, anda boleh melaporkannya dalam ulasan.

Jika anda baru bermula dengan mengikis web dan tidak tahu mengapa sesetengah tapak web bergelut dengannya, maka sebelum anda memulakan projek pertama anda dalam bidang ini, bantu diri anda sendiri dan lakukan carian Google pada perkataan "etika mengikis web" . Percubaan anda mungkin berakhir lebih awal daripada yang anda fikirkan jika anda melakukan pengikisan web secara tidak bijak.

Bermula

Berikut ialah gambaran umum tentang perkara yang akan berlaku dalam kod pengikis web kami:

  • Import perpustakaan yang diperlukan.
  • Membuka tab Google Chrome.
  • Panggil fungsi yang memulakan bot, memberikannya bandar dan tarikh yang akan digunakan semasa mencari tiket.
  • Fungsi ini mengambil hasil carian pertama, diisih mengikut yang terbaik, dan mengklik butang untuk memuatkan lebih banyak hasil.
  • Fungsi lain mengumpul data dari keseluruhan halaman dan mengembalikan bingkai data.
  • Dua langkah sebelumnya dilakukan menggunakan jenis pengisihan mengikut harga tiket (murah) dan mengikut kelajuan penerbangan (terpantas).
  • E-mel yang mengandungi ringkasan harga tiket (tiket termurah dan harga purata) dihantar kepada pengguna skrip dan bingkai data dengan maklumat yang diisih mengikut tiga metrik yang disebutkan di atas disimpan sebagai fail Excel.
  • Semua tindakan di atas dilakukan dalam kitaran selepas tempoh masa tertentu.

Perlu diingatkan bahawa setiap projek Selenium bermula dengan pemacu web. saya guna Pemacu Chrome, saya bekerja dengan Google Chrome, tetapi terdapat pilihan lain. PhantomJS dan Firefox juga popular. Selepas memuat turun pemacu, anda perlu meletakkannya dalam folder yang sesuai, dan ini melengkapkan penyediaan untuk penggunaannya. Baris pertama skrip kami membuka tab Chrome baharu.

Perlu diingat bahawa dalam cerita saya, saya tidak cuba membuka ruang baharu untuk mencari tawaran hebat pada tiket penerbangan. Terdapat kaedah yang lebih maju untuk mencari tawaran sedemikian. Saya hanya ingin menawarkan pembaca bahan ini cara yang mudah tetapi praktikal untuk menyelesaikan masalah ini.

Inilah kod yang kita bincangkan di atas.

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)

Pada permulaan kod anda boleh melihat arahan import pakej yang digunakan sepanjang projek kami. Jadi, randint digunakan untuk membuat bot "tertidur" selama beberapa saat rawak sebelum memulakan operasi carian baharu. Biasanya, tiada satu bot pun boleh melakukannya tanpa ini. Jika anda menjalankan kod di atas, tetingkap Chrome akan dibuka, yang akan digunakan bot untuk berfungsi dengan tapak.

Mari buat sedikit percubaan dan buka laman web kayak.com dalam tetingkap yang berasingan. Kami akan memilih bandar dari mana kami akan terbang, dan bandar yang kami ingin pergi, serta tarikh penerbangan. Semasa memilih tarikh, pastikan julat +-3 hari digunakan. Saya menulis kod dengan mengambil kira apa yang dihasilkan oleh tapak sebagai tindak balas kepada permintaan sedemikian. Jika, sebagai contoh, anda perlu mencari tiket hanya untuk tarikh yang ditentukan, maka terdapat kebarangkalian tinggi bahawa anda perlu mengubah suai kod bot. Apabila saya bercakap tentang kod, saya memberikan penjelasan yang sesuai, tetapi jika anda berasa keliru, beritahu saya.

Sekarang klik pada butang carian dan lihat pautan dalam bar alamat. Ia sepatutnya serupa dengan pautan yang saya gunakan dalam contoh di bawah di mana pembolehubah diisytiharkan kayak, yang menyimpan URL, dan kaedah digunakan get pemacu web. Selepas mengklik butang carian, keputusan akan muncul pada halaman.

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong
Apabila saya menggunakan arahan get lebih daripada dua atau tiga kali dalam beberapa minit, saya diminta untuk melengkapkan pengesahan menggunakan reCaptcha. Anda boleh lulus semakan ini secara manual dan meneruskan percubaan sehingga sistem memutuskan untuk menjalankan semakan baharu. Apabila saya menguji skrip, nampaknya sesi carian pertama sentiasa berjalan lancar, jadi jika anda ingin mencuba kod, anda hanya perlu menyemak secara manual secara berkala dan membiarkan kod berjalan, menggunakan selang masa yang panjang antara sesi carian. Dan, jika anda memikirkannya, seseorang itu tidak mungkin memerlukan maklumat tentang harga tiket yang diterima pada selang 10 minit antara operasi carian.

Bekerja dengan halaman menggunakan XPath

Jadi, kami membuka tetingkap dan memuatkan tapak tersebut. Untuk mendapatkan harga dan maklumat lain, kami perlu menggunakan teknologi XPath atau pemilih CSS. Saya memutuskan untuk tetap menggunakan XPath dan tidak merasakan keperluan untuk menggunakan pemilih CSS, tetapi agak mungkin untuk berfungsi dengan cara itu. Menavigasi di sekitar halaman menggunakan XPath boleh menjadi rumit, dan walaupun anda menggunakan teknik yang saya terangkan ini artikel, yang melibatkan penyalinan pengecam yang sepadan daripada kod halaman, saya menyedari bahawa ini, sebenarnya, bukan cara yang optimum untuk mengakses elemen yang diperlukan. By the way, dalam ini Buku ini memberikan penerangan yang sangat baik tentang asas bekerja dengan halaman menggunakan pemilih XPath dan CSS. Inilah rupa kaedah pemacu web yang sepadan.

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong
Jadi, mari kita teruskan kerja pada bot. Mari gunakan keupayaan program untuk memilih tiket termurah. Dalam imej berikut, kod pemilih XPath diserlahkan dengan warna merah. Untuk melihat kod, anda perlu klik kanan pada elemen halaman yang anda minati dan pilih arahan Inspect daripada menu yang muncul. Perintah ini boleh dipanggil untuk elemen halaman yang berbeza, kod yang akan dipaparkan dan diserlahkan dalam pemapar kod.

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong
Lihat kod halaman

Untuk mendapatkan pengesahan alasan saya tentang keburukan menyalin pemilih daripada kod, beri perhatian kepada ciri berikut.

Inilah yang anda dapat apabila anda menyalin kod:

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

Untuk menyalin sesuatu seperti ini, anda perlu klik kanan pada bahagian kod yang anda minati dan pilih arahan Copy > Copy XPath daripada menu yang muncul.

Inilah yang saya gunakan untuk menentukan butang Termurah:

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

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong
Salin Perintah > Salin XPath

Ia agak jelas bahawa pilihan kedua kelihatan lebih mudah. Apabila digunakan, ia mencari elemen a yang mempunyai atribut data-code, sama dengan price. Apabila menggunakan pilihan pertama, elemen dicari id yang sama dengan wtKI-price_aTab, dan laluan XPath ke elemen kelihatan seperti /div[1]/div/div/div[1]/div/span/span. Pertanyaan XPath seperti ini ke halaman akan melakukan silap mata, tetapi sekali sahaja. Saya boleh katakan sekarang ini id akan berubah apabila halaman dimuatkan seterusnya. Urutan watak wtKI berubah secara dinamik setiap kali halaman dimuatkan, jadi kod yang menggunakannya akan menjadi sia-sia selepas muat semula halaman seterusnya. Jadi ambil sedikit masa untuk memahami XPath. Pengetahuan ini akan memberi manfaat kepada anda.

Walau bagaimanapun, perlu diingatkan bahawa menyalin pemilih XPath boleh berguna apabila bekerja dengan tapak yang agak mudah, dan jika anda selesa dengan ini, tidak ada yang salah dengannya.

Sekarang mari kita fikirkan tentang perkara yang perlu dilakukan jika anda perlu mendapatkan semua hasil carian dalam beberapa baris, di dalam senarai. Sangat ringkas. Setiap hasil berada di dalam objek dengan kelas resultWrapper. Memuatkan semua keputusan boleh dilakukan dalam gelung yang serupa dengan yang ditunjukkan di bawah.

Perlu diingatkan bahawa jika anda memahami perkara di atas, maka anda harus memahami dengan mudah kebanyakan kod yang akan kami analisis. Semasa kod ini berjalan, kami mengakses apa yang kami perlukan (sebenarnya, elemen di mana hasilnya dibalut) menggunakan beberapa jenis mekanisme penentu laluan (XPath). Ini dilakukan untuk mendapatkan teks elemen dan meletakkannya dalam objek dari mana data boleh dibaca (pertama kali digunakan flight_containers, kemudian - flights_list).

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong
Tiga baris pertama dipaparkan dan kami dapat melihat dengan jelas semua yang kami perlukan. Walau bagaimanapun, kami mempunyai cara yang lebih menarik untuk mendapatkan maklumat. Kita perlu mengambil data daripada setiap elemen secara berasingan.

Bekerja!

Cara paling mudah untuk menulis fungsi ialah memuatkan hasil tambahan, jadi di situlah kita akan bermula. Saya ingin memaksimumkan bilangan penerbangan yang program menerima maklumat mengenainya, tanpa menimbulkan syak wasangka dalam perkhidmatan yang membawa kepada pemeriksaan, jadi saya mengklik butang Muatkan lebih banyak hasil sekali setiap kali halaman dipaparkan. Dalam kod ini, anda harus memberi perhatian kepada blok try, yang saya tambah kerana kadangkala butang tidak dimuatkan dengan betul. Jika anda juga menghadapi ini, ulas panggilan ke fungsi ini dalam kod fungsi start_kayak, yang akan kita lihat di bawah.

# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
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

Sekarang, selepas analisis panjang fungsi ini (kadang-kadang saya boleh terbawa-bawa), kami bersedia untuk mengisytiharkan fungsi yang akan mengikis halaman.

Saya telah mengumpul sebahagian besar daripada apa yang diperlukan dalam fungsi berikut dipanggil page_scrape. Kadangkala data laluan yang dikembalikan digabungkan, jadi saya menggunakan kaedah mudah untuk memisahkannya. Sebagai contoh, apabila saya menggunakan pembolehubah untuk kali pertama section_a_list и section_b_list. Fungsi kami mengembalikan bingkai data flights_df, ini membolehkan kami memisahkan hasil yang diperoleh daripada kaedah pengisihan data yang berbeza dan kemudian menggabungkannya.

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

Saya cuba menamakan pembolehubah supaya kod itu dapat difahami. Ingat bahawa pembolehubah bermula dengan a tergolong dalam peringkat pertama laluan, dan b - ke yang kedua. Mari kita beralih ke fungsi seterusnya.

Mekanisme sokongan

Kami kini mempunyai fungsi yang membolehkan kami memuatkan hasil carian tambahan dan fungsi untuk memproses hasil carian tersebut. Artikel ini mungkin berakhir di sini, kerana kedua-dua fungsi ini menyediakan semua yang anda perlukan untuk mengikis halaman yang boleh anda buka sendiri. Tetapi kami belum mempertimbangkan beberapa mekanisme tambahan yang dibincangkan di atas. Sebagai contoh, ini adalah kod untuk menghantar e-mel dan beberapa perkara lain. Semua ini boleh didapati dalam fungsi start_kayak, yang akan kita pertimbangkan sekarang.

Untuk fungsi ini berfungsi, anda memerlukan maklumat tentang bandar dan tarikh. Menggunakan maklumat ini, ia membentuk pautan dalam pembolehubah kayak, yang digunakan untuk membawa anda ke halaman yang akan mengandungi hasil carian yang diisih mengikut padanan terbaiknya dengan pertanyaan. Selepas sesi mengikis pertama, kami akan bekerja dengan harga dalam jadual di bahagian atas halaman. Iaitu, kita akan mencari harga tiket minimum dan harga purata. Semua ini, bersama-sama dengan ramalan yang dikeluarkan oleh tapak, akan dihantar melalui e-mel. Pada halaman, jadual yang sepadan hendaklah berada di sudut kiri atas. Bekerja dengan jadual ini, dengan cara ini, boleh menyebabkan ralat semasa mencari menggunakan tarikh yang tepat, kerana dalam kes ini jadual tidak dipaparkan pada halaman.

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.....')

Saya menguji skrip ini menggunakan akaun Outlook (hotmail.com). Saya belum mengujinya untuk berfungsi dengan betul dengan akaun Gmail, sistem e-mel ini agak popular, tetapi terdapat banyak pilihan yang mungkin. Jika anda menggunakan akaun Hotmail, maka untuk semuanya berfungsi, anda hanya perlu memasukkan data anda ke dalam kod.

Jika anda ingin memahami apa sebenarnya yang sedang dilakukan dalam bahagian khusus kod untuk fungsi ini, anda boleh menyalinnya dan mencubanya. Bereksperimen dengan kod adalah satu-satunya cara untuk benar-benar memahaminya.

Sistem sedia

Sekarang kita telah melakukan semua yang kita bincangkan, kita boleh mencipta gelung mudah yang memanggil fungsi kita. Skrip meminta data daripada pengguna tentang bandar dan tarikh. Apabila menguji dengan permulaan semula skrip yang berterusan, anda tidak mungkin mahu memasukkan data ini secara manual setiap kali, jadi baris yang sepadan, untuk masa ujian, boleh diulas dengan menyahkomen yang di bawahnya, di mana data yang diperlukan oleh skrip dikodkan keras.

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.....')

Ini adalah rupa ujian skrip.
Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong
Ujian skrip

Keputusan

Jika anda telah berjaya sejauh ini, tahniah! Anda kini mempunyai pengikis web yang berfungsi, walaupun saya sudah dapat melihat banyak cara untuk memperbaikinya. Sebagai contoh, ia boleh disepadukan dengan Twilio supaya ia menghantar mesej teks dan bukannya e-mel. Anda boleh menggunakan VPN atau sesuatu yang lain untuk menerima hasil daripada beberapa pelayan secara serentak. Terdapat juga masalah yang timbul secara berkala dengan memeriksa pengguna tapak untuk melihat sama ada dia adalah orang, tetapi masalah ini juga boleh diselesaikan. Walau apa pun, kini anda mempunyai pangkalan yang boleh anda kembangkan jika anda mahu. Contohnya, pastikan fail Excel dihantar kepada pengguna sebagai lampiran kepada e-mel.

Python - pembantu dalam mencari tiket penerbangan murah untuk mereka yang suka melancong

Hanya pengguna berdaftar boleh mengambil bahagian dalam tinjauan. Log masuk, Sama-sama.

Adakah anda menggunakan teknologi mengikis web?

  • Ya

  • Tiada

8 pengguna mengundi. 1 pengguna berpantang.

Sumber: www.habr.com

Tambah komen