Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni

A cikk írója, amelynek fordítását ma közöljük, azt állítja, hogy a célja egy webkaparó fejlesztéséről beszélni Pythonban a Selenium segítségével, amely repülőjegy árakat keres. A jegyek keresésekor rugalmas időpontokat alkalmazunk (+-3 nap a megadott dátumokhoz képest). A lehúzó egy Excel-fájlba menti a keresési eredményeket, és a keresést végző személynek e-mailt küld a találtak összefoglalásával. A projekt célja, hogy segítse az utazókat a legjobb ajánlatok megtalálásában.

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni

Ha az anyag megértése közben elveszettnek érzi magát, vessen egy pillantást ezt cikk.

Mit keresünk?

Az itt leírt rendszert szabadon használhatja tetszés szerint. Például arra használtam, hogy hétvégi túrákat és jegyeket keressek szülővárosomba. Ha komolyan szeretne nyereséges jegyeket találni, futtathatja a szkriptet a szerveren (egyszerű szerveren, havi 130 rubelért, eléggé alkalmas erre), és ügyeljen arra, hogy naponta egyszer vagy kétszer működjön. A keresési eredményeket e-mailben küldjük el. Ezen kívül azt javaslom, hogy állíts be mindent úgy, hogy a szkript egy Dropbox mappába mentsen egy Excel fájlt a keresési eredményekkel, amivel bárhonnan és bármikor megtekintheted az ilyen fájlokat.

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni
Hibás tarifákat még nem találtam, de szerintem lehetséges

A keresés során, mint már említettük, „rugalmas dátumot” használnak, a szkript olyan ajánlatokat talál, amelyek az adott dátumtól számított három napon belül vannak. Bár a script futtatásakor csak egy irányba keresi az ajánlatokat, könnyen módosítható, hogy több repülési irányban is gyűjthessen adatokat. Segítségével akár hibás tarifákat is kereshet, az ilyen leletek nagyon érdekesek lehetnek.

Miért van szüksége egy másik webkaparóra?

Amikor először elkezdtem a webkaparást, őszintén szólva nem érdekelt különösebben. Több projektet szerettem volna megvalósítani a prediktív modellezés, a pénzügyi elemzés, esetleg a szövegek érzelmi színezésének elemzése területén. De kiderült, hogy nagyon érdekes volt kitalálni, hogyan lehet olyan programot készíteni, amely adatokat gyűjt a webhelyekről. Ahogy elmélyültem ebben a témában, rájöttem, hogy a webkaparás az internet „motorja”.

Azt gondolhatja, hogy ez túl merész kijelentés. De vegyük figyelembe, hogy a Google egy webkaparóval indult, amelyet Larry Page készített Java és Python használatával. A Google robotjai folyamatosan kutatják az internetet, és igyekeznek a felhasználók számára a legjobb választ adni kérdéseikre. A webes lekaparás végtelenül használható, és még ha valami másra is kíváncsi a Data Science területén, szüksége lesz bizonyos lekaparási készségekre az elemzéshez szükséges adatok megszerzéséhez.

Csodálatosnak találtam az itt használt technikák egy részét a könyv a webkaparásról, amit nemrég szereztem be. Sok egyszerű példát és ötletet tartalmaz a tanultak gyakorlati alkalmazásához. Ezen kívül van egy nagyon érdekes fejezet a reCaptcha ellenőrzések megkerülésével kapcsolatban. Ez újdonság volt számomra, hiszen nem is tudtam, hogy léteznek speciális eszközök, sőt teljes szolgáltatások az ilyen problémák megoldására.

Szeretsz utazni?!

A rovat címében feltett egyszerű és meglehetősen ártalmatlan kérdésre gyakran lehet pozitív választ hallani, pár sztori kíséretében annak az embernek az utazásairól, akinek feltették. A legtöbben egyetértünk abban, hogy az utazás nagyszerű módja annak, hogy elmerüljön új kulturális környezetekben, és szélesítse látókörét. Ha azonban megkérdezi valakit, hogy szeret-e repülőjegyeket keresni, biztos vagyok benne, hogy a válasz nem lesz ennyire pozitív. Ami azt illeti, a Python itt a segítségünkre.

