پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند

نویسنده مقاله که ترجمه آن را امروز منتشر می کنیم، می گوید که هدف آن صحبت در مورد توسعه وب اسکریپر در پایتون با استفاده از سلنیوم است که قیمت بلیط هواپیما را جستجو می کند. هنگام جستجوی بلیط، از تاریخ های انعطاف پذیر استفاده می شود (+- 3 روز نسبت به تاریخ های مشخص شده). اسکراپر نتایج جستجو را در یک فایل اکسل ذخیره می کند و برای شخصی که جستجو را انجام داده ایمیلی را با خلاصه ای از آنچه پیدا کرده است ارسال می کند. هدف این پروژه کمک به مسافران برای یافتن بهترین معاملات است.

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند

اگر در حین درک مطالب، احساس گمراهی کردید، نگاهی به آن بیندازید این مقاله.

ما دنبال چی میگردیم؟

شما مختار هستید از سیستمی که در اینجا توضیح داده شده است، هر طور که می خواهید استفاده کنید. به عنوان مثال، من از آن برای جستجوی تورهای آخر هفته و بلیط به زادگاهم استفاده کردم. اگر در مورد یافتن بلیط های سودآور جدی هستید، می توانید اسکریپت را روی سرور اجرا کنید (ساده سرور، برای 130 روبل در ماه، برای این کاملا مناسب است) و مطمئن شوید که یک یا دو بار در روز اجرا می شود. نتایج جستجو از طریق ایمیل برای شما ارسال خواهد شد. علاوه بر این، توصیه می کنم همه چیز را طوری تنظیم کنید که اسکریپت یک فایل اکسل را با نتایج جستجو در یک پوشه Dropbox ذخیره کند، که به شما امکان می دهد چنین فایل هایی را از هر کجا و در هر زمان مشاهده کنید.

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند
من هنوز تعرفه هایی با خطا پیدا نکردم، اما فکر می کنم امکان پذیر است

همانطور که قبلاً ذکر شد هنگام جستجو، از «تاریخ انعطاف‌پذیر» استفاده می‌شود؛ اسکریپت پیشنهادهایی را پیدا می‌کند که در عرض سه روز از تاریخ‌های معین هستند. اگرچه هنگام اجرای اسکریپت، پیشنهادات را فقط در یک جهت جستجو می کند، به راحتی می توان آن را تغییر داد تا بتواند داده ها را در چندین جهت پرواز جمع آوری کند. با کمک آن، حتی می توانید به دنبال تعرفه های اشتباه باشید؛ چنین یافته هایی می تواند بسیار جالب باشد.

چرا به وب اسکراپر دیگری نیاز دارید؟

وقتی برای اولین بار اسکراپینگ وب را شروع کردم، صادقانه بگویم که علاقه خاصی به آن نداشتم. می‌خواستم پروژه‌های بیشتری در زمینه مدل‌سازی پیش‌بینی‌کننده، تحلیل مالی و احتمالاً در زمینه تحلیل رنگ‌آمیزی احساسی متون انجام دهم. اما معلوم شد که نحوه ایجاد برنامه ای که داده ها را از وب سایت ها جمع آوری می کند بسیار جالب بود. همانطور که به این موضوع پرداختم، متوجه شدم که خراش دادن وب "موتور" اینترنت است.

ممکن است فکر کنید که این بیانیه بیش از حد جسورانه است. اما در نظر بگیرید که گوگل با یک وب اسکراپر که لری پیج با استفاده از جاوا و پایتون ایجاد کرد، شروع به کار کرد. ربات های گوگل در حال کاوش در اینترنت بوده و سعی در ارائه بهترین پاسخ به سوالات کاربران خود دارند. خراش دادن وب کاربردهای بی پایانی دارد، و حتی اگر به چیز دیگری در علم داده علاقه مند هستید، برای به دست آوردن داده هایی که برای تجزیه و تحلیل نیاز دارید به مهارت های خراش دادن نیاز دارید.

من برخی از تکنیک های مورد استفاده در اینجا را در یک کتاب فوق العاده یافتم کتاب در مورد خراش دادن وب، که اخیراً به دست آوردم. این شامل بسیاری از مثال ها و ایده های ساده برای کاربرد عملی آنچه آموخته اید است. علاوه بر این، فصل بسیار جالبی در مورد دور زدن چک های reCaptcha وجود دارد. این برای من یک خبر بود، زیرا من حتی نمی دانستم که ابزارهای ویژه و حتی کل خدمات برای حل چنین مشکلاتی وجود دارد.

