Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch

Tác giả của bài báo, bản dịch mà chúng tôi xuất bản ngày hôm nay, nói rằng mục tiêu của nó là nói về sự phát triển của một trình quét web bằng Python bằng cách sử dụng Selenium, công cụ tìm kiếm giá vé máy bay. Khi tìm kiếm vé, ngày linh hoạt được sử dụng (+- 3 ngày so với ngày được chỉ định). Công cụ cạp lưu kết quả tìm kiếm vào một tệp Excel và gửi cho người chạy nó một email kèm theo bản tóm tắt những gì họ tìm thấy. Mục tiêu của dự án này là giúp khách du lịch tìm được những ưu đãi tốt nhất.

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch

Nếu trong khi hiểu tài liệu mà bạn cảm thấy lạc lõng, hãy xem qua điều này bài báo.

Chúng tôi đang tìm kiếm cái gì?

Bạn được tự do sử dụng hệ thống được mô tả ở đây theo ý muốn. Ví dụ: tôi đã sử dụng nó để tìm kiếm các chuyến du lịch cuối tuần và vé về quê. Nếu bạn nghiêm túc trong việc tìm kiếm vé có lợi nhuận, bạn có thể chạy tập lệnh trên máy chủ (đơn giản máy chủ, với giá 130 rúp một tháng, khá phù hợp cho việc này) và đảm bảo rằng nó chạy một hoặc hai lần một ngày. Kết quả tìm kiếm sẽ được gửi cho bạn qua email. Ngoài ra, tôi khuyên bạn nên thiết lập mọi thứ để tập lệnh lưu tệp Excel có kết quả tìm kiếm vào thư mục Dropbox, điều này sẽ cho phép bạn xem các tệp đó từ mọi nơi và bất kỳ lúc nào.

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch
Tôi chưa tìm thấy biểu giá nào có lỗi, nhưng tôi nghĩ điều đó có thể xảy ra

Khi tìm kiếm, như đã đề cập, “ngày linh hoạt” được sử dụng; tập lệnh tìm các ưu đãi trong vòng ba ngày kể từ ngày đã cho. Mặc dù khi chạy tập lệnh, nó chỉ tìm kiếm các ưu đãi theo một hướng nhưng rất dễ sửa đổi để có thể thu thập dữ liệu trên một số hướng bay. Với sự trợ giúp của nó, bạn thậm chí có thể tìm kiếm các mức thuế sai, những phát hiện như vậy có thể rất thú vị.

Tại sao bạn cần một trình quét web khác?

Khi tôi mới bắt đầu tìm kiếm web, thực lòng tôi không đặc biệt quan tâm đến nó. Tôi muốn thực hiện nhiều dự án hơn trong lĩnh vực mô hình dự đoán, phân tích tài chính và có thể là trong lĩnh vực phân tích màu sắc cảm xúc của văn bản. Nhưng hóa ra việc tìm ra cách tạo một chương trình thu thập dữ liệu từ các trang web lại rất thú vị. Khi đi sâu vào chủ đề này, tôi nhận ra rằng việc quét web là “động cơ” của Internet.

Bạn có thể cho rằng đây là một tuyên bố quá táo bạo. Nhưng hãy xem xét rằng Google đã bắt đầu với một công cụ quét web mà Larry Page đã tạo bằng Java và Python. Các robot của Google đã và đang khám phá Internet, cố gắng cung cấp cho người dùng những câu trả lời tốt nhất cho câu hỏi của họ. Quét web có vô số công dụng và ngay cả khi bạn quan tâm đến thứ khác trong Khoa học dữ liệu, bạn sẽ cần một số kỹ năng quét web để có được dữ liệu cần phân tích.

Tôi nhận thấy một số kỹ thuật được sử dụng ở đây rất tuyệt vời quyển sách về việc quét web mà tôi mới có được. Nó chứa nhiều ví dụ và ý tưởng đơn giản để áp dụng thực tế những gì bạn đã học. Ngoài ra, có một chương rất thú vị về việc vượt qua kiểm tra reCaptcha. Điều này đối với tôi như một tin tức vì tôi thậm chí còn không biết rằng có những công cụ đặc biệt và thậm chí cả dịch vụ hoàn chỉnh để giải quyết những vấn đề như vậy.

Bạn có thích đi du lịch?!