Az első feladat, amelyet meg kell oldanunk a repülőjegyekkel kapcsolatos információkereső rendszer létrehozása felé vezető úton, egy megfelelő platform kiválasztása lesz, ahonnan információkat veszünk. A probléma megoldása nem volt könnyű számomra, de végül a Kayak szolgáltatást választottam. Kipróbáltam a Momondo, a Skyscanner, az Expedia és még néhány másik szolgáltatását, de ezeken az erőforrásokon a robotvédelmi mechanizmusok áthatolhatatlanok voltak. Többszöri próbálkozás után, amelyek során közlekedési lámpákkal, gyalogátkelőhelyekkel és kerékpárokkal kellett megküzdenem, és megpróbáltam meggyőzni a rendszereket, hogy ember vagyok, úgy döntöttem, hogy a Kayak a legmegfelelőbb számomra, annak ellenére, hogy még ha túl sok oldal van is betöltve. rövid időn belül, és megkezdődnek az ellenőrzések is. Sikerült elérni, hogy a bot 4-6 órás időközönként kéréseket küldjön az oldalra, és minden rendben működött. Időről időre nehézségek merülnek fel a Kayak-kal való munka során, de ha elkezdenek zaklatni a csekkekkel, akkor vagy manuálisan kell kezelni őket, majd elindítani a botot, vagy várni kell néhány órát, és az ellenőrzések leállnak. Ha szükséges, könnyen adaptálhatod a kódot egy másik platformra, és ha így teszel, kommentben jelezheted is.

Ha még csak most kezdi használni a webkaparást, és nem tudja, miért küzdenek ezzel bizonyos webhelyek, akkor mielőtt elkezdené első projektjét ezen a területen, tegyen magának egy szívességet, és keressen a Google-ban a „web scraping etikett” kifejezésre. . Kísérletei hamarabb véget érhetnek, mint gondolná, ha oktalanul végez webkaparást.

Az első lépések

Íme egy általános áttekintés arról, hogy mi fog történni webkaparó kódunkban:

  • Importálja a szükséges könyvtárakat.
  • A Google Chrome lap megnyitása.
  • Hívjon egy függvényt, amely elindítja a botot, átadva neki a jegyek keresésekor használt városokat és dátumokat.
  • Ez a funkció megkapja az első keresési eredményeket a legjobbak szerint rendezve, és rákattint egy gombra további találatok betöltéséhez.
  • Egy másik függvény a teljes oldalról gyűjt adatokat, és egy adatkeretet ad vissza.
  • Az előző két lépés a jegyár (olcsó) és a repülési sebesség (leggyorsabb) szerinti rendezési típusokkal történik.
  • A szkript felhasználója kap egy e-mailt, amely tartalmazza a jegyárak összefoglalóját (legolcsóbb jegyek és átlagár), valamint egy adatkeretet, amely a fent említett három mutató szerint rendezett információkat tartalmaz, Excel fájlként menti.
  • A fenti műveletek mindegyike egy ciklusban, meghatározott idő elteltével történik.

Meg kell jegyezni, hogy minden Selenium projekt web-illesztőprogrammal kezdődik. használom Chromedriver, Google Chrome-mal dolgozom, de vannak más lehetőségek is. A PhantomJS és a Firefox is népszerű. Az illesztőprogram letöltése után el kell helyezni a megfelelő mappába, és ezzel befejeződik a használat előkészítése. Szkriptünk első sorai egy új Chrome-lapot nyitnak meg.

Ne feledje, hogy történetemben nem próbálok új távlatokat nyitni a remek repülőjegy-ajánlatok keresésére. Vannak sokkal fejlettebb módszerek az ilyen ajánlatok keresésére. Csak egy egyszerű, de praktikus módszert szeretnék ajánlani ennek az anyagnak az olvasóinak a probléma megoldására.