دوست داری سفر کنی؟!

به سؤال ساده و نسبتاً بی‌ضرر مطرح شده در عنوان این بخش، اغلب می‌توانید پاسخ مثبتی را بشنوید که همراه با چند داستان از سفرهای شخصی که از او پرسیده شده است. بسیاری از ما موافقیم که سفر راهی عالی برای غوطه ور شدن در محیط های فرهنگی جدید و گسترش افق های خود است. با این حال، اگر از کسی بپرسید که آیا دوست دارد بلیط هواپیما را جستجو کند، مطمئن هستم که پاسخ آنقدرها مثبت نخواهد بود. در واقع، پایتون در اینجا به کمک ما می آید.

اولین کاری که در راه ایجاد سیستم جستجوی اطلاعات بلیط هواپیما باید حل کنیم، انتخاب بستر مناسبی است که از آن اطلاعات بگیریم. حل این مشکل برای من آسان نبود اما در نهایت سرویس کایاک را انتخاب کردم. من خدمات موموندو، اسکای اسکنر، اکسپدیا و چند مورد دیگر را امتحان کردم، اما مکانیسم های حفاظتی ربات روی این منابع غیرقابل نفوذ بود. پس از چندین بار تلاش، که طی آن مجبور شدم با چراغ‌های راهنمایی، گذرگاه‌های عابر پیاده و دوچرخه‌ها سر و کار داشته باشم، و سعی کردم سیستم‌ها را متقاعد کنم که من انسان هستم، به این نتیجه رسیدم که کایاک برای من مناسب‌تر است، علی‌رغم این واقعیت که حتی اگر صفحات زیادی بارگذاری شود. در مدت زمان کوتاهی و بررسی ها نیز آغاز می شود. من توانستم کاری کنم که ربات در فواصل زمانی 4 تا 6 ساعت درخواست های خود را به سایت ارسال کند و همه چیز به خوبی کار کرد. هر از گاهی هنگام کار با کایاک مشکلاتی پیش می آید، اما اگر شروع به آزار و اذیت شما با چک کنند، باید یا به صورت دستی با آنها برخورد کنید و سپس ربات را راه اندازی کنید، یا چند ساعت صبر کنید تا بررسی ها متوقف شود. در صورت لزوم می توانید به راحتی کد را برای پلتفرم دیگری تطبیق دهید و در صورت انجام این کار می توانید آن را در نظرات گزارش دهید.

اگر به تازگی شروع به کار با خراش دادن وب کرده‌اید و نمی‌دانید چرا برخی از وب‌سایت‌ها با آن مشکل دارند، پس قبل از شروع اولین پروژه خود در این زمینه، به خودتان لطف کنید و عبارت «آداب خراش دادن وب» را در گوگل جستجو کنید. . ممکن است آزمایش‌های شما زودتر از آنچه فکر می‌کنید به پایان برسد، اگر خراش‌های وب خود را نابخردانه انجام دهید.

شروع

در اینجا یک نمای کلی از آنچه در کد اسکراپر وب ما اتفاق می افتد وجود دارد:

  • کتابخانه های مورد نیاز را وارد کنید.
  • باز کردن تب گوگل کروم
  • تابعی را فراخوانی کنید که ربات را راه‌اندازی می‌کند و شهرها و تاریخ‌هایی را که هنگام جستجوی بلیت استفاده می‌شوند، ارسال می‌کند.
  • این تابع اولین نتایج جستجو را می گیرد که بر اساس بهترین مرتب شده اند و روی دکمه ای کلیک می کند تا نتایج بیشتری بارگیری شود.
  • تابع دیگری داده ها را از کل صفحه جمع آوری می کند و یک قاب داده را برمی گرداند.
  • دو مرحله قبلی با استفاده از انواع مرتب سازی بر اساس قیمت بلیط (ارزان) و سرعت پرواز (سریع ترین) انجام می شود.
  • برای کاربر اسکریپت ایمیلی حاوی خلاصه‌ای از قیمت بلیط (ارزان‌ترین بلیط و متوسط ​​قیمت) ارسال می‌شود و یک چارچوب داده با اطلاعات مرتب‌شده بر اساس سه شاخص فوق به‌عنوان یک فایل اکسل ذخیره می‌شود.
  • تمام اقدامات فوق در یک چرخه پس از یک دوره زمانی مشخص انجام می شود.