Đối với câu hỏi đơn giản và khá vô hại được đặt ra trong tiêu đề của phần này, bạn thường có thể nghe thấy một câu trả lời tích cực, kèm theo một vài câu chuyện về chuyến đi của người được hỏi. Hầu hết chúng ta đều đồng ý rằng du lịch là một cách tuyệt vời để hòa mình vào môi trường văn hóa mới và mở rộng tầm nhìn của bạn. Tuy nhiên, nếu bạn hỏi ai đó rằng họ có thích tìm kiếm vé máy bay hay không, tôi chắc chắn rằng câu trả lời sẽ không tích cực như vậy. Trên thực tế, Python đã hỗ trợ chúng tôi ở đây.

Nhiệm vụ đầu tiên mà chúng ta cần giải quyết trên con đường tạo ra một hệ thống tìm kiếm thông tin về vé máy bay sẽ là lựa chọn một nền tảng phù hợp để chúng ta lấy thông tin. Giải quyết vấn đề này không hề dễ dàng với tôi nhưng cuối cùng tôi đã chọn dịch vụ Kayak. Tôi đã thử các dịch vụ của Momondo, Skyscanner, Expedia và một số dịch vụ khác, nhưng cơ chế bảo vệ robot trên các tài nguyên này là không thể xuyên thủng. Sau nhiều lần thử, trong đó tôi phải xử lý đèn giao thông, lối sang đường dành cho người đi bộ và xe đạp, cố gắng thuyết phục hệ thống rằng tôi là con người, tôi quyết định rằng Kayak là phù hợp nhất với tôi, mặc dù thực tế là ngay cả khi có quá nhiều trang được tải trong một thời gian ngắn và việc kiểm tra cũng bắt đầu. Tôi đã cố gắng khiến bot gửi yêu cầu đến trang web trong khoảng thời gian từ 4 đến 6 giờ và mọi thứ đều hoạt động tốt. Đôi khi, khó khăn nảy sinh khi làm việc với Kayak, nhưng nếu họ bắt đầu làm phiền bạn bằng séc, thì bạn cần phải xử lý chúng theo cách thủ công rồi khởi chạy bot hoặc đợi vài giờ và quá trình kiểm tra sẽ dừng lại. Nếu cần, bạn có thể dễ dàng điều chỉnh mã cho nền tảng khác và nếu làm như vậy, bạn có thể báo cáo mã đó trong phần nhận xét.

Nếu bạn mới bắt đầu với việc quét web và không biết tại sao một số trang web lại gặp khó khăn với nó, thì trước khi bạn bắt đầu dự án đầu tiên của mình trong lĩnh vực này, hãy tự mình tìm kiếm trên Google với từ "nghi thức quét web" . Thử nghiệm của bạn có thể kết thúc sớm hơn bạn nghĩ nếu bạn quét web không cẩn thận.

Bắt đầu

Dưới đây là tổng quan chung về những gì sẽ xảy ra trong mã quét web của chúng tôi:

  • Nhập các thư viện cần thiết.
  • Đang mở tab Google Chrome.
  • Gọi một hàm khởi động bot, chuyển cho nó các thành phố và ngày tháng sẽ được sử dụng khi tìm kiếm vé.
  • Chức năng này lấy kết quả tìm kiếm đầu tiên, sắp xếp theo kết quả tốt nhất và nhấp vào nút để tải thêm kết quả.
  • Một hàm khác thu thập dữ liệu từ toàn bộ trang và trả về khung dữ liệu.
  • Hai bước trước được thực hiện bằng cách sắp xếp theo giá vé (rẻ) và theo tốc độ bay (nhanh nhất).
  • Người dùng tập lệnh sẽ nhận được một email chứa bản tóm tắt về giá vé (vé rẻ nhất và giá trung bình) và khung dữ liệu với thông tin được sắp xếp theo ba chỉ số nêu trên sẽ được lưu dưới dạng tệp Excel.
  • Tất cả các hành động trên được thực hiện theo một chu kỳ sau một khoảng thời gian xác định.

Cần lưu ý rằng mọi dự án Selenium đều bắt đầu bằng trình điều khiển web. tôi sử dụng Trình điều khiển Chrome, Tôi làm việc với Google Chrome, nhưng có những lựa chọn khác. PhantomJS và Firefox cũng rất phổ biến. Sau khi tải xuống trình điều khiển, bạn cần đặt nó vào thư mục thích hợp và việc chuẩn bị sử dụng nó đã hoàn tất. Những dòng đầu tiên trong tập lệnh của chúng tôi sẽ mở ra một tab Chrome mới.

Hãy nhớ rằng trong câu chuyện của tôi, tôi không cố gắng mở ra những chân trời mới để tìm kiếm những ưu đãi lớn về vé máy bay. Có nhiều phương pháp nâng cao hơn để tìm kiếm những ưu đãi như vậy. Tôi chỉ muốn cung cấp cho độc giả tài liệu này một cách đơn giản nhưng thiết thực để giải quyết vấn đề này.

