Пост актуальный для майских праздников. 6 недель назад я подал документы, чтобы получить визу в Ирландию. Вылет запланирован на 30 апреля. Существует сайт посольства, на котором публикуются списки решений по визам. Они это делают по понедельникам и четвергам. И вот я сижу в воскресенье, 28 апреля, по моей визе решения еще нет. И дальнейшие мои действия в понедельник зависят от того, будет ли мое заявление в новом отчете или нет. Если нет, то надо будет ехать в посольство и разбираться. Если есть, то дергать визовый центр. Сидеть и обновлять страничку целый день в понедельник казалось унылым времяпрепровождением, поэтому я написал скрипт на Python.



Disclaimer. Я не программист, но умею программировать. Это значит, что я не могу написать изящный и эффективный код, но я могу заставить эту шарманку делать то, что мне от нее нужно.


1. Проверка страницы на наличие нового отчета


Итак, что требуется сделать:


  1. Нужно спарсить эту страницу.
  2. Найти среди отчетов новый по новой дате (в моем случае можно искать по слову 24 April).
  3. Получить ссылку на этот отчет.

Код функции выглядит следующим образом:


def check_report(url, div_class, date):
    embassy_page = requests.get(url)
    page_text = BeautifulSoup(embassy.text, 'lxml')
    tags = page_text.findAll('div', {"class": div_class})
    text = ''
    report_url = ''
    for tag in tags:
        tag_soup = BeautifulSoup(tag.text, 'lxml')
        report = s(text=re.compile(date))
        if len(report) > 0:
            text = 'New report published'
            report_url = 'https://www.dfa.ie' + tag.find('a').attrs['href']
    return text, report_url

Теперь подробнее, что в этом коде происходит.


Для начала мы пользуемся библиотекой requests, которая помогает нам скачать требуемую страницу. Затем, мы используем другую библиотеку BeautifulSoup, которая помогает превратить эту дикую разметку страницы в более красивый и удобный вид.


До использования BeautifulSoup:


'<!DOCTYPE html>\r\n\r\n<html lang="en">\r\n  <head>\r\n    <META http-equiv="X-UA-Compatible" content="IE=edge">\r\n    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\t\r\n\t<meta name="viewport" content="initial-scale=1">\r\n    \r\n    \r\n     \r\n    \r\n      \r\n              <!-- Static Meta data -->\r\n         \r\n\t<!-- <meta name="DC.Creator" content="Department of Foreign Affairs" />\r\n\t<meta name="DC.Publisher" content="Department of Foreign Affairs" /> \r\n\t<meta name="DC.Format" content="text/xhtml" /> \r\n\t<meta name="DC.Copyright" content="All material (c) copyright 2012 Department of Foreign Affairs" /> \r\n\t<meta name="DC.Source" content="Department of Foreign Affairs" /> \r\n\t<meta name="DC.Language" content="en" />\r\n\t<meta name="DC.Author" content="Department of Foreign Affairs" /> -->\r\n\r\n\r\n<meta name="author" content="Department of Foreign Affairs">\r\n<meta name="google-site-verification" content="HHtulupgM8GXpd9YYDjoXUb6MiU7_mGTkHixUrVPFYQ" />\r\n      \r\n\t<title>Weekly Decision Report - Department of Foreign Affairs and Trade</title>\r\n      <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />  \r\n    <link rel="stylesheet" type="text/css" media="screen" href="/media/dfa-2017/style-assets/css/font-defs.css" />\t<!-- 2017 font-defs.css --> \r\n\t<link rel="stylesheet" type="text/css" media="screen" href="/media/dfa-2017/style-assets/css/style.css" />\t<!-- 2017 style.css -->\r\n    <link rel="stylesheet" type="text/css" media="print" href="/media/dfa-2017/style-assets/css/print.css" />\t<!-- 2017 print.css -->\r\n 

После:


<!DOCTYPE html>
<html lang="en">
<head>
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta content="initial-scale=1" name="viewport"/>
<!-- Static Meta data -->
<!-- <meta name="DC.Creator" content="Department of Foreign Affairs" />
    <meta name="DC.Publisher" content="Department of Foreign Affairs" /> 
    <meta name="DC.Format" content="text/xhtml" /> 
    <meta name="DC.Copyright" content="All material (c) copyright 2012 Department of Foreign Affairs" /> 
    <meta name="DC.Source" content="Department of Foreign Affairs" /> 
    <meta name="DC.Language" content="en" />
    <meta name="DC.Author" content="Department of Foreign Affairs" /> -->
<meta content="Department of Foreign Affairs" name="author"/>
<meta content="HHtulupgM8GXpd9YYDjoXUb6MiU7_mGTkHixUrVPFYQ" name="google-site-verification"/>
<title>Weekly Decision Report - Department of Foreign Affairs and Trade</title>
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="/media/dfa-2017/style-assets/css/font-defs.css" media="screen" rel="stylesheet" type="text/css"/> <!-- 2017 font-defs.css -->
<link href="/media/dfa-2017/style-assets/css/style.css" media="screen" rel="stylesheet" type="text/css"/> <!-- 2017 style.css -->

С этим теперь можно как-то жить и работать. В частности в моем случае мы будем искать особый div class, который используется для ссылок на отчеты. Мы это видим по исходному коду страницы: <div class="gen-content-landing__block">. В моем коде мы ищем все такие теги.


Далее идем по собранным тегам и ищем тот, внутри которого содержится дата нового отчета: 24 April. Если такой результат найден, то вытаскиваем из него ссылку и формируем текст, что новый отчет опубликован.


2. Поиск id визы в новом отчете


Итак, что требуется сделать теперь здесь:


  1. Скачать этот новый отчет.
  2. Распарсить pdf-файл.
  3. Найти в нем мой id.
  4. Найти статус, соответствующий ему.

Код функции выглядит следующим образом:


def check_visa(report_url, filename, visa_id, text):
    pdf = requests.get(report_url)
    file_path = Path(filename)
    file_path.write_bytes(pdf.content)
    pdfFileObj = open(filename, 'rb')
    pdfReader = PyPDF2.PdfFileReader(pdfFileObj, strict=False)  
    for pageNum in range(0, pdfReader.numPages):
        page = str(pdfReader.getPage(pageNum).extractText().encode('utf-8')).split('\\n')
        if visa_id in page:
            visa_index = page.index(visa_id)
            status = page[visa_index + 1]
            text = text + '\t' + visa_id + '\t' + status    
    return text

При помощи опять же библиотеки requests делаем запрос к этому отчету. Далее сохраняем его локально. При помощи библиотеки PyPDF2 производим чтение файла. После чего перебираем его страницы и ищем visa_id в массиве токенов. Разметка этого pdf-файла такова, что следующим токеном после visa_id идет непосредственно статус рассмотрения: Approved или Refused. Далее мы конкатенируем существующий текст с id и статусом.


3. Отправка статуса сообщением в Slack


Хорошо. Cкрипт нашел, допустим, мой id, но нужно же где-то меня уведомить об этом. Мы в компании у себя используем Slack в качестве мессенджера, поэтому я подумал, что мне было бы удобно получить уведомление туда.


По этой ссылке можно настроить себе webhook. Там можно выбрать канал или адресата, кому запостить сообщение (возможно, для этого шага потребуется быть админом WorkSpace). Там же вы получите уникальный url для webhook, который можно использовать в коде.


def send_to_slack(webhook_url, text):
    post = {"text": "{0}".format(text)}
    json_data = json.dumps(post)
    req = requests.post(webhook_url, data=json_data.encode('ascii'), headers={'Content-Type': 'application/json'})
    return req.status_code

Пользуясь все той же библиотекой requests, делаем POST-запрос с содержимым text по адресу webhook.


4. Использование функций


Остаток кода выглядит так:


url = 'https://www.dfa.ie/irish-embassy/russia/visas/weekly-decision-report/'
div_class = 'gen-content-landing__block'
date = '24 April'
filename = 'weekly_report.pdf'
visa_id = '38644112'
webhook_url = 'https://hooks.slack.com/services/...'

text, report_url = check_report(url, div_class, date)
if text != '':
    text = check_visa(report_url, filename, visa_id, text)
    print(send_to_slack(webhook_url, text))

Присваиваем значения всем необходимым переменным и далее запускаем прописанные функции.


5. Запуск по плану


Ок. Скрипт я написал, но если мне придется его самостоятельно каждый раз дергать, то я недалеко ушел от изначального состояния дел, где мне бы пришлось сидеть и обновлять страничку.


$ crontab -e

В него добавил строчку:


*/10 * * * * python3 /home/ubuntu/embassy.py >/dev/null 2>&1

Я подумал, что мне достаточно, если этот скрипт будет отрабатываться каждые 10 минут по cron на сервере с ubuntu.


6. Заключение


В 11:50 мне пришло сообщение, что появлися новый отчет, но моей визы в нем не оказалось… После этого я поехал в посольство. Взял его штурмом (на письма и звонки не отвечали в течение нескольких недель) и в итоге получил свой паспорт с визой.


В общем, скилл программирования важен в современном мире, даже если ты не программист. Он позволяет автоматизировать некоторые твои рутинные операции, что делает твой мир чуточку удобнее. На самом деле можно было бы это даже оформить в отдельный сервис, где человек просто вводит свой id и e-mail, и ему приходит сообщение о готовности визы на почту.

Комментарии (11)


  1. HEKOT
    30.04.2019 11:45

    Когда в конце апреля люди на работе занимаются всякой хренью, это называется «отпускное настроение» :)
    Приятного отпуска!


    1. a-pichugin Автор
      30.04.2019 12:03

      Спасибо)

      P.S. Это ж воскресенье в нерабочее время)


  1. pz_true
    30.04.2019 11:57
    +1

    Программирование — грамотность 21 века.


  1. EvgeniyNuAfanasievich
    30.04.2019 14:02
    +1

    После этого я поехал в посольство. Взял его штурмом

    Вот самый ценный скилл)


  1. Mykola_Von_Raybokobylko
    30.04.2019 14:45

    Вам еще повезло. Тут наоборот «ирландец» в Россию приезжал. Чтоб получить визу для многократного посещения нашей необъятной родины, несчастный иностранец каждый месяц по два три раза интервьюировался в ген консульстве.


    1. a-pichugin Автор
      30.04.2019 14:51

      И ведь не автоматизируешь это


      1. Mykola_Von_Raybokobylko
        30.04.2019 15:08

        Можно автоматизировать. Убрав пункт обязательное интервью в консульстве. Где нужно подробно многократно объяснять цель своего визита в страну. Для деловых многократных поездок нужно буквально заучивать легенду чтоб не сбиться в показаниях))Также нужно наличие официального приглашения от принимающей стороны на фирменном бланке. Принимающая сторона также должна иметь аккредитацию в МИДе. Форма для получения обычной туристической визы есть на сайте и через сайт же отсылается на рассмотрение. срок рассмотрения 1 неделя.
        Самый сок для иностранцев в России это поставить на учет автомобиль. Сейчас может и упростили процедуру, но вот 10 лет назад, иностранная компания не могла самостоятельно оформить гос регистрацию, только сотрудник с российским гражданством на которого была оформлена особая доверенность на право производить регистрацию.


  1. ilya_kochetov
    30.04.2019 21:35

    Отличный кейс для роботизации, и навыки программирования особо не нужны


  1. Kastrulya0001
    30.04.2019 23:05

    Там же есть услуга отправки СМС по готовности документов.


    1. maxzhurkin
      02.05.2019 14:04

      SMS — прошлый век :)


  1. technic93
    03.05.2019 19:36

    А откуда автор заранее знал как будет выглядеть полученный PDF и в каком div будет лежать ответ на сайте? Это была не первая виза?)