Itt van a kód, amelyről fentebb beszéltünk.

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)

A kód elején láthatja a projektünk során használt csomagimportálási parancsokat. Így, randint arra szolgál, hogy a bot véletlen számú másodpercre „elaludjon”, mielőtt új keresési műveletet kezdene. Általában egyetlen bot sem nélkülözheti ezt. Ha futtatja a fenti kódot, megnyílik egy Chrome ablak, amelyet a bot a webhelyekkel való együttműködéshez használ.

Végezzünk egy kis kísérletet, és nyissuk meg egy külön ablakban a kayak.com weboldalt. Kiválasztjuk a várost, ahonnan repülünk, és a várost, ahová el szeretnénk jutni, valamint a repülési dátumokat. A dátumok kiválasztásakor ügyeljen arra, hogy a +-3 napos tartományt használja. A kódot annak figyelembevételével írtam, hogy az oldal mit produkál az ilyen kérésekre válaszul. Ha például csak meghatározott dátumokra kell jegyeket keresnie, akkor nagy a valószínűsége annak, hogy módosítania kell a bot kódot. Amikor a kódról beszélek, megfelelő magyarázatot adok, de ha zavartnak érzi magát, jelezze.

Most kattintson a keresés gombra, és nézze meg a hivatkozást a címsorban. Hasonlónak kell lennie ahhoz a hivatkozáshoz, amelyet az alábbi példában használok, ahol a változó deklarálva van kayak, amely az URL-t tárolja, és a módszert használja get web-illesztőprogram. A keresés gombra kattintás után a találatoknak meg kell jelenniük az oldalon.

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni
Amikor a parancsot használtam get néhány percen belül több mint két-három alkalommal megkérték, hogy fejezzem be az ellenőrzést a reCaptcha segítségével. Ezt az ellenőrzést manuálisan is átadhatja, és addig folytathatja a kísérletezést, amíg a rendszer úgy dönt, hogy új ellenőrzést futtat. Amikor teszteltem a szkriptet, úgy tűnt, hogy az első keresési munkamenet mindig zökkenőmentesen zajlott, így ha kísérletezni szeretne a kóddal, csak időnként manuálisan kell ellenőriznie és futnia kell a kódot, hosszú időközt használva a keresési munkamenetek között. És ha belegondol, nem valószínű, hogy egy személynek szüksége lesz információra a keresési műveletek közötti 10 perces időközönként kapott jegyárakról.

Munka oldallal XPath használatával

Tehát kinyitottunk egy ablakot, és betöltöttük az oldalt. Az árak és egyéb információk megszerzéséhez XPath technológiát vagy CSS-szelektorokat kell használnunk. Úgy döntöttem, hogy maradok az XPath mellett, és nem éreztem szükségét a CSS-szelektorok használatának, de ez teljesen lehetséges. Az XPath használatával egy oldalon való navigálás bonyolult lehet, még akkor is, ha az általam leírt technikákat használja ezt cikket, amely a megfelelő azonosítók oldalkódból való másolásával járt, rájöttem, hogy valójában nem ez az optimális módja a szükséges elemek elérésének. Mellesleg be ezt A könyv kiválóan leírja az XPath és CSS szelektorok használatával végzett lapokkal való munka alapjait. Így néz ki a megfelelő web-illesztőprogram-módszer.

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni
Tehát folytassuk a munkát a bottal. Használjuk ki a program lehetőségeit a legolcsóbb jegyek kiválasztásához. A következő képen az XPath választó kód piros színnel van kiemelve. A kód megtekintéséhez jobb egérgombbal kell kattintani az Önt érdeklő oldalelemre, és a megjelenő menüből kiválasztani az Inspect parancsot. Ez a parancs különböző oldalelemekhez hívható, amelyek kódja megjelenik és kiemelve jelenik meg a kódnézőben.

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni
Oldalkód megtekintése