Đây là đoạn mã chúng ta đã nói ở trên.

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)

Ở phần đầu của mã, bạn có thể thấy các lệnh nhập gói được sử dụng trong suốt dự án của chúng tôi. Vì thế, randint được sử dụng để làm cho bot “ngủ quên” trong một số giây ngẫu nhiên trước khi bắt đầu thao tác tìm kiếm mới. Thông thường, không một bot nào có thể làm được nếu không có điều này. Nếu bạn chạy đoạn mã trên, một cửa sổ Chrome sẽ mở ra và bot sẽ sử dụng cửa sổ này để hoạt động với các trang web.

Hãy cùng thực hiện một thử nghiệm nhỏ và mở trang web kayak.com trong một cửa sổ riêng. Chúng tôi sẽ chọn thành phố nơi chúng tôi sẽ bay và thành phố chúng tôi muốn đến, cũng như ngày bay. Khi chọn ngày, hãy đảm bảo rằng phạm vi +-3 ngày được sử dụng. Tôi đã viết mã có tính đến những gì trang web tạo ra để đáp ứng những yêu cầu như vậy. Ví dụ: nếu bạn chỉ cần tìm kiếm vé cho những ngày được chỉ định thì khả năng cao là bạn sẽ phải sửa đổi mã bot. Khi nói về mã, tôi sẽ đưa ra những giải thích phù hợp nhưng nếu bạn cảm thấy khó hiểu, hãy cho tôi biết.

Bây giờ hãy nhấp vào nút tìm kiếm và xem liên kết trên thanh địa chỉ. Nó phải giống với liên kết tôi sử dụng trong ví dụ bên dưới nơi khai báo biến kayak, lưu trữ URL và phương thức được sử dụng get trình điều khiển web. Sau khi nhấp vào nút tìm kiếm, kết quả sẽ xuất hiện trên trang.

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch
Khi tôi sử dụng lệnh get hơn hai hoặc ba lần trong vòng vài phút, tôi được yêu cầu hoàn tất xác minh bằng reCaptcha. Bạn có thể vượt qua bước kiểm tra này theo cách thủ công và tiếp tục thử nghiệm cho đến khi hệ thống quyết định chạy một bước kiểm tra mới. Khi tôi kiểm tra tập lệnh, có vẻ như phiên tìm kiếm đầu tiên luôn diễn ra suôn sẻ, vì vậy nếu muốn thử nghiệm mã, bạn chỉ cần kiểm tra thủ công định kỳ và để mã chạy, sử dụng khoảng thời gian dài giữa các phiên tìm kiếm. Và, nếu bạn nghĩ về điều đó, một người khó có thể cần thông tin về giá vé nhận được trong khoảng thời gian 10 phút giữa các hoạt động tìm kiếm.

Làm việc với một trang bằng XPath

Vì vậy, chúng tôi đã mở một cửa sổ và tải trang web. Để biết giá và thông tin khác, chúng tôi cần sử dụng công nghệ XPath hoặc bộ chọn CSS. Tôi quyết định gắn bó với XPath và không cảm thấy cần phải sử dụng bộ chọn CSS, nhưng hoàn toàn có thể làm việc theo cách đó. Điều hướng xung quanh một trang bằng XPath có thể khó khăn và ngay cả khi bạn sử dụng các kỹ thuật tôi đã mô tả trong điều này bài viết liên quan đến việc sao chép các mã nhận dạng tương ứng từ mã trang, tôi nhận ra rằng trên thực tế, đây không phải là cách tối ưu để truy cập các phần tử cần thiết. Nhân tiện, trong điều này Cuốn sách cung cấp mô tả tuyệt vời về những điều cơ bản khi làm việc với các trang bằng bộ chọn XPath và CSS. Đây là giao diện của phương thức trình điều khiển web tương ứng.

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch
Vì vậy, hãy tiếp tục làm việc với bot. Hãy sử dụng khả năng của chương trình để chọn vé rẻ nhất. Trong hình ảnh sau đây, mã bộ chọn XPath được đánh dấu màu đỏ. Để xem mã, bạn cần nhấp chuột phải vào thành phần trang mà bạn quan tâm và chọn lệnh Kiểm tra từ menu xuất hiện. Lệnh này có thể được gọi cho các thành phần trang khác nhau, mã của thành phần này sẽ được hiển thị và đánh dấu trong trình xem mã.

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch
Xem mã trang

Để tìm sự xác nhận cho lý luận của tôi về những nhược điểm của việc sao chép bộ chọn từ mã, hãy chú ý đến các tính năng sau.

