Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել

Հոդվածի հեղինակը, որի թարգմանությունն այսօր հրապարակում ենք, ասում է, որ դրա նպատակն է խոսել ավիաընկերության տոմսերի գները որոնող Python-ում վեբ քերիչի մշակման մասին՝ օգտագործելով Selenium-ը։ Տոմսեր փնտրելիս օգտագործվում են ճկուն ժամկետներ (+- 3 օր նշված ամսաթվերի համեմատ): Քերիչը պահպանում է որոնման արդյունքները Excel ֆայլում և այն անձին, ով իրականացրել է որոնումը, ուղարկում է էլ. Այս նախագծի նպատակն է օգնել ճանապարհորդներին գտնել լավագույն գործարքները:

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել

Եթե ​​նյութը հասկանալիս կորած ես զգում, նայիր սա է հոդված.

Ի՞նչ ենք մենք փնտրում:

Դուք ազատ եք օգտագործել այստեղ նկարագրված համակարգը, ինչպես ցանկանում եք: Օրինակ, ես օգտագործել եմ այն ​​հանգստյան օրերի շրջագայությունների և իմ հայրենի քաղաքի տոմսերի որոնման համար: Եթե ​​դուք լրջորեն մտածում եք շահավետ տոմսեր գտնելու մասին, կարող եք գործարկել սցենարը սերվերի վրա (պարզ սերվեր, ամսական 130 ռուբլու համար, բավականին հարմար է դրա համար) և համոզվեք, որ այն աշխատում է օրը մեկ կամ երկու անգամ։ Որոնման արդյունքները ձեզ կուղարկվեն էլ. Բացի այդ, ես խորհուրդ եմ տալիս ամեն ինչ կարգավորել այնպես, որ սցենարը պահպանի Excel ֆայլը որոնման արդյունքներով Dropbox թղթապանակում, որը թույլ կտա դիտել նման ֆայլերը ցանկացած վայրից և ցանկացած ժամանակ:

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել
Սխալներով սակագներ դեռ չեմ գտել, բայց կարծում եմ, որ դա հնարավոր է

Որոնման ժամանակ, ինչպես արդեն նշվեց, օգտագործվում է «ճկուն ամսաթիվ», սցենարը գտնում է առաջարկներ, որոնք նշված ամսաթվերից երեք օրվա ընթացքում են: Թեև սկրիպտը գործարկելիս այն փնտրում է առաջարկներ միայն մեկ ուղղությամբ, հեշտ է այն փոփոխել, որպեսզի կարողանա տվյալներ հավաքել թռիչքի մի քանի ուղղությունների վերաբերյալ։ Նրա օգնությամբ դուք նույնիսկ կարող եք սխալ սակագներ փնտրել, նման գտածոները կարող են շատ հետաքրքիր լինել:

Ինչու՞ է ձեզ անհրաժեշտ մեկ այլ վեբ քերիչ:

Երբ ես առաջին անգամ սկսեցի վեբ քերել, ես անկեղծորեն առանձնապես հետաքրքրված չէի դրանով: Ես ուզում էի ավելի շատ նախագծեր անել կանխատեսող մոդելավորման, ֆինանսական վերլուծության և, հնարավոր է, տեքստերի էմոցիոնալ գունավորումը վերլուծելու ոլորտում: Բայց պարզվեց, որ շատ հետաքրքիր էր պարզել, թե ինչպես կարելի է ստեղծել այնպիսի ծրագիր, որը տվյալներ է հավաքում կայքերից։ Երբ ես խորացավ այս թեմայի մեջ, ես հասկացա, որ վեբ քերծումը ինտերնետի «շարժիչն» է:

Դուք կարող եք մտածել, որ սա չափազանց համարձակ հայտարարություն է: Բայց հաշվի առեք, որ Google-ը սկսել է վեբ քերիչով, որը Լարի Փեյջը ստեղծել է Java-ի և Python-ի միջոցով: Google-ի ռոբոտները ուսումնասիրում են համացանցը՝ փորձելով իր օգտատերերին տրամադրել իրենց հարցերի լավագույն պատասխանները: Վեբ քերծումն ունի անվերջ կիրառություն, և նույնիսկ եթե ձեզ հետաքրքրում է տվյալների գիտության մեջ որևէ այլ բան, ձեզ անհրաժեշտ կլինեն քերելու որոշ հմտություններ՝ վերլուծելու համար անհրաժեշտ տվյալները ստանալու համար:

Ես գտա որոշ տեխնիկա, որն օգտագործվում է այստեղ մի հրաշալի գիրքը վեբ քերելու մասին, որը ես վերջերս եմ ձեռք բերել: Այն պարունակում է բազմաթիվ պարզ օրինակներ և գաղափարներ ձեր սովորածի գործնական կիրառման համար: Բացի այդ, կա մի շատ հետաքրքիր գլուխ reCaptcha չեկերը շրջանցելու մասին։ Սա ինձ համար նորություն էր, քանի որ ես նույնիսկ չգիտեի, որ կան հատուկ գործիքներ և նույնիսկ ամբողջ ծառայություններ նման խնդիրների լուծման համար:

Սիրում ես ճամփորդել?!

Այս բաժնի վերնագրում առաջադրված պարզ և բավականին անվնաս հարցին հաճախ կարելի է դրական պատասխան լսել՝ ուղեկցվող մի քանի պատմություն այն մարդու ճամփորդություններից, ում դա տրվել է: Մեզանից շատերը կհամաձայնեն, որ ճանապարհորդելը հիանալի միջոց է նոր մշակութային միջավայրում ընկղմվելու և ձեր մտահորիզոնն ընդլայնելու համար: Այնուամենայնիվ, եթե ինչ-որ մեկին հարցնեք՝ սիրո՞ւմ է ավիատոմսեր փնտրել, վստահ եմ, որ պատասխանն այնքան էլ դրական չի լինի։ Այստեղ, ըստ էության, մեզ օգնության է հասնում Python-ը։

Առաջին խնդիրը, որը պետք է լուծենք ավիատոմսերի վերաբերյալ տեղեկատվության որոնման համակարգի ստեղծման ճանապարհին, կլինի համապատասխան հարթակի ընտրությունը, որտեղից մենք տեղեկատվություն կվերցնենք։ Այս խնդրի լուծումն ինձ համար հեշտ չէր, բայց ի վերջո ընտրեցի Kayak ծառայությունը։ Ես փորձեցի Momondo-ի, Skyscanner-ի, Expedia-ի և մի քանի այլ ծառայություններ, բայց այս ռեսուրսների վրա ռոբոտների պաշտպանության մեխանիզմներն անթափանց էին: Մի քանի փորձերից հետո, որոնց ընթացքում ես ստիպված էի առնչվել լուսացույցների, հետիոտների անցումների և հեծանիվների հետ, փորձելով համոզել համակարգերին, որ ես մարդ եմ, ես որոշեցի, որ Kayak-ը ինձ համար լավագույնս համապատասխանում է, չնայած այն հանգամանքին, որ նույնիսկ եթե շատ էջեր են բեռնված: կարճ ժամանակում, և սկսվում են նաև ստուգումները։ Ինձ հաջողվեց ստիպել, որ բոտը հարցումներ ուղարկի կայք 4-ից 6 ժամ ընդմիջումներով, և ամեն ինչ լավ էր աշխատում: Ժամանակ առ ժամանակ դժվարություններ են առաջանում Kayak-ի հետ աշխատելիս, բայց եթե նրանք սկսում են նեղացնել ձեզ չեկերով, ապա պետք է կամ ձեռքով զբաղվել դրանցով, ապա գործարկել բոտը, կամ սպասել մի քանի ժամ, և ստուգումները պետք է դադարեցվեն: Անհրաժեշտության դեպքում դուք կարող եք հեշտությամբ հարմարեցնել կոդը մեկ այլ հարթակի համար, իսկ եթե դա անեք, ապա կարող եք հայտնել մեկնաբանություններում։