Annak érdekében, hogy megerősítsem a szelektorok kódból való másolásának hátrányaival kapcsolatos érvelésemet, ügyeljen a következő jellemzőkre.

A kód másolásakor ezt kapod:

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

Ahhoz, hogy ilyesmit másolhass, jobb egérgombbal kell kattintanod az Önt érdeklő kódrészletre, és a megjelenő menüből kiválasztani a Másolás > Másolás XPath parancsot.

A legolcsóbb gomb meghatározásához a következőket használtam:

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

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni
Parancs másolása > XPath másolása

Nyilvánvaló, hogy a második lehetőség sokkal egyszerűbbnek tűnik. Használatakor egy olyan elemet keres, amely rendelkezik az attribútummal data-codeegyenlő price. Az első opció használatakor a rendszer az elemet keresi id ami egyenlő azzal wtKI-price_aTab, és az elem XPath elérési útja így néz ki /div[1]/div/div/div[1]/div/span/span. Egy oldalhoz hasonló XPath-lekérdezés megteszi a trükköt, de csak egyszer. Ezt már most mondhatom id az oldal következő betöltésekor megváltozik. Karaktersorozat wtKI dinamikusan változik az oldal minden betöltésekor, így az azt használó kód használhatatlan lesz a következő oldal újratöltés után. Tehát szánjon egy kis időt az XPath megértésére. Ez a tudás jól fog szolgálni.

Megjegyzendő azonban, hogy az XPath szelektorok másolása hasznos lehet, ha meglehetősen egyszerű oldalakkal dolgozunk, és ha ez kényelmes, nincs vele semmi baj.

Most gondoljuk át, mit tegyünk, ha az összes keresési eredményt több sorban, egy listán belül kell megjeleníteni. Nagyon egyszerű. Minden eredmény egy osztályú objektumon belül található resultWrapper. Az összes eredmény betöltése az alábbihoz hasonló ciklusban történhet.

Meg kell jegyezni, hogy ha megérti a fentieket, akkor könnyen meg kell értenie az elemezni kívánt kód nagy részét. A kód futása közben valamilyen útvonal-meghatározó mechanizmus (XPath) segítségével hozzáférünk ahhoz, amire szükségünk van (valójában az eredményt tartalmazó elemhez). Ez azért történik, hogy megkapjuk az elem szövegét és elhelyezzük egy objektumban, ahonnan adatok olvashatók (első használat flight_containers, azután - flights_list).

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni
Megjelenik az első három sor, és tisztán láthatunk mindent, amire szükségünk van. Vannak azonban érdekesebb módjai is az információszerzésnek. Minden egyes elemről külön kell adatokat venni.

Munkára!

A függvény írásának legegyszerűbb módja, ha további eredményeket töltünk be, tehát ezzel kezdjük. Szeretném maximalizálni azon járatok számát, amelyekről a program információt kap, anélkül, hogy gyanút keltene az ellenőrzéshez vezető szolgáltatásban, ezért az oldal minden megjelenésekor egyszer kattintok a További találatok betöltése gombra. Ebben a kódban figyelni kell a blokkra try, amit azért tettem hozzá, mert néha nem tölt be rendesen a gomb. Ha Ön is találkozik ezzel, írja be a függvény hívását a függvénykódban start_kayak, amelyet alább megnézünk.

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

Most, ennek a függvénynek a hosszas elemzése után (néha elkaphatom magam), készen állunk egy olyan függvény deklarálására, amely lekaparja az oldalt.

A szükségesek nagy részét már összegyűjtöttem a következő ún page_scrape. Néha a visszaadott útvonaladatokat egyesítik, ezért egy egyszerű módszert használok a szétválasztásukra. Például amikor először használok változókat section_a_list и section_b_list. A függvényünk egy adatkeretet ad vissza flights_df, ez lehetővé teszi, hogy a különböző adatrendezési módszerekből kapott eredményeket elkülönítsük, majd később kombináljuk.

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

