• Степень схожести строк (коэффициент Танимото)

    Иногда нужно определить степень схожести двух строк, т.е., насколько одна строка похожа на другую. Например, у вас есть список названий фильмов. Источники разные, нет никаких уникальных идентификаторов, только заголовки. При этом стоит учесть, что даже самые простые названия могут быть написаны по-разному: «ЗАЛОЖНИЦА 2» и Заложница 2. Очевидно, что сравнение этих строк в лоб ничего не даст: лишние символы, разный регистр.

    Строки можно рассматривать как множество символов. Две строки — два множества. Коэффициент Танимото позволяет определить степень схожести двух множеств:

    screenshot

    , где:

    • k — сам коэффициент от 0 до 1;
    • a — количество элементов в первом множестве;
    • b — количество элементов во втором множестве;
    • c — количество общих элементов в двух множествах.

    Изящная функция на Питоне:

    def tanimoto(s1, s2):
        a, b, c = len(s1), len(s2), 0.0
    
        for sym in s1:
            if sym in s2:
                c += 1
    
        return c / (a + b - c)
    

    Пара примеров:

    print tanimoto('Иван Гришаев', 'Гришаев И.В.')
    0.846153846154
    
    print tanimoto('tanimoto test 1', 'testing Tanimoto #2')
    0.7
    

    В Питоне работает т.н. утиная типизация, когда важен не тип объекта, а его поля и методы. Нетрудно заметить, что код функции требует от объектов методов длины, вхождения элемента и итерации. Это значит, что вместо строк можно передавать списки, кортежи, словари и другие структуры:

    print tanimoto(
        (1, 2, 3, 4, 5, 4),
        (4, 2, 5, 4, 9, 3),
    )
    0.714285714286
    

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

    Возвращаясь к нашему примеру:

    print tanimoto(
        u'«ЗАЛОЖНИЦА 2»'.lower(),
        u'Заложница 2'.lower(),
    )
    0.846153846154
    

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

  • Работа с DBF в Питоне

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

    Для работы с файлами DBF в Питоне потребуется библиотека dbfpy. Это, кстати, не единственное решение, есть еще библиотеки pyDBF и dbf. Сам я работал только с первой из трех, функционал полность устраивает.

    Приведу пару примеров: чтение и запись файла.

    from dbfpy import dbf
    
    # Открываем имеющийся файл.
    db = dbf.Dbf("dbfile.dbf")
    
    # Обход всех записей.
    for rec in db:
        print rec
    
    # Извлечение записи по номеру.
    rec = db[42]
    
    # Значения полей извлекаются по их именам.
    value = rec["VALUE"]
    account = rec["ACCOUNT"]
    date = rec["DATE"]
    
    # Всегда закрывайте файл.
    db.close()
    

    Запись нового файла немного сложнее, так как потребуется указать схему данных. Фрагмент кода из реального проекта:

    from dbfpy import dbf
    
    # Определяем набор полей файла. C — строка, N — число, D — дата, L — булево.
    # Для строк нужно указать длину, для чисел — количество разрядов целой и дробной частей.
    SCHEMA = (
        ("PSU",        "C", 64   ),
        ("TSO",        "C", 64   ),
        ("PAYMENT",    "N", 16, 0),
        ("SERVICE",    "N",  8, 0),
        ("DATE",       "D"       ),
        ("MRO",        "C", 64   ),
        ("TERMINAL",   "N",  8, 0),
        ("ACCOUNT",    "C", 16   ),
        ("SUMMARY",    "N", 16, 2),
        ("OPERATOR",   "N",  8, 0),
        ("SUPPLIER",   "N",  8, 0),
        ("TIME",       "C",  8   ),
        ("LAST_NAME",  "C", 64   ),
        ("FIRST_NAME", "C", 64   ),
        ("MIDDLE_NAM", "C", 64   ),
    )
    
    db = dbf.Dbf("dbfile.dbf", new=True)
    db.addField(*SCHEMA)
    
    # Обходим в цикле данные, которые необходимо записать в DBF.
    # В данном случае payments — объект QuerySet из Джанго.
    # На каждом шаге цикла создается новая запись и заполняются ее поля.
    for payment in payments:
        rec = db.newRecord()
        rec["ACCOUNT"] = payment.account
        rec["FIRST_NAME"] = payment.first_name.encode("cp866")
        rec["date"] = payment.date
    
        # Заполняем остальные поля...
    
        # Сохранение записи.
        rec.store()
    
    db.close()
    

    Готово, файл создан.

    Наверняка вы обратили внимание на кодирование поля payment.first_name. Это unocode-строка, и для того, чтобы записать ее в файл, необходимо сперва преобразовать в обычную строку в какой-то кодировке. Исторически сложилось так, что основной кодировкой для этого формата является OEM, она же DOS, она же cp866. Многие программы, например, офисные Эксель и Акцесс работают с DBF-файлами именно в этой кодировке. Чтобы созданные вами файлы читались в сторонних программах, преобразовывайте перед записью строки с кириллицей в cp866.

    Работать с DBF можно как с SQL-базой через драйвер ODBC. При этом именем таблицы является имя файла. Доступны все CRUD-операции на записями. Склеить два DBF-файла оператором JOIN, к сожалению, нельзя. Для работы с ODBC-источниками скачайте библиотеку pyodbc.

    import pyodbc
    
    # Строка, определяющая источник ODBC. DefaultDir — директория с DBF-файлом.
    s = "Driver={Microsoft dBASE Driver (*.dbf)};DefaultDir=d:\\temp"
    
    db = pyodbc.connect(s, autocommit=True)
    cursor = db.cursor()
    
    # Пример выборки.
    cursor.execute('''
    SELECT
        value,
        account,
        date
    FROM
        dbfile
    WHERE
        processed = 'false'
    ''')
    
    # Извлечение записи.
    row = cursor.fetchone()
    if row:
        print row
    
    # Закрываем курсор и ресурс.
    cursor.close()
    db.close()
    

    Вставка, удаление и обновление записей реализуются аналогично согласно SQL-синтаксису:

    cursor.execute('''
    DELETE FROM
        dbfile
    WHERE
        processed = 'false'
    ''')
    
    cursor.execute('''
    UPDATE
        dbfile
    SET
        account = 'test',
        value = 42
    WHERE
        id = 1
    ''')
    

    Какой способ работы с DBF задействовать в проекте — решать вам. dbfpy не требует сторонних зависимостей, написана на чистом Питоне, может работать в сервисах типа App Engine, однако, не столь быстра, как pyodbc. Последняя, напротив, быстрее, но зависит от платформы и иногда глючит на Винде (у меня были проблемы с ошибками в DLL-библиотеках). Для небольших проектов рекомендую использовать dbfpy, в промышленных масштабах — pyodbc.

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

    04/08/14 Городецкий: привет! получаю invalid syntax при from dbfpy import dbf. Подскажи пожалуйста, что не так?

    04/09/14 Иван Гришаев: Точка в конце не нужна. Или какая-то из букв кириллическая. Трейс должен показывать стрелочкой, где косяк.

    01/09/15 Ole: Отличный пост

    09/02/15 Дамир Аманов: в pyodbc как работать с кириллицой? в windows

    09/03/15 Иван Гришаев: Насколько помню, кодировать строки в cp2151, если не получается с юникодом.

  • Замечательный движок Эгея

    Этот блог работает на движке Эгея. Я узнал о нем из блога Ильи Бирмана (автора проекта). Поначалу мое отношение было скептическим — мало ли сейчас различных движков? Решив вести собственный блог, я перебрал несколько движков и КМС, и вышло так, что только Эгея не только удовлетворила все мои требования, но и приятно удивила в дальнейшем.

    Почему я не использовал Друпал? Его долго собирать. Это не готовая КМС, а конструктор. Установив Друпал, приходится еще полдня ставить модули и лазить в настройках. Эгея же просто работает. В ней нет хлама вроде опросов, древовидных тегов, визуального редактора, архива за прошлые года. Она минималистична и быстра.

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

    Пока что я вижу в движке два недостатка: вики-разметка и кодировка cp1251.

    Использование разметки вместо обычного ХТМЛ является правильным решением: набирать теги руками замучаешься, а визуальные редакторы выдают отвратительный код. Вот только Илья не угадал с разметкой, лучше было бы взять не вики, а маркдаун, он проще и удобнее. Впрочем, к вики быстро привыкаешь.

    Кодировка cp1251 устарела и отовсюду вытесняется УТФ, так как не позволяет использовать символы юникода и не дружит со многими языками. Эгею нужно перевести на УТФ обязательно, cp1251 — преграда на пути проекта к широкой аудитории.

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

    Эгея легко поддается темизации, написать свою тему для нее нетрудно. Движок поддерживает пользовательские блоки — это ПХП-файлики, содержимое которых выводится в определенное место, например, перед статьей, после статьи, в сайдбаре, в подвале и т.д. Мне понадобилось всего три таких файла — сайдбар с меню, область после статьи для социальных кнопок и подвал для джаваскрипта (метрика, подствека кода).

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

  • Мысли о Друпале

    Друпал (анг. Drupal) — система управления содержимым, написанная на ПХП Дрисом Бёйтартом. Вместе с Вордпрессом и Джумлой входит в большую тройку КМС.

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

    В этом посте я хочу выразить некоторые мысли, которые сформировались за долгое время работы с Друпалом. Это отдельные тезисы с краткими пояснениями.

    Гибкость системы. Главным достоинством Дупала является его гибкость. Не секрет, что любая КМС в той или иной степени ограничивает разработчика. Дрис и команда разработали систему модулей и хуков. Она позволяет менять поведение Друпала без правок ядра.

    Хуки — именованные функции. За хакерским словечком «хук» скрывается банальная вещь — функция, имя которой удовлетворяет шаблону <имя модуля>_<имя хука>. Эти функции должны располагаться в теле модуля. Друпал опрашивает модули на предмет наличия этих функций и вызывает их. При этом функция-хук должна либо вернуть какие-то данные, либо изменить те, что были переданы ей по ссылке. Хуков много, вызываются они в разное время. Некоторые модули могут определять собственные хуки.

    Множество модулей. Система модулей и хуков понравилась разработчикам. В настоящее время число всех модулей оценивается в несколько тысяч. В актуальном состоянии поддерживается около тысячи модулей. Написать свой модуль легко — это ПХП-файл с расширением *.module с функциями-хуками внутри. Описание каждого хука с примерами смотрите в документации.

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

    Не делайте из Друпала культ. Начинающим веб-разработчикам кажется, что Друпал способен на все. Это правда, но лишь в теории. Легкость создания больших сайтов без написания кода компенсируется многими уровнями абстракций и чрезмерно сложной логикой. Приступая к новому проекту, спросите себя, так ли нужен здесь Друпал, нельзя ли обойтись чем-либо попроще? Например, вы решили вести блог. Стоит ли качать Друпал, ставить на него и настраивать кучу модулей, если проще взять специализированный движок?

    Любите Друпал не больше других средств разработки. Не зацикливайтесь на Друпале. Постарайтесь освоить несколько технологий, языков, фреймворков, КМС. С разнообразием приходит опыт. Заимствуйте удачные решения из одного проекта в другой. Старайтесь не привязывать удачные решения только к Друпалу. Например, если в модуле есть код рассылки смс, оформите его в отдельном файле и выложите на Гитхаб.

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

    08/19/12 jedi: Согласен со всем, жалко мало написали. Сам перехожу на yii

  • Победителя определяет скорость

    screenshot

    Скорость разработки — это:

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

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

    Для рутинных действий и автоматизации процессов хорошо подходит Питон — скриптовый язык с динамической типизацией. Он поддерживает множество парадигм, позволяет писать код так, как нужно именно вам. Например, если скрипт простой, вы сразу описываете рабочую логику, а не мусор вроде public class Foo и public static void main().

    С Питоном вы становитесь эффективнее. Работа с почтой, разбор и формирование XML, смс-рассылки, перегон данных из одного формата в другой, обработка офисных документов, чтение и запись DBF, COM-автоматизация, взаимодействие с сервисам Гугла, быстрые сайты на Джанго, работа с устройствами по серийному порту и уйма всего другого — это Питон. Плюс кросплатформенность, малый размер дистрибутива, пакетный менеджер (даже два — pip и pypm), масса библиотек на все случаи жизни. Плюс скорость.

    Не бойтесь делегировать больше задач сторонним утилитам. Например, вы программируете сайт, и в какой-то момент требуется ужать и обрезать загруженное пользователем изображение. Или сгенерировать капчу. Или штрихкод. Или QR-код. Или Excel-книгу. Или отправить письмо. Стандартный подход — подключить модуль, просмотреть примеры, написать код. Вы потратили время, код увеличился.

    Быстрый подход — задействовать консольную утилиту. Работа с изображениями — ImageMagick, генерация капчи — он же, отправка письма — sendmail или blat для Винды, работа с Excel — скрипт на VB Script, штрихкод — GNU Barcode, QR-код — Google Charts. И так далее.

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

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

    Не бойтесь внедрять новые технологии. Роман Ворушин внедрил Джанго-систему в банке, где все было написано на Джаве. Иван Сагалаев принес Джанго в Яндекс. Я, не являясь специалистом такого уровня, внедрил Питон и Джанго в организации, где был только PHP и сервера на Винде.

    Скорость очень убедительна.

  • Умирает ли РСС?

    В последнее время в Сети то и дело пишут о том, что формат РСС умирает. Дескать, все продвинутые читают Твиттер и Гугл+, все актуальное и интересное там, а РСС для неудачников.

    Поговорим об этом.

    Чтобы развеять миф, озвученный в первом предложении, необходимо и достаточно ответить на два вопроса:

    1. Какие преимущества есть у РСС-лент?
    2. Что произойдет, если формат действительно умрет, и все сайты мира отключат РСС?

    Преимущества

    Во-первых, РСС — очень простой формат, обычная XML-ка.

    Взгляните на официальный документ — структура совершенно прозрачна. Причем не обязательно соблюдать условности вроде тега <title> — современные приложения в состоянии разобрать даже самые невалидные ленты. Например, сайт slon.ru отдает ленту только с тегами <description>, <link> и <pubDate>, что никак не мешает ее чтению.

    Во-вторых — открытость и минималистичность формата делает работу с ним очень удобной. Построение и парсинг XML — обычная рутинная задача веб-разработчика. Собрал XML и сохранил на диск — приложение пользователя само его заберет и распарсит. Никаких OAuth-танцев, токенов и авторизаций.

    Третий пункт — практически каждый сайтовый движок, CMS, форум может генерировать РСС. Во многих движках есть ленты по отдельным авторам, тегам, комментариям к статьям, разделам форумов и даже поисковым запросам! Это же очень удобно — подписались на двух интересных авторов и читаете только их. Или отслеживаете комментации в интересной статье.

    Четвертый — существет немыслимое количество приложений для чтения лент, для всех операционных систем и платформ. Читать можно в браузере, в десктоп-приложениях, в телефонах, планшетах. Немало сервисов основано на передачи данных из одного источника в другой именно в РСС-формате. Кросспост в Твиттер, Фейсбук и другие социальные сети основан на РСС-лентах. Сервис twitterfeed — абсолютный монополист в этой области — обслуживает на текущий момент свыше шести миллионов лент!

    screenshot

    Пятый — в некоторые сайты помещают в ленту статьи целиком, а не только анонс. Если при этом в статье сохранены все теги, картинки и форматирование, то РСС-читалка становится полноценным браузером. Не нужно открывать сайт, загружать рекламу и баннеры.

    Шестой — продвинутую читалку вроде Google Reader можно использовать как каталог избранных ссылок. На многих сайтах есть возможность помечать статью как избранную или добавлять в закладки (что по сути одно и то же). При этом они хранятся разрозненно: на Хабре — избранное только Хабра и т.д. Однако, если отмечать статьи в едином пространстве (в РСС-читалке), то поиск и просмотр избранных статей станет намного проще.

    Представим, что РСС умер

    Как теперь распростронять информацию с сайта, блога? Через Твиттер. Для того, чтобы организовать кросспост, владельцу сайта необходимо выполнить следующие шаги:

    1. зарегистрировать в Твиттере новое приложение;
    2. найти модуль для своей CMS или библиотеку для языка/платформы;
    3. все настроить, запустить, протестировать.

    Хорошо, если сайт работает на какой-нибудь CMS вроде Дупала, Вордпресса или Джумлы. Для них модули уже есть. А как быть владельцам малораспространенных CMS или тем, чьи сайты работают на фреймворках или самописных движках? Уверены, что сможете все организовать сами? Действительно ли это проще, чем добавить ленту в twitterfeed?

    Далее, при кросспосте нужно сокращать ссылки. Кто будет этим заниматься? Теперь ответственность лежит на вас. Регистрируемся в [Bit.ly]((http://Bit.ly), получаем открытый и закрытый ключи, качаем библиотеку для нужного языка, проходим OAuth-авторизацию и //только теперь// можем приступить к сокращению ссылок! Аналогично с Goo.gl: сперва включаем сервис в консоли API, получаем ключи, качаем библиотеки, читаем доки, авторизуемся и сокращаем. Уверяю, не все так просто. Тонкости работы с goo.gl я подробно описывал на Хабре.

    Исходя из вышесказанного, можно с уверенностью предсказать возникновение сервисов, которые возьмут волокиту на себя. От нас только потребуется передавать информацию в удобном виде — вот заголовок, вот тело статьи, вот ссылка на оригинал. Формат, вероятно, XML — он лучше подходит для структурированных данных. И к чему мы пришли? Не к тому ли, что имеем — РСС и twitterfeed?

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

    И последний довод. Всю информацию, поступающую в Твиттер, можно разделить на два типа. Первый тип — то, что написано живыми людьми, неважно, при помощи какого приложения. Написал ли человек твит прямо на сайте, или отправил с телефона, или с Инстаграма — это первый тип. Такая информация отражает актуальные действия и мысли человека, т.е. прямо отвечает на вопрос-слоган «Что ты делаешь сейчас»?

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

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

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

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

    05/21/13 sheber: Отличный пост, спасибо. Кстати, Google Reader закрывается http://googlereader.blogspot.com/2013/03/powering-down-google-reader.html

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

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

    Ответ — подключить библиотеку 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. Полученный файл пишется на диск.

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

Страница 50 из 51