Đây là những gì bạn nhận được khi sao chép mã:

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

Để sao chép một cái gì đó như thế này, bạn cần nhấp chuột phải vào phần mã bạn quan tâm và chọn lệnh Copy > Copy XPath từ menu xuất hiện.

Đây là những gì tôi đã sử dụng để xác định nút Rẻ nhất:

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

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch
Sao chép lệnh > Sao chép XPath

Rõ ràng là tùy chọn thứ hai trông đơn giản hơn nhiều. Khi được sử dụng, nó tìm kiếm phần tử a có thuộc tính data-codetương đương với price. Khi sử dụng tùy chọn đầu tiên, phần tử được tìm kiếm id bằng với wtKI-price_aTabvà đường dẫn XPath tới phần tử trông giống như /div[1]/div/div/div[1]/div/span/span. Một truy vấn XPath như thế này tới một trang sẽ thực hiện được thủ thuật nhưng chỉ một lần. Tôi có thể nói ngay bây giờ rằng id sẽ thay đổi vào lần tải trang tiếp theo. Chuỗi ký tự wtKI thay đổi linh hoạt mỗi khi trang được tải, vì vậy mã sử dụng nó sẽ vô dụng sau khi tải lại trang tiếp theo. Vì vậy hãy dành chút thời gian để hiểu XPath. Kiến thức này sẽ phục vụ bạn tốt.

Tuy nhiên, cần lưu ý rằng việc sao chép các bộ chọn XPath có thể hữu ích khi làm việc với các trang khá đơn giản, và nếu bạn cảm thấy thoải mái với việc này thì không có gì sai với nó.

Bây giờ, hãy nghĩ xem phải làm gì nếu bạn cần lấy tất cả kết quả tìm kiếm trong một vài dòng, bên trong một danh sách. Rất đơn giản. Mỗi kết quả nằm bên trong một đối tượng có một lớp resultWrapper. Việc tải tất cả các kết quả có thể được thực hiện trong một vòng lặp tương tự như vòng lặp được hiển thị bên dưới.

Cần lưu ý rằng nếu bạn hiểu những điều trên thì bạn sẽ dễ dàng hiểu được hầu hết mã mà chúng tôi sẽ phân tích. Khi mã này chạy, chúng tôi truy cập những gì chúng tôi cần (trên thực tế, phần tử trong đó kết quả được bao bọc) bằng cách sử dụng một số loại cơ chế chỉ định đường dẫn (XPath). Điều này được thực hiện để lấy văn bản của phần tử và đặt nó vào một đối tượng mà dữ liệu có thể được đọc (lần đầu tiên được sử dụng flight_containers, sau đó - flights_list).

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch
Ba dòng đầu tiên được hiển thị và chúng ta có thể thấy rõ mọi thứ mình cần. Tuy nhiên, chúng ta có nhiều cách thú vị hơn để thu thập thông tin. Chúng ta cần lấy dữ liệu từ từng phần tử riêng biệt.

Bắt đầu làm!

Cách dễ nhất để viết một hàm là tải các kết quả bổ sung, vì vậy đó là nơi chúng ta sẽ bắt đầu. Tôi muốn tối đa hóa số lượng chuyến bay mà chương trình nhận được thông tin mà không gây nghi ngờ về dịch vụ dẫn đến việc kiểm tra, vì vậy tôi nhấp vào nút Tải thêm kết quả mỗi lần trang được hiển thị. Trong đoạn mã này, bạn nên chú ý đến khối try, tôi đã thêm vì đôi khi nút không tải đúng cách. Nếu bạn cũng gặp phải điều này, hãy bình luận các lệnh gọi hàm này trong mã hàm start_kayak, mà chúng ta sẽ xem xét dưới đây.

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

Bây giờ, sau một thời gian dài phân tích hàm này (đôi khi tôi có thể cảm thấy khó chịu), chúng ta đã sẵn sàng khai báo một hàm sẽ loại bỏ trang.

Tôi đã thu thập hầu hết những gì cần thiết trong hàm sau được gọi là page_scrape. Đôi khi dữ liệu đường dẫn trả về được kết hợp lại nên tôi sử dụng một phương pháp đơn giản để tách nó ra. Ví dụ: khi tôi sử dụng biến lần đầu tiên section_a_list и section_b_list. Hàm của chúng tôi trả về một khung dữ liệu flights_df, điều này cho phép chúng tôi tách các kết quả thu được từ các phương pháp sắp xếp dữ liệu khác nhau và sau đó kết hợp chúng lại.

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

