• Работаем с Твиттером

    Как по-быстому связать приложение на Питоне с Твиттером?

    Ответ — подключить библиотеку oauthtwitter.

    Почему не python-twitter? Потому что первая гораздо меньше по объему кода и числу зависимостей от дополнительных модулей: 300 строк кода против 4000!

    Установим необходимые библиотеки:

    pip install ouath2
    pip install httplib2
    pip install simplejson
    

    Для работы с Твиттером нужно зарегистрировать приложение. Твиттер позволяет указывать ссылки вида localhost или 127.0.0.1 в поле «Callback URL», что очень полезно при разработке на локальной машине.

    screenshot

    Consumer key — это открытый ключ приложения, что-то вроде логина. Consumer secret — закрытый ключ, которым подписываются запросы к Твиттеру. Если вы где-то опубликовали его, немедленно смените в настройках приложения.

    Авторизация в Твиттере проходит в несколько шагов.

    Шаг 1: запрос на авторизацию

    import oauthtwitter
    
    CONSUMER_KEY = "key"
    SECRET_KEY = "secret"
    
    # Инициализация клиента по ключам
    def sign_init(request):
        client = oauthtwitter.OAuthApi(CONSUMER_KEY, SECRET_KEY)
    
        # Уникальный токен, действительный только для одной авторизации
        request_token = client.getRequestToken()
    
        # Сохраняем токен в сессию пользователя. Можно сохранить на диск или в БД
        request.session["request_token"] = request_token
    
        # Получаем уникальный адрес авторизации на основе токена
        url = client.getAuthorizationURL(request_token)
    
        # Перенаправляем пользователя на страницу ввода пароля
        return redirect(url)
    

    Шаг 2: ввод логина и пароля на стороне Твиттера

    screenshot

    Шаг 3: возврат пользователя на ваш сайт

    После авторизации пользователь будет направлен обратно на ваш сайт. Напомню, что в приложении на стадии тестирования у нас указано что-то вроде http://localhost:8080/twitter/callback По этому адресу должен быть обработчик, который завершит процесс авторизации.

    def sign_callback(request):
        # Извлекаем сохраненный ранее в сессии токен.
        # Если токена нет, то пусть начинает всё с начала.
        request_token = request.session.get("request_token")
        if not request_token:
            add_message(request, ERROR, u"Request token not found.")
            return redirect(frontpage)
    
        # Ключ валидации запроса из командной строки.
        oauth_verifier = request.GET.get("oauth_verifier")
        if not oauth_verifier:
            add_message(request, ERROR, u"OAuth verifier not found.")
            return redirect(frontpage)
    
        # Снова инициализируем клиента
        client = oauthtwitter.OAuthApi(CONSUMER_KEY, SECRET_KEY)
    
        # Извлечени параметров доступа
        access_token = client.getAccessToken(request_token, oauth_verifier)
        print access_token
        >>>
        {
            'oauth_token_secret': 'xTbNbG0IcUGgng9pyNdy******************',
            'user_id': '123123123',
            'oauth_token': '78741204-cLgfThwKXuqudeXGTQP***********************',
            'screen_name': 'username'
        }
    

    Отлично! Как видно, access_token представляет из себя словарь со всеми нужными данными: идентификатором и именем пользователя, открытым и закрытым ключами доступа.

    Дальнейший сценарий зависит от архитектуры вашего приложения. Например, на основании screen_name заводится новый пользователь (или извлекается имеющийся). Оба ключа записываются в профиль пользователя. Теперь можно работать с Твиттером:

    # Инициализация клиента с ключами приложения и пользователя
    client = oauthtwitter.OAuthApi(CONSUMER_KEY, SECRET_KEY,
                        user_oauth_token, user_oauth_token_secret)
    
    # Извлечение твитов пользователя
    timeline = client.GetUserTimeline()
    
    # Твитнуть сообщение
    client.UpdateStatus("Python testing")
    
    # Получить фолловеров
    timeline = client.GetFollowers()
    

    И так далее, список всех методов ищите в коде класса. Очень доволен этой библиотекой, использую ее в двух проектах.

    Комментарии из старого блога

    02/11/13 Майя Любимая: я ничего не понимаю

    02/20/13 Иван Гришаев: То же самое могу сказать о вашем комментарии.

  • Создание файлов Excel

    В статье «Почему форматы Microsoft Office такие сложные?» Джоэл Спольски пишет:

    Заставьте Office делать грязную работу за вас. Word и Excel завязаны на сложных объектных моделях, основанных на автоматизации COM, что позволяет программно делать всё, что угодно. Во многих ситуациях проще будет использовать код Office, чем пытаться реализовать его с нуля.

    Действительно, если требуется сформировать сложный офисный документ, проще сделать это вызовами API через COM-соединение. Однако, для создания простой эксель-таблички можно задействовать библиотеку xlwt.

    Библиотека ставиться просто:

    pip install xlwt
    

    Требуется сформировать таблицу с тремя колонками:

    1. номер лицевого счета абонента, строковый тип;
    2. дата показания, тип дата;
    3. показание, тип число.

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

    class Statement(models.Model):
        user = models.ForeignKey(User)
        value = models.DecimalField(max_digits=12, decimal_places=6)
        date = models.DateTimeField(default=datetime.now)
    

    Показания, которые необходимо записать в таблицу, извлекаются следующим образом

    statements = Statement.objects.filter(date__range=(t1, t2))
    

    Формирование файла Excel будет выглядеть так:

    import xlwt
    
    # Определяем формат ячеек для дат
    date_style = xlwt.XFStyle()
    date_style.num_format_str = "M/D/YY"
    
    # Определяем формат ячеек для чисел
    num_style = xlwt.XFStyle()
    num_style.num_format_str = "0.00"
    
    wbk = xlwt.Workbook() # Новая книга
    sheet = wbk.add_sheet("statements") # Новый лист в книге
    
    # Добавляем шапку таблицы
    sheet.write(0, 0, u"Лицевой счет")
    sheet.write(0, 1, u"Дата")
    sheet.write(0, 2, u"Показание")
    
    # Обход показаний, statements — объект QuerySet из Джанго
    for i, s in enumerate(statements):
    
        # i — индекс итерации, используется как номер строки
        sheet.write(i+1, 0, s.user.username)
        sheet.write(i+1, 1, s.date, date_style)
        sheet.write(i+1, 2, s.value, num_style)
    
    # Задаем ширину колонок
    sheet.col(0).width = 5000
    sheet.col(1).width = 5000
    sheet.col(2).width = 5000
    
    # Запись файла
    wbk.save("path/to/file.xls")
    
  • Запись непойманных исключений в файл

    На работе у меня много скриптов, написанных на питоне, и большая часть из них выполняется ночью. Проблема — любой, даже самый надежный скрипт может упасть. Не удалось соединиться с БД, не найден нужный файл, отвалился сетевой ресурс, прислали кривой реестр.

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

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

    screenshot

    Имея эти данные, нетрудно выявить причину ошибки и внести в скрипт нужные коррективы. Модуль очень полезен для CGI-скриптов: ошибки не придется искать в логах Апача, тем более, что такой подробной информации вы там не найдете.

    Подключить обработчик очень легко:

    import os
    import cgitb
    cgitb.enable(display=False, logdir=os.path.dirname(__file__))
    

    logdir — директория, куда будет записан html-файл с информацией об ошибке. В данном примере указана директория, в которой расположен исполняемый скрипт. В CGI-скриптах должна быть указана директория, к которой нет публичного доступа.

    При работе с cgitb важно подключать его как можно быстрее, желательно, в начале скрипта. Это поможет обработать ошибки импорта библиотек.

  • Работа с файлами mailbox

    Чем хорош почтовый клиент Mozilla Thunderbird?

    Хотя бы тем, что хранит письма в удобном формате mailbox. Благодрая этому можно рулить почтой программно: считывать содержимое ящика, удалять и добавлять письма.

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

    В составе богатой библиотеки Питона есть модуль mailbox. Небольшой пример:

    import os
    import cgitb
    cgitb.enable(display=False, logdir=os.path.dirname(__file__))
    
    import mailbox
    
    MAILBOX_PATH = "path/to/mailbox.file" # Путь к файлу ящика
    DATA_PATH = "store/path" # Директория для файлов
    
    mbox = mailbox.mbox(MAILBOX_PATH, create=False)
    for i, message in enumerate(mbox):
        attachments = message.get_payload()
        for attachment in attachments:
            filename = attachment.get_filename()
            if filename and filename.endswith(".txt"):
                body = attachment.get_payload(decode=True)
                filepath = os.path.join(DATA_PATH, filename)
                with open(filepath, "w") as f:
                    f.write(body)
        mbox.remove(i)
    mbox.close()
    

    Основной цикл обходит все письма в ящике. Переменная attachments — это список частей письма. Частью может быть всё — простой текст, HTML-сообщение, файловое вложение и т.д.

    get_filename() возвращает имя части. Для файлов оно будет отлично от None. Нас интересуют только текстовые файлы. Тело файла извлекается методом get_payload() части.

    При передаче файла он может быть закодирован методами base64 или uuencode. Чтобы получить исходный файл, передается параметр decode=True. Полученный файл пишется на диск.

    Сообщение удаляется из ящика. По окончании цикла ящик закрывается.

  • Рассылка смс в Питоне

    Отправлять короткие сообщения можно с помощью любого из многочисленных сервисов смс-рассылок.

    Я остановил свой выбор на LittleSMS. Низкая фиксированная цена, удобные API, подробная документация, готовые решения для многих языков и платформ.

    Работа с сервисом осуществляется по протоколу HTTP GET-запросами. При регистрации вы получите API-ключ. Этим ключом подписываются все запросы. Ключ не является паролем к учетной записи. Если ключ где-то засветился, немедленно смените его в личном кабинете сервиса.

    Для Питона рекомендую использовать мою библиотеку с Гитхаба.

    Пример работы:

    import littlesms
    
    # Инициализация класса
    api = littlesms.Api("user", "API_key")
    
    # Проверка баланса.
    print api.balance()
    >>> {u'status': u'success', u'balance': 0.5}
    
    # Отправка сообщения.
    print api.send(u"Hello, World!", "7xxxxxxxxxx")
    >>> {
            u'count': 1,
            u'status': u'success',
            u'recipients': [u'7xxxxxxxxxx'],
            u'price': 0.5,
            u'parts': 1,
            u'test': 0,
            u'balance': 0.5,
            u'messages_id': [u'xxxxxx']
    }
    
    # Отправка сообщения нескольким адресатам с подменой поля «отправитель».
    recipients = ("7xxxxxxxxx1", "7xxxxxxxxx2", "7xxxxxxxxx3")
    print api.send(u"Hello, World!", recipients, sender="Anonym")
    >>> {
            u'count': 1,
            u'status': u'success',
            u'recipients': [u'7xxxxxxxxx1', u'7xxxxxxxxx2', u'7xxxxxxxxx3'],
            u'price': 0.5,
            u'parts': 1,
            u'test': 0,
            u'balance': 0.5, u'messages_id': [u'xxxxxx1', u'xxxxxx2', u'xxxxxx3']
    }
    
    # Если компьютер расположен за прокси со сложной схемой авторизации.
    PROXY = {
        "proxy": "172.27.86.8",
        "port": 3128,
        "user": "ivan",
        "passw": "secret"
    }
    opener = littlesms.curl_opener(**PROXY)
    api = littlesms.Api("user", "API_key", opener=opener)
    
    # Пример работы в облачной платформе App Engine.
    opener = littlesms.gae_opener()
    api = littlesms.Api("user", "API_key", opener=opener)
    
    # Пример обработки исключения.
    try:
        print api.send(u"Hello, World!", "7xxxxxxxxxx", sender="TooLongSender!!!111")
    except littlesms.ApiError, e:
        print e
        >>> Error 7: incorrect sender
    

    Библиотека активно используется больше года в проекте личного кабинета Читинской энергосбытовой компании. На текущий момент отправлено около 15000 сообщений.

Страница 89 из 89