لازم به ذکر است که هر پروژه سلنیوم با یک درایور وب شروع می شود. من استفاده می کنم Chromedriver، من با Google Chrome کار می کنم، اما گزینه های دیگری وجود دارد. PhantomJS و Firefox نیز محبوب هستند. پس از دانلود درایور، باید آن را در پوشه مناسب قرار دهید و این کار آماده سازی برای استفاده از آن را کامل می کند. اولین خطوط اسکریپت ما یک تب جدید کروم را باز می کند.

به خاطر داشته باشید که در داستان من سعی نمی کنم افق های جدیدی را برای یافتن معاملات عالی در بلیط هواپیما باز کنم. روش های بسیار پیشرفته تری برای جستجوی چنین پیشنهاداتی وجود دارد. من فقط می خواهم به خوانندگان این مطالب یک راه ساده اما کاربردی برای حل این مشکل ارائه دهم.

در اینجا کدی است که در بالا در مورد آن صحبت کردیم.

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 برای به خواب رفتن ربات برای تعداد تصادفی از ثانیه قبل از شروع عملیات جستجوی جدید استفاده می شود. معمولاً هیچ رباتی نمی تواند بدون این کار کند. اگر کد بالا را اجرا کنید، یک پنجره کروم باز می شود که ربات از آن برای کار با سایت ها استفاده می کند.

بیایید یک آزمایش کوچک انجام دهیم و وب سایت kayak.com را در یک پنجره جداگانه باز کنیم. ما شهری که قرار است از آنجا پرواز کنیم، شهری که می خواهیم به آن برسیم و همچنین تاریخ پرواز را انتخاب می کنیم. هنگام انتخاب تاریخ، مطمئن شوید که از محدوده +-3 روز استفاده می شود. من کد را با در نظر گرفتن آنچه سایت در پاسخ به چنین درخواست هایی تولید می کند نوشتم. به عنوان مثال، اگر فقط برای تاریخ های مشخص شده نیاز به جستجوی بلیط داشته باشید، احتمال زیادی وجود دارد که مجبور شوید کد ربات را تغییر دهید. وقتی در مورد کد صحبت می کنم، توضیحات مناسبی ارائه می کنم، اما اگر احساس گیجی کردید، به من اطلاع دهید.

حالا روی دکمه جستجو کلیک کنید و به لینک موجود در نوار آدرس نگاه کنید. باید شبیه پیوندی باشد که در مثال زیر استفاده می‌کنم، جایی که متغیر اعلام شده است kayak، که URL را ذخیره می کند و از روش استفاده می شود get درایور وب پس از کلیک بر روی دکمه جستجو، نتایج باید در صفحه ظاهر شوند.

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند
وقتی از دستور استفاده کردم get بیش از دو یا سه بار در عرض چند دقیقه، از من خواسته شد تا تأیید را با استفاده از reCaptcha تکمیل کنم. می توانید این بررسی را به صورت دستی پاس کنید و تا زمانی که سیستم تصمیم به اجرای یک بررسی جدید بگیرد، به آزمایش ادامه دهید. وقتی اسکریپت را آزمایش کردم، به نظر می‌رسید که اولین جلسه جستجو همیشه بدون مشکل پیش می‌رفت، بنابراین اگر می‌خواهید کد را آزمایش کنید، فقط باید به صورت دوره‌ای به صورت دستی بررسی کنید و اجازه دهید کد اجرا شود، با استفاده از فواصل طولانی بین جلسات جستجو. و اگر در مورد آن فکر کنید، بعید است که شخصی به اطلاعاتی در مورد قیمت بلیط دریافت شده در فواصل 10 دقیقه ای بین عملیات جستجو نیاز داشته باشد.

کار با یک صفحه با استفاده از XPath