Եթե ​​դուք նոր եք սկսել վեբ քերծումը և չգիտեք, թե ինչու են որոշ կայքեր պայքարում դրա հետ, ապա նախքան այս ոլորտում ձեր առաջին նախագիծը սկսելը, լավություն արեք ինքներդ ձեզ և Google-ում որոնեք «վեբ քերելու վարվելակարգ» բառերը: . Ձեր փորձերը կարող են ավարտվել ավելի շուտ, քան կարծում եք, եթե անխոհեմաբար վեբ քերծեք:

Ինչից սկսել

Ահա ընդհանուր ակնարկ, թե ինչ տեղի կունենա մեր վեբ քերիչի կոդում.

  • Ներմուծեք անհրաժեշտ գրադարանները:
  • Google Chrome-ի ներդիրի բացում:
  • Զանգահարեք մի ֆունկցիա, որը գործարկում է բոտը՝ փոխանցելով այն քաղաքներն ու ամսաթվերը, որոնք կօգտագործվեն տոմսեր որոնելիս:
  • Այս ֆունկցիան վերցնում է որոնման առաջին արդյունքները՝ դասավորված ըստ լավագույնների, և սեղմում է կոճակը՝ ավելի շատ արդյունքներ բեռնելու համար:
  • Մեկ այլ գործառույթ հավաքում է տվյալներ ամբողջ էջից և վերադարձնում տվյալների շրջանակ:
  • Նախորդ երկու քայլերն իրականացվում են ըստ տոմսերի գնի (էժան) և թռիչքի արագության (ամենաարագ) տեսակների տեսակավորման միջոցով:
  • Սկրիպտի օգտագործողին ուղարկվում է նամակ, որը պարունակում է տոմսերի գների ամփոփում (ամենաէժան տոմսեր և միջին գին), և տվյալների շրջանակը՝ վերը նշված երեք ցուցանիշներով դասավորված տեղեկություններով, պահվում է որպես Excel ֆայլ:
  • Վերոհիշյալ բոլոր գործողությունները կատարվում են ցիկլով` սահմանված ժամկետից հետո:

Հարկ է նշել, որ յուրաքանչյուր Selenium նախագիծ սկսվում է վեբ դրայվերից։ ես օգտագործում եմ Chromedriver, աշխատում եմ Google Chrome-ով, բայց կան այլ տարբերակներ։ Հայտնի են նաև PhantomJS-ը և Firefox-ը: Վարորդը ներբեռնելուց հետո անհրաժեշտ է տեղադրել այն համապատասխան թղթապանակում, և դա ավարտում է դրա օգտագործման նախապատրաստումը: Մեր սցենարի առաջին տողերը բացում են Chrome-ի նոր ներդիր:

Նկատի ունեցեք, որ իմ պատմության մեջ ես չեմ փորձում նոր հորիզոններ բացել ավիատոմսերի համար մեծ գործարքներ գտնելու համար: Նման առաջարկների որոնման շատ ավելի առաջադեմ մեթոդներ կան: Ես պարզապես ուզում եմ այս նյութի ընթերցողներին առաջարկել այս խնդիրը լուծելու պարզ, բայց գործնական միջոց:

Ահա այն կոդը, որի մասին խոսեցինք վերևում։

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)

Կոդի սկզբում դուք կարող եք տեսնել փաթեթի ներմուծման հրամանները, որոնք օգտագործվում են մեր նախագծի ողջ ընթացքում: Այսպիսով, randint օգտագործվում էր բոտին «քնելու» համար պատահական թվով վայրկյաններ առաջ նոր որոնողական գործողություն սկսելուց առաջ: Սովորաբար ոչ մի բոտ չի կարող առանց դրա: Եթե ​​գործարկեք վերը նշված կոդը, կբացվի Chrome-ի պատուհան, որը բոտը կօգտագործի կայքերի հետ աշխատելու համար։

Եկեք մի փոքր փորձ անենք և առանձին պատուհանում բացենք kayak.com կայքը։ Մենք կընտրենք քաղաքը, որտեղից պատրաստվում ենք թռչել, և քաղաքը, որտեղ ցանկանում ենք հասնել, ինչպես նաև թռիչքների ժամկետները: Ամսաթվեր ընտրելիս համոզվեք, որ օգտագործված է +-3 օրվա միջակայքը։ Ես գրել եմ կոդը՝ հաշվի առնելով, թե ինչ է արտադրում կայքը՝ ի պատասխան նման հարցումների։ Եթե, օրինակ, անհրաժեշտ է տոմսեր փնտրել միայն նշված ամսաթվերի համար, ապա մեծ է հավանականությունը, որ ստիպված կլինեք փոփոխել բոտի կոդը։ Երբ ես խոսում եմ օրենսգրքի մասին, տալիս եմ համապատասխան բացատրություններ, բայց եթե շփոթված եք, տեղեկացրեք ինձ։

Այժմ սեղմեք որոնման կոճակը և նայեք հասցեի տողում գտնվող հղմանը: Այն պետք է նման լինի այն հղմանը, որը ես օգտագործում եմ ստորև բերված օրինակում, որտեղ փոփոխականը հայտարարված է kayak, որը պահպանում է URL-ը, և օգտագործվում է մեթոդը get վեբ վարորդ. Որոնման կոճակը սեղմելուց հետո արդյունքները պետք է հայտնվեն էջում:

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել
Երբ ես օգտագործեցի հրամանը get ավելի քան երկու կամ երեք անգամ մի քանի րոպեի ընթացքում ինձ խնդրեցին ավարտել ստուգումը reCaptcha-ի միջոցով: Դուք կարող եք ձեռքով անցնել այս ստուգումը և շարունակել փորձարկումները, մինչև համակարգը որոշի նոր ստուգում կատարել: Երբ ես փորձարկեցի սկրիպտը, թվում էր, որ որոնման առաջին նիստը միշտ սահուն է անցել, այնպես որ, եթե ցանկանում էիք փորձարկել կոդը, դուք միայն պետք է պարբերաբար ձեռքով ստուգեք և թույլ տաք, որ կոդը գործարկվի՝ օգտագործելով որոնման սեսիաների միջև երկար ընդմիջումներ: Եվ եթե մտածեք դրա մասին, ապա դժվար թե մարդուն անհրաժեշտ լինի տոմսերի գների մասին տեղեկություններ, որոնք ստացվում են որոնման գործողությունների միջև 10 րոպե ընդմիջումներով:

Աշխատեք էջի հետ՝ օգտագործելով XPath

Այսպիսով, մենք բացեցինք պատուհան և բեռնեցինք կայքը: Գների և այլ տեղեկություններ ստանալու համար մենք պետք է օգտագործենք XPath տեխնոլոգիան կամ CSS ընտրիչները: Ես որոշեցի մնալ XPath-ի հետ և չզգացի CSS ընտրիչներ օգտագործելու անհրաժեշտություն, բայց միանգամայն հնարավոր է այդպես աշխատել: XPath-ի միջոցով էջի շուրջ նավարկելը կարող է բարդ լինել, և նույնիսկ եթե դուք օգտագործում եք իմ նկարագրած տեխնիկան սա է հոդվածը, որը ներառում էր էջի կոդից համապատասխան նույնացուցիչների պատճենումը, ես հասկացա, որ դա իրականում անհրաժեշտ տարրեր մուտք գործելու օպտիմալ միջոց չէ: Ի դեպ, ներս սա է Գիրքը հիանալի նկարագրում է XPath և CSS ընտրիչներով էջերի հետ աշխատելու հիմունքները: Ահա թե ինչ տեսք ունի համապատասխան վեբ վարորդի մեթոդը:

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել
Այսպիսով, եկեք շարունակենք աշխատել բոտի վրա: Եկեք օգտագործենք ծրագրի հնարավորությունները՝ ընտրելու ամենաէժան տոմսերը։ Հետևյալ պատկերում XPath ընտրիչ կոդը ընդգծված է կարմիրով: Կոդը դիտելու համար անհրաժեշտ է աջ սեղմել ձեզ հետաքրքրող էջի տարրի վրա և հայտնվող մենյուից ընտրել Inspect հրամանը։ Այս հրամանը կարող է կանչվել էջի տարբեր տարրերի համար, որոնց կոդը կցուցադրվի և կնշվի կոդերի դիտիչում։

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել
Դիտեք էջի կոդը

Կոդից ընտրիչները պատճենելու թերությունների մասին իմ պատճառաբանության հաստատումը գտնելու համար ուշադրություն դարձրեք հետևյալ հատկանիշներին.

Սա այն է, ինչ դուք ստանում եք, երբ պատճենում եք կոդը.

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

Այսպիսի մի բան պատճենելու համար հարկավոր է աջ սեղմել ձեզ հետաքրքրող կոդի հատվածի վրա և հայտնվող մենյուից ընտրել Copy > Copy XPath հրամանը:

Ահա թե ինչ եմ օգտագործել «Ամենաէժան» կոճակը սահմանելու համար.

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

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել
Պատճենել հրամանը > Պատճենել XPath-ը

Ակնհայտ է, որ երկրորդ տարբերակը շատ ավելի պարզ է թվում։ Երբ օգտագործվում է, այն փնտրում է a տարր, որն ունի հատկանիշ data-code, հավասար price. Առաջին տարբերակն օգտագործելիս տարրը որոնվում է id որը հավասար է wtKI-price_aTab, և XPath-ի ուղին դեպի տարրը նման է /div[1]/div/div/div[1]/div/span/span. Այսպիսի XPath հարցումը էջին կհաջողվի, բայց միայն մեկ անգամ: Ես կարող եմ հենց հիմա ասել, որ id կփոխվի հաջորդ անգամ, երբ էջը բեռնվի: Նիշերի հաջորդականությունը wtKI Դինամիկ կերպով փոխվում է ամեն անգամ, երբ էջը բեռնվում է, ուստի այն օգտագործող կոդը անիմաստ կլինի հաջորդ էջի վերաբեռնումից հետո: Այսպիսով, որոշ ժամանակ տրամադրեք XPath-ը հասկանալու համար: Այս գիտելիքը ձեզ լավ կծառայի:

Այնուամենայնիվ, հարկ է նշել, որ XPath ընտրիչների պատճենումը կարող է օգտակար լինել բավականին պարզ կայքերի հետ աշխատելու ժամանակ, և եթե դա ձեզ հարմար է, ապա դրանում ոչ մի վատ բան չկա:

Հիմա եկեք մտածենք, թե ինչ անել, եթե ձեզ անհրաժեշտ է ստանալ բոլոր որոնման արդյունքները մի քանի տողերում, ցուցակի ներսում: Շատ պարզ. Յուրաքանչյուր արդյունք գտնվում է դաս ունեցող օբյեկտի ներսում resultWrapper. Բոլոր արդյունքների բեռնումը կարող է կատարվել ստորև ներկայացվածի նման հանգույցով:

Պետք է նշել, որ եթե հասկանում եք վերը նշվածը, ապա պետք է հեշտությամբ հասկանաք կոդի մեծ մասը, որը մենք կվերլուծենք։ Երբ այս կոդը գործարկվում է, մենք մուտք ենք գործում այն, ինչ մեզ անհրաժեշտ է (իրականում, այն տարրը, որի մեջ փաթաթված է արդյունքը)՝ օգտագործելով ինչ-որ ուղի սահմանող մեխանիզմ (XPath): Սա արվում է տարրի տեքստը ստանալու և այն օբյեկտի մեջ տեղադրելու համար, որտեղից կարելի է կարդալ տվյալները (առաջին անգամ օգտագործվել է flight_containers, ապա - flights_list).

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել
Առաջին երեք տողերը ցուցադրվում են, և մենք կարող ենք հստակ տեսնել այն ամենը, ինչ մեզ անհրաժեշտ է: Այնուամենայնիվ, մենք ունենք տեղեկատվություն ստանալու ավելի հետաքրքիր եղանակներ։ Մենք պետք է տվյալներ վերցնենք յուրաքանչյուր տարրից առանձին:

Անցիր գործի!

Ֆունկցիան գրելու ամենահեշտ ձևը լրացուցիչ արդյունքներ բեռնելն է, ուստի մենք կսկսենք այստեղից: Ես կցանկանայի առավելագույնի հասցնել թռիչքների թիվը, որոնց մասին ծրագիրը ստանում է տեղեկատվություն՝ առանց կասկածներ հարուցելու ծառայության մեջ, որը հանգեցնում է ստուգման, այնպես որ էջը ցուցադրելուց մեկ անգամ սեղմում եմ Load more results կոճակը: Այս օրենսգրքում դուք պետք է ուշադրություն դարձնեք բլոկին try, որը ես ավելացրել եմ, քանի որ երբեմն կոճակը ճիշտ չի բեռնվում։ Եթե ​​դուք նույնպես հանդիպեք դրան, մեկնաբանեք այս ֆունկցիայի զանգերը ֆունկցիայի կոդի մեջ start_kayak, որը կանդրադառնանք ստորև։

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

Այժմ, այս ֆունկցիայի երկար վերլուծությունից հետո (երբեմն կարող եմ տարվել), մենք պատրաստ ենք հայտարարել մի ֆունկցիա, որը կքերծի էջը։

Ես արդեն հավաքել եմ անհրաժեշտի մեծ մասը հետևյալ կոչվող ֆունկցիայի մեջ page_scrape. Երբեմն վերադարձված ուղու տվյալները համակցվում են, ուստի ես օգտագործում եմ պարզ մեթոդ՝ դրանք առանձնացնելու համար: Օրինակ, երբ ես առաջին անգամ օգտագործում եմ փոփոխականներ section_a_list и section_b_list. Մեր գործառույթը վերադարձնում է տվյալների շրջանակ flights_df, սա թույլ է տալիս առանձնացնել տվյալների տեսակավորման տարբեր մեթոդներից ստացված արդյունքները և հետագայում համատեղել դրանք։

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

Փորձեցի անվանել փոփոխականները, որպեսզի կոդը հասկանալի լինի։ Հիշեք, որ փոփոխականները սկսած a պատկանում են ճանապարհի առաջին փուլին, և b - երկրորդին: Անցնենք հաջորդ ֆունկցիային։

Աջակցման մեխանիզմներ

Այժմ մենք ունենք գործառույթ, որը թույլ է տալիս բեռնել լրացուցիչ որոնման արդյունքներ և գործառույթ՝ այդ արդյունքները մշակելու համար: Այս հոդվածը կարող էր ավարտվել այստեղ, քանի որ այս երկու գործառույթներն ապահովում են այն ամենը, ինչ անհրաժեշտ է էջերը քերելու համար, որոնք կարող եք ինքներդ բացել: Բայց մենք դեռ չենք դիտարկել վերը քննարկված օժանդակ մեխանիզմներից մի քանիսը: Օրինակ՝ սա նամակներ ուղարկելու և այլ բաների ծածկագիրն է։ Այս ամենը կարելի է գտնել ֆունկցիայի մեջ start_kayak, որը մենք հիմա կքննարկենք։

Որպեսզի այս գործառույթն աշխատի, ձեզ անհրաժեշտ է տեղեկություններ քաղաքների և ամսաթվերի մասին: Օգտագործելով այս տեղեկատվությունը, այն կազմում է հղում փոփոխականի մեջ kayak, որն օգտագործվում է ձեզ տանելու էջ, որը կպարունակի որոնման արդյունքներ՝ դասավորված ըստ իրենց լավագույն համապատասխանության հարցմանը: Առաջին քերիչ նիստից հետո մենք կաշխատենք էջի վերևում գտնվող աղյուսակի գների հետ: Այսինքն՝ մենք կգտնենք տոմսի նվազագույն գինը և միջին գինը։ Այս ամենը կայքի կողմից հրապարակված կանխատեսման հետ մեկտեղ կուղարկվի էլեկտրոնային փոստով։ Էջում համապատասխան աղյուսակը պետք է լինի վերին ձախ անկյունում։ Այս աղյուսակի հետ աշխատելը, ի դեպ, կարող է սխալ առաջացնել ճշգրիտ ամսաթվերով որոնելիս, քանի որ այս դեպքում աղյուսակը չի ցուցադրվում էջում։

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

Ես փորձարկեցի այս սցենարը՝ օգտագործելով Outlook հաշիվ (hotmail.com): Ես չեմ փորձարկել այն Gmail-ի հաշվի հետ ճիշտ աշխատելու համար, այս էլփոստի համակարգը բավականին տարածված է, բայց կան բազմաթիվ հնարավոր տարբերակներ։ Եթե ​​դուք օգտագործում եք Hotmail հաշիվ, ապա որպեսզի ամեն ինչ աշխատի, դուք պարզապես պետք է մուտքագրեք ձեր տվյալները կոդի մեջ։

Եթե ​​ցանկանում եք հասկանալ, թե կոնկրետ ինչ է արվում կոդի հատուկ բաժիններում այս ֆունկցիայի համար, կարող եք պատճենել դրանք և փորձարկել դրանք: Կոդի հետ փորձարկումն այն իրական հասկանալու միակ միջոցն է:

Պատրաստի համակարգ

Այժմ, երբ մենք արել ենք այն ամենը, ինչի մասին խոսեցինք, մենք կարող ենք ստեղծել մի պարզ օղակ, որը կանչում է մեր գործառույթները: Սցենարը օգտատերից պահանջում է տվյալներ քաղաքների և ամսաթվերի մասին: Սկրիպտի անընդհատ վերագործարկմամբ փորձարկելիս դժվար թե ցանկանաք ամեն անգամ ձեռքով մուտքագրել այս տվյալները, ուստի համապատասխան տողերը, թեստավորման ժամանակ, կարող են մեկնաբանվել՝ չմեկնաբանելով դրանց տակ գտնվողները, որոնցում անհրաժեշտ են տվյալները: սցենարը կոշտ կոդավորված է:

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

Ահա թե ինչ տեսք ունի սցենարի փորձնական գործարկումը:
Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել
Սցենարի փորձնական գործարկում

Արդյունքները

Եթե ​​այսքանը հասել եք, շնորհավորում եմ: Դուք այժմ ունեք աշխատող վեբ քերիչ, չնայած ես արդեն տեսնում եմ այն ​​բարելավելու բազմաթիվ ուղիներ: Օրինակ, այն կարող է ինտեգրվել Twilio-ի հետ, որպեսզի էլփոստի փոխարեն տեքստային հաղորդագրություններ ուղարկի: Դուք կարող եք օգտագործել VPN կամ այլ բան՝ միաժամանակ մի քանի սերվերներից արդյունքներ ստանալու համար: Պարբերաբար առաջացող խնդիր կա նաև կայքի օգտատերերին ստուգելու համար, թե արդյոք նա մարդ է, բայց այս խնդիրը նույնպես կարող է լուծվել։ Ամեն դեպքում, հիմա դուք ունեք բազա, որը ցանկության դեպքում կարող եք ընդլայնել։ Օրինակ, համոզվեք, որ Excel-ի ֆայլն ուղարկվում է օգտվողին որպես էլփոստի հավելված:

Python - օգնական էժան ավիատոմսեր գտնելու նրանց համար, ովքեր սիրում են ճանապարհորդել

Հարցմանը կարող են մասնակցել միայն գրանցված օգտվողները։ Մուտք գործել, խնդրում եմ:

Դուք օգտվո՞ւմ եք վեբ քերծման տեխնոլոգիաներից:

  • Այո

  • Ոչ

Քվեարկել է 8 օգտատեր։ 1 օգտատեր ձեռնպահ է մնացել։

Source: www.habr.com

Добавить комментарий