Tôi đã cố gắng đặt tên các biến sao cho mã có thể hiểu được. Hãy nhớ rằng các biến bắt đầu bằng a thuộc về giai đoạn đầu tiên của con đường, và b - đến thứ hai. Hãy chuyển sang chức năng tiếp theo.

Cơ chế hỗ trợ

Bây giờ chúng tôi có một chức năng cho phép chúng tôi tải các kết quả tìm kiếm bổ sung và một chức năng để xử lý các kết quả đó. Bài viết này có thể đã kết thúc ở đây vì hai chức năng này cung cấp mọi thứ bạn cần để loại bỏ các trang mà bạn có thể tự mở. Nhưng chúng tôi vẫn chưa xem xét một số cơ chế phụ trợ đã thảo luận ở trên. Ví dụ: đây là mã để gửi email và một số thứ khác. Tất cả điều này có thể được tìm thấy trong chức năng start_kayak, mà bây giờ chúng ta sẽ xem xét.

Để chức năng này hoạt động, bạn cần có thông tin về thành phố và ngày tháng. Sử dụng thông tin này, nó tạo thành một liên kết trong một biến kayak, được sử dụng để đưa bạn đến một trang chứa các kết quả tìm kiếm được sắp xếp theo kết quả phù hợp nhất với truy vấn. Sau phiên thu thập dữ liệu đầu tiên, chúng tôi sẽ làm việc với giá trong bảng ở đầu trang. Cụ thể là chúng ta sẽ tìm giá vé tối thiểu và giá trung bình. Tất cả điều này, cùng với dự đoán do trang web đưa ra, sẽ được gửi qua email. Trên trang, bảng tương ứng phải ở góc trên bên trái. Nhân tiện, làm việc với bảng này có thể gây ra lỗi khi tìm kiếm theo ngày chính xác, vì trong trường hợp này bảng không được hiển thị trên trang.

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

Tôi đã kiểm tra tập lệnh này bằng tài khoản Outlook (hotmail.com). Tôi chưa kiểm tra xem nó có hoạt động chính xác với tài khoản Gmail hay không, hệ thống email này khá phổ biến nhưng có nhiều tùy chọn khả thi. Nếu bạn sử dụng tài khoản Hotmail thì để mọi thứ hoạt động, bạn chỉ cần nhập dữ liệu của mình vào mã.

Nếu bạn muốn hiểu chính xác những gì đang được thực hiện trong các phần cụ thể của mã cho chức năng này, bạn có thể sao chép chúng và thử nghiệm chúng. Thử nghiệm với mã là cách duy nhất để thực sự hiểu nó.

Hệ thống sẵn sàng

Bây giờ chúng ta đã hoàn thành mọi thứ đã nói, chúng ta có thể tạo một vòng lặp đơn giản gọi các hàm của mình. Tập lệnh yêu cầu dữ liệu từ người dùng về thành phố và ngày tháng. Khi kiểm tra với việc khởi động lại tập lệnh liên tục, bạn có thể không muốn nhập dữ liệu này theo cách thủ công mỗi lần, do đó, các dòng tương ứng, trong thời gian thử nghiệm, có thể được nhận xét bằng cách bỏ ghi chú những dòng bên dưới chúng, trong đó dữ liệu cần thiết cho tập lệnh được mã hóa cứng.

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

Đây là giao diện chạy thử của tập lệnh.
Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch
Chạy thử script

Kết quả

Nếu bạn đã làm được điều này đến nay, xin chúc mừng! Bây giờ bạn đã có một trình quét web đang hoạt động, mặc dù tôi đã có thể thấy nhiều cách để cải thiện nó. Ví dụ: nó có thể được tích hợp với Twilio để gửi tin nhắn văn bản thay vì email. Bạn có thể sử dụng VPN hoặc thứ gì khác để nhận kết quả đồng thời từ một số máy chủ. Ngoài ra còn có một vấn đề phát sinh định kỳ là kiểm tra người dùng trang web xem họ có phải là người hay không, nhưng vấn đề này cũng có thể được giải quyết. Trong mọi trường hợp, bây giờ bạn đã có cơ sở mà bạn có thể mở rộng nếu muốn. Ví dụ: đảm bảo rằng tệp Excel được gửi tới người dùng dưới dạng tệp đính kèm vào email.

Python - trợ thủ tìm vé máy bay giá rẻ cho những người thích đi du lịch

Chỉ những người dùng đã đăng ký mới có thể tham gia khảo sát. Đăng nhập, xin vui lòng.

Bạn có sử dụng công nghệ quét web không?

  • vâng

  • Không

8 người dùng bình chọn. 1 người dùng đã bỏ phiếu trắng.

Nguồn: www.habr.com

Thêm một lời nhận xét