بنابراین، پنجره ای را باز کردیم و سایت را بارگذاری کردیم. برای دریافت قیمت و سایر اطلاعات، باید از فناوری XPath یا انتخابگرهای CSS استفاده کنیم. من تصمیم گرفتم از XPath استفاده کنم و نیازی به استفاده از انتخابگرهای CSS احساس نکردم، اما کار به این صورت کاملاً ممکن است. پیمایش در یک صفحه با استفاده از XPath می تواند دشوار باشد، حتی اگر از تکنیک هایی که در آن توضیح دادم استفاده کنید این مقاله، که شامل کپی کردن شناسه های مربوطه از کد صفحه بود، متوجه شدم که این در واقع راه بهینه برای دسترسی به عناصر ضروری نیست. به هر حال، در این این کتاب توضیحاتی عالی از اصول اولیه کار با صفحات با استفاده از انتخابگرهای XPath و CSS ارائه می دهد. این همان چیزی است که روش درایور وب مربوطه به نظر می رسد.

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند
بنابراین، اجازه دهید به کار بر روی ربات ادامه دهیم. بیایید از قابلیت های برنامه برای انتخاب ارزان ترین بلیط ها استفاده کنیم. در تصویر زیر کد انتخابگر XPath با رنگ قرمز مشخص شده است. برای مشاهده کد، باید بر روی عنصر صفحه مورد نظر خود کلیک راست کرده و از منوی ظاهر شده دستور Inspect را انتخاب کنید. این دستور را می توان برای عناصر مختلف صفحه فراخوانی کرد که کد آن در نمایشگر کد نمایش داده می شود و برجسته می شود.

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند
مشاهده کد صفحه

به منظور تأیید استدلال من در مورد معایب کپی کردن انتخابگرها از کد، به ویژگی های زیر توجه کنید.

این چیزی است که هنگام کپی کردن کد به دست می آورید:

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

برای کپی کردن چیزی مانند این، باید بر روی قسمت کد مورد نظر خود کلیک راست کرده و از منوی ظاهر شده، دستور Copy > Copy XPath را انتخاب کنید.

در اینجا چیزی است که من برای تعریف دکمه ارزانترین استفاده کردم:

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

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند
Copy Command > Copy 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).

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند
سه خط اول نمایش داده می شود و ما می توانیم هر آنچه را که نیاز داریم به وضوح ببینیم. با این حال، ما راه های جالب تری برای به دست آوردن اطلاعات داریم. باید از هر عنصر جداگانه داده بگیریم.

دست به کار شو!

ساده ترین راه برای نوشتن یک تابع، بارگذاری نتایج اضافی است، بنابراین از اینجا شروع می کنیم. من می‌خواهم تعداد پروازهایی را که برنامه در مورد آنها اطلاعات دریافت می‌کند، بدون ایجاد سوء ظن در سرویسی که منجر به بازرسی می‌شود، به حداکثر برسانم، بنابراین هر بار که صفحه نمایش داده می‌شود، دکمه 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) آزمایش کردم. من آن را آزمایش نکرده ام تا با یک حساب جیمیل به درستی کار کند، این سیستم ایمیل بسیار محبوب است، اما گزینه های ممکن زیادی وجود دارد. اگر از یک حساب 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.....')

اجرای آزمایشی اسکریپت به این صورت است.
پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند
اجرای آزمایشی اسکریپت

نمایش نتایج: از

اگر تا اینجا پیش رفته اید، به شما تبریک می گویم! شما اکنون یک اسکراپر وب دارید، اگرچه من می توانم راه های زیادی برای بهبود آن ببینم. به عنوان مثال، می توان آن را با Twilio ادغام کرد تا به جای ایمیل، پیامک ارسال کند. می توانید از VPN یا چیز دیگری برای دریافت همزمان نتایج از چندین سرور استفاده کنید. همچنین یک مشکل دوره ای برای بررسی کاربر سایت برای دیدن اینکه آیا او یک شخص است وجود دارد، اما این مشکل نیز قابل حل است. در هر صورت، اکنون شما یک پایه دارید که در صورت تمایل می توانید آن را گسترش دهید. به عنوان مثال، مطمئن شوید که یک فایل اکسل به عنوان ضمیمه یک ایمیل برای کاربر ارسال می شود.

پایتون - دستیار در یافتن بلیط هواپیما ارزان برای کسانی که عاشق سفر هستند

فقط کاربران ثبت نام شده می توانند در نظرسنجی شرکت کنند. ورود، لطفا.

آیا از فناوری های خراش دادن وب استفاده می کنید؟

  • بله

  • بدون

8 کاربر رای دادند. 1 کاربر ممتنع.

منبع: www.habr.com

اضافه کردن نظر