Pull to refresh

Пишем мониторинг наличия билетов на РЖД

PythonProgrammingGoogle App Engine
Sandbox
Не раз слышал от своих знакомых, что было бы неплохо увидеть сайт который будет мониторить наличие свободных мест на ржд. Про себя я думал — «да неплохо бы» и благополучно забывал, но пост заставил меня вспомнить навыки копи паста, которыми я владею в совершенстве и обернуть это дело в питонячий код. Сразу оговорюсь что именно про мониторинг будет во второй части, а в этой будет про то: как ходить на РЖД из питона, что это за загадочный sleep про который писали в предыдущем посту и как живется на Google App Engine. Итак приступим:
image

Сначала я написал код, а потом задумался о хостинге, естественно сайт не предполагал никакой наживы, а был лишь 4 fun, поэтому и хостинг должен быть бесплатным и тут я вспомнил про App Engine. Для начала работы стоит скачать SDK. Запускаем, указываем путь к будущему приложению:
image

В указанной рабочей директории создаем файл настроек app.yaml содержащий примерно следующее:

application: rzdzstan1
version: 1
runtime: python27
threadsafe: false
api_version: 1

handlers:
- url: /favicon.ico
  static_files: favicon.ico
  upload: favicon.ico

- url: /.*
  script: web.py

libraries:
- name: webapp2
  version: "latest"


Дальше в вышеобозначенной рабочей директории создаем, web.py и тут уже можно начинать писать код копипастить. Приложение будем строить на легковесном WebApp2. Итак пишем основные обработчики:

import webapp2

application = webapp2.WSGIApplication([
    ('/', MainPage),
    ('/trains', TrainListPage),
    ('/suggester', SuggesterPage),
], debug=True)

def main():
    application.run()

if __name__ == "__main__":
    main()


Далее, как и говорилось в базовой статье, нам понадобятся коды городов для создания запроса:

def getCityId(city, s):
  req = 'http://pass.rzd.ru/suggester?lang=ru&stationNamePart=' + urllib.quote(city.encode('utf-8'))
  city = city.lower()
  respData = getResponse(req)
  rJson = json.loads(respData)
  for item in rJson:
    if item['name'].lower() == city:
      s.response.out.write(u'Найден: '+item['name']+' -> '+str(item['id'])+'<br>')
      return str(item['id'])
  s.response.out.write(u'Не найден: '+city+'<br>')
  s.response.out.write(u'Выбранный вами город не найден, попробуйте найти в списке и ввести еще раз:<a href="../">Вернуться</a><br>')
  for item in rJson:
    s.response.out.write(item['name']+'<br>')
  return None


Ну а дальше остается получить rid, SESSION_ID и сформировать окончательный запрос, не забывая что часто РЖД рвет соединения, отвечает 500 кодом и т.д. чтобы это замаскировать напишем пару костылей-обработчиков:

def getResponse(url):
  good = False
  while not good:
    try:
      resp = opener.open(url, timeout=5)
      if resp.getcode() in [httplib.OK, httplib.CREATED, httplib.ACCEPTED]:
        good = True
    except (urllib2.HTTPError, HTTPException):
      pass
  return resp.read()

def getResponseStub(url):
  r = json.loads(getResponse(url))
  cnt = 0
  while (r['result']!='OK' and cnt < 5):
    sleep(1)
    cnt+=1
    r = json.loads(getResponse(url))
  return r

def getFinalRequest():
  req1 = 'http://pass.rzd.ru/timetable/public/ru?STRUCTURE_ID=735&layer_id=5371&dir=0&tfl=3&checkSeats=1&\
st0='+st0+'&code0='+id0+'&dt0='+date+'&st1='+st1+'&code1='+id1+'&dt1='+date

  r = json.loads(getResponse(req1))
  if (r['result']=='OK'):
    s.response.out.write(r['tp'][0]['msgList'][0]['message']) #errType
    s.response.out.write('<br>')
    return
  sid = str(r['SESSION_ID'])
  rid = str(r['rid'])
  req2 = 'http://pass.rzd.ru/timetable/public/ru?STRUCTURE_ID=735&layer_id=5371&dir=0&tfl=3&checkSeats=1&\
st0='+st0+'&code0='+id0+'&dt0='+date+'&st1='+st1+'&code1='+id1+'&dt1='+date+'&rid='+rid+'&SESSION_ID='+sid

  r = getResponseStub(req2)


И в получившемся ответе — лежит все необходимое для финального парсинга. Теперь о загадочном sleep, он переехал в функцию: getResponseStub, дело в том что когда мы запрашиваем req1 му таким образом просим поставить нас в очередь исполнения, и если сразу спросить req2 — результат может быть еще не получен. Радиоактивные исходники доступны тут качать осторожно. Попробовать в действии можно тут и тут ибо квоты там небольшие и под известным эффектом быстро закончатся, а пока эта статья проходит премодерацию попробую закинуть немного денег чтобы страница продержалась продолжительное время. В следующей части будем приделывать собственно саму нотификацию по емайлу.
Tags:python ржд
Hubs: Python Programming Google App Engine
Total votes 26: ↑18 and ↓8 +10
Views27.8K

Comments 14

Only those users with full accounts are able to leave comments. Log in, please.

Popular right now

Программист Unreal Engine 4
from 120,000 ₽MYTONARemote job
Python middle
from 150,000 to 200,000 ₽Portal BiletRemote job
Python разработчик
from 90,000 to 180,000 ₽AGORARemote job
Ведущий разработчик python
from 200,000 ₽Группа «Самолет»Remote job
Python разработчик
from 120,000 to 200,000 ₽Squilla FundRemote job