python

Как я узнал, что моя виза не готова, сообщением в Slack

  • среда, 1 мая 2019 г. в 00:27:20
https://habr.com/ru/company/newprolab/blog/450060/
  • Блог компании New Professions Lab
  • DIY или Сделай сам
  • Python
  • Кодобред
  • Открытые данные


Пост актуальный для майских праздников. 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, и ему приходит сообщение о готовности визы на почту.