Мониторинг электронных билетов ржд при помощи selenium

в 12:04, , рубрики: python, selenium, билеты через интернет, Песочница, метки: , ,

Волей судьбы мне приходится часто ездить по России из города в город на небольшие расстояния: я живу и работаю в Москве, а родственники и друзья живут в Казани. Раньше билеты на поезда приходилось покупать заранее, ехать на вокзал в кассы, стоять в очередях, и процесс занимал много времени, был негибок, и все приходилось планировать заранее.

В данной статье я расскажу о проблемах при покупке билетов РЖД и о том, как я пытаюсь их решать при помощи автоматизации действий в браузере.

Введение

Несколько лет назад РЖД начали продавать билеты через интернет на своем сайте ticket.rzd.ru, а вскоре почти на все поезда появилась электронная регистрация (не обязательно получать купленный через интернет билет в окошке на вокзале, а можно купить билет в интернете, при желании распечатать бланк заказа, и с ним напрямую идти к проводнику, у которого есть список пассижиров).

Таким образом, процесс стал отнимать меньше времени, и вся суть его сводилась лишь к покупке билета на сайте. Но билеты на популярные направления быстро разбирают, если их не покупать заранее, и часто билетов на нужный тебе поезд попросту нет, не говоря уже о таких больших праздниках, как новый год, когда билеты появляются в продаже за 45 дней, а через несколько дней их уже нет в наличии.

Часто в таких случаях я просто сидел и обновлял страницу в надежде, что кто-то сдаст билет, и я успею его купить. С удивлением стоит признать, что при интенсивном и продолжительном обновлении страницы с билетами я всегда получал возможность в итоге купить билет и уехать, но приходилось посвещать такому «брутфорсу» много времени и сил.

Автоматизация

В какойто момент мне в голову пришла идея автоматизировать этот процесс. Сразу оговорюсь, что я автоматизировал процесс мониторинга свободных билетов, без этапа покупки и проплаты, т.к. очевидно, что это опасная затея.

Я видел много статей на хабре о selenium и его применении для автоматизации действий в браузере и решил применить его для данной задачи. Для решения на python(он прекрасно подходит для этой задачи) нужно установить соответствующий пакет:

pip install selenium

Макет кода можно быстро сгенерировать с помощью Selenium IDE (это плагин для Firefox), просто совершая стандартные действия на странице при заранее включенной записи действий пользователя. Плагин доступен на официальном сайте.

Затем этот код пришлось немного «причесать» и добавить парсинг отображаемой информации, нужные нам проверки, запуск сигнала тревоги.
В итоге получается чтото подобное коду ниже:

# coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
import time, re
import winsound


class Rzdtemp():
    def __init__(self, logger):
        self.logger = logger
    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.base_url = "http://ticket.rzd.ru/"
        self.verificationErrors = []

    def test_rzdtemp(self):

        self.logger.info('Вход...')

        driver = self.driver
        driver.get(self.base_url + "/static/public/ticket?STRUCTURE_ID=2")
        driver.find_element_by_link_text("Вход").click()

        self.logger.info('Ввод логина и пароля...')

        driver.find_element_by_id("j_username").clear()
        driver.find_element_by_id("j_username").send_keys("username")
        driver.find_element_by_id("j_password").clear()
        driver.find_element_by_id("j_password").send_keys("password")
        driver.find_element_by_id("other").click()

        self.logger.info('Меню покупки билетов...')

        driver.find_element_by_link_text("Покупка билета").click()

        self.logger.info('Ввод данных...')

        driver.find_element_by_id("fromInput").clear()
        driver.find_element_by_id("fromInput").send_keys(u'КАЗАНЬ ПАСС')
        driver.find_element_by_id("whereInput").clear()
        driver.find_element_by_id("whereInput").send_keys(u'МОСКВА КАЗАНСКАЯ')
        driver.find_element_by_id("forwardDate").clear()
        driver.find_element_by_id("forwardDate").send_keys(u'02.09.2012')
        driver.find_element_by_id("ticket_button_submit").click()

        time.sleep(40)

        self.logger.info('Поиск нужных билетов...')
        rawhtml = driver.find_element_by_id('ajaxTrainTable').get_attribute("innerHTML")

        if u'Плацкартный' in rawhtml:
            self.logger.info('!!!ЕСТЬ ПЛАЦКАРТ!!!')
            strlist = [x.strip() for x in rawhtml.split('n') if x.strip()!=u'']
            #print strlist
            train = ''
            for i,x in enumerate(strlist):
                if x == u'<div class="wotnumarrow">':
                    train = strlist[i+1].replace('<span><b>','')
                if x == u'Плацкартный':
                    #включаем сигнал тревоги
                    winsound.PlaySound('alarma.ogg', winsound.SND_NOWAIT)
                    self.logger.info(u'Поезд-%s Число-%s %s' % ( train, strlist[i+3].replace('<b>','').replace('</b>',''),
                    strlist[i+5].replace('<td><span>','').replace('</span></td>','')))

        elif u'Сидячий' in rawhtml:
            self.logger.info('Только сидячие...')
        elif u'Купе' in rawhtml:
            self.logger.info('Только купе...')

        self.logger.info('Выход...')

        driver.find_element_by_link_text("Выход").click()

    def tearDown(self):
        self.logger.info('Закрываем браузер...')
        self.driver.close()
        self.driver.quit()


Этот код можно в цикле запускать с заданным интервалом, например проверять билеты каждые 5 минут.
Весь процесс написания и отладки кода занял примерно полчаса.
К коду можно прикрутить любые проверки, для нужных нам поездов, дат, мест и так далее.
В данной версии при обнаружении срабатывает звуковой сигнал, что билет появился, в качестве альтернативного варианта можно отсылать email или смс, однако в этом случае можно попросту не успеть купить билет.

Итоги

Пока на сайте нет капчи, можно спокойно пользоваться похожими механизмами, если вдруг появится, придется придумывать чтото посложнее. Кто знает, возможно в будующем у РЖД появится чтото типа сервиса с API.
Стоит отметить, что билеты, к моему удивлению, сдаются достаточно часто, и вероятность перехватить только что сданный билет очень велика, если быстро успеть заполнить заказ. Часто бывало, что билет появлялся, но после заполнения выяснялось, что его уже успел купить ктото другой.
По личному опыту больше всего билетов сдается в последний день.
Удачных поездок!

Автор: Ramiel

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js