Igyekeztem úgy elnevezni a változókat, hogy a kód érthető legyen. Ne feledje, hogy a változókkal kezdődik a az út első szakaszához tartoznak, és b - a másodikra. Térjünk át a következő függvényre.

Támogató mechanizmusok

Mostantól van egy funkciónk, amely lehetővé teszi további keresési eredmények betöltését, valamint egy funkció az eredmények feldolgozására. Ez a cikk itt véget is érhetett volna, hiszen ez a két funkció mindent megad, ami a saját maga által megnyitható oldalak kaparásához szükséges. De még nem vettünk figyelembe néhány fent tárgyalt segédmechanizmust. Ez például az e-mailek küldésének és néhány egyéb dolognak a kódja. Mindez megtalálható a függvényben start_kayak, amelyet most megvizsgálunk.

Ez a funkció városokkal és dátumokkal kapcsolatos információkat igényel. Ezt az információt felhasználva hivatkozást képez egy változóban kayak, amely egy olyan oldalra viszi Önt, amely a keresési eredményeket a lekérdezésnek leginkább megfelelő sorrend szerint tartalmazza. Az első kaparási munkamenet után az oldal tetején lévő táblázatban szereplő árakkal dolgozunk. Ugyanis megtaláljuk a minimális jegyárat és az átlagárat. Mindezt az oldal által kiadott jóslattal együtt e-mailben küldjük el. Az oldalon a megfelelő táblázatnak a bal felső sarokban kell lennie. Ennek a táblázatnak a használata egyébként hibát okozhat a pontos dátumokkal történő kereséskor, mivel ebben az esetben a táblázat nem jelenik meg az oldalon.

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

Ezt a szkriptet egy Outlook-fiókkal (hotmail.com) teszteltem. Nem teszteltem, hogy megfelelően működik-e Gmail fiókkal, ez az e-mail rendszer meglehetősen népszerű, de sok lehetőség van. Ha Hotmail fiókot használ, akkor ahhoz, hogy minden működjön, csak be kell írnia adatait a kódba.

Ha meg akarja érteni, hogy pontosan mi történik a kód egyes szakaszaiban ehhez a funkcióhoz, átmásolhatja őket, és kísérletezhet velük. A kóddal való kísérletezés az egyetlen módja annak, hogy valóban megértsük.

Kész rendszer

Most, hogy mindent megtettünk, amiről beszéltünk, létrehozhatunk egy egyszerű hurkot, amely meghívja a függvényeinket. A szkript adatokat kér a felhasználótól városokról és dátumokról. A szkript folyamatos újraindításával végzett tesztelésnél nem valószínű, hogy minden alkalommal manuálisan akarja megadni ezeket az adatokat, így a tesztelés idejére vonatkozó megfelelő sorok az alattuk lévők megjegyzésének törlésével kommentálhatók, amelyekben a program által igényelt adatok. a szkript kemény kódolású.

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

Így néz ki a szkript tesztfutása.
Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni
A szkript teszt futtatása

Eredményei

Ha idáig eljutott, gratulálunk! Most már van egy működő webkaparója, bár már most is sok módot látok a javítására. Például integrálható a Twilio-val, így e-mailek helyett szöveges üzeneteket küld. Használhat VPN-t vagy valami mást, hogy egyszerre több szerverről is kapjon eredményeket. Időnként felmerül az is, hogy az oldal felhasználóját ellenőrizzük, hogy személy-e, de ez a probléma is megoldható. Mindenesetre most már van egy bázisa, amelyet ha akar, bővíthet. Például győződjön meg arról, hogy egy Excel-fájlt küld a felhasználónak egy e-mail mellékleteként.

Python - asszisztens olcsó repülőjegyek megtalálásában azok számára, akik szeretnek utazni

A felmérésben csak regisztrált felhasználók vehetnek részt. Bejelentkezés, kérem.

Használ webkaparási technológiákat?

  • Igen

  • Nincs

8 felhasználó szavazott. 1 felhasználó tartózkodott.

Forrás: will.com

Hozzászólás