papilio

Записи с меткой «Python»

Получение задач Google Tasks

Давненько я не писал в этот блог, хотя, конечно, не от того, что мне не о чем было писать или я перестал интересоваться web-разработкой и интернет-технологиями. Нет, скорее всего было просто лень :-)

Но чтобы эту лень побороть, надо пользоваться каким-нибудь планировщиком/календарём. А так как я полностью на продуктах Google, то неудивительно, что я пользуюсь Google Calendar (который замечательно синхронизируется как с системой, так и с телефоном) и теперь Google Tasks.

Итак, задача и проблема: мне необходимо вывести мои задачи Google Tasks прямо в консоли (это необходимо, например, чтобы потом повесить этот список задач прямо на мой рабочий стол, где уже есть Google Calendar). Как же это сделать, если пока нет Google Tasks API? Если нет, то нам придётся эмулировать работу браузера, чтобы получить список задач.

Для начала стоит выбрать верную версию сайта Google Tasks среди огромного множества (разные устройства, iGoogle, Gmail и т.п.). Я выбрал мобильную версию, так как можно напрямую обратиться к нужному списку:

  • https://mail.google.com/tasks/m для владельцев Google Accounts
  • https://mail.google.com/tasks/a/domain.com/m для пользователей Google Apps

Сначала я решил воспользоваться просто bash и написать скрипт. Но после того, как я довольно долго с этим промучался, я решил всё-таки писать на python, так как он есть у всех.

В итоге получился скрипт, который я немного прокомментирую чуть ниже (можно скачать отдельным файлом google_tasks.py):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/env python
 
"""
This is a python script for retrieving Google Tasks 
Usage: 
    -e or --email [email] 
        Your Google Account or Google Apps email 
    -p or --password [password] 
        Your password 
    -b or --bullet [bullet] 
        Bullet for tasks list. Default is '*'
 
Example:
    google_tasks.py -e bob@gmail.com -p yaroslavl -b --
 
Thank you Scott Hillman for the implementation of Google Authentication
http://everydayscripting.blogspot.com/2009/10/python-fixes-to-google-login-script.html
 
Evgeny Pavlov, http://evgeny.tel
"""
 
import urllib
import urllib2
import htmllib
import getpass
import re
import sys
import getopt
 
def unescape(text):
    """Removes HTML or XML character references 
       and entities from a text string
 
       From Fredrik Lundh
       http://effbot.org/zone/re-sub.htm#unescape-html
 
       Little bit modified
    """
    def fixup(m):
        text = m.group(0)
        if text[:2] == "&#":
            # Character reference
            try:
                if text[:3] == "&#x":
                    return unichr(int(text[3:-1], 16))
                else:
                    return unichr(int(text[2:-1]))
            except ValueError:
                print "Error with encoding HTML entities"
                pass
        else:
            # Named entity
            text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
        return text # leave as is
    return re.sub("&#?\w+;", fixup, text)
 
def main(argv):
    """ Get arguments: email, password and type of bullet """
 
    bullet = '* '
    email =  ''
    password = ''
 
    try:
        opts, args = getopt.getopt(argv, "he:p:b:", ["help", "email=", "password=", "bullet="]) 
    except getopt.GetoptError:
        usage()
        sys.exit(2)
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit()
        elif opt in ("-e", "--email"):
            email = arg
        elif opt in ("-p", "--password"):
            password = arg
        elif opt in ("-b", "--bullet"):
            bullet = arg    
 
    return (email, password, bullet)
 
def usage():
    """ Help and the list of arguments for this script """
 
    print """This is a python script for retrieving Google Tasks 
Usage: 
    -e or --email [email] 
        Your Google Account or Google Apps email 
    -p or --password [password] 
        Your password 
    -b or --bullet [bullet] 
        Bullet for tasks list. Default is '*'
 
Example:
    google_tasks.py -e bob@gmail.com -p yaroslavl -b --
 
Evgeny Pavlov, http://evgeny.tel"""
 
if __name__ == "__main__":
    # Arguments
    (email, password, bullet) = main(sys.argv[1:])
 
    # Google Account or Google Apps
    email_split = email.split('@')
    try:
        email_domain = email_split[1]
    except:
        print 'Incorrect email address!\n'
        usage()
        sys.exit()
    if email_domain in ('googlemail.com', 'gmail.com', 'google.com'):
        google_apps = 0
    else:
        google_apps = 1
        email = email_split[0]
 
    # Initialization  
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
    urllib2.install_opener(opener)
 
    # Define URLs
    if google_apps:
        login_page_url = 'https://www.google.com/a/%s/ServiceLogin' % email_domain
        auth_url = 'https://www.google.com/a/%s/LoginAction2' % email_domain
        tasks_url = 'https://mail.google.com/tasks/a/%s/m' % email_domain
    else:
        login_page_url = 'https://www.google.com/accounts/ServiceLogin'
        auth_url = 'https://www.google.com/accounts/ServiceLoginAuth'
        tasks_url = 'https://mail.google.com/tasks/m'
 
    # 1. Load login page
    login_page_content = opener.open(login_page_url).read()
 
    # Find GALX value
    galx_match_obj = re.search(r'name="GALX"\s*value="([^"]+)"', login_page_content, re.IGNORECASE)
 
    galx = galx_match_obj.group(1) if galx_match_obj.group(1) is not None else ''
 
    # Set up login credentials
    login_params = urllib.urlencode( {
       'Email' : email,
       'Passwd' : password,
       'continue' : tasks_url,
       'GALX': galx
    })
 
    # 2. Login
    opener.open(auth_url, login_params)
 
    # 3. Open Tasks home page
    tasks_content = opener.open(tasks_url).read()
 
    # Check signing in
    key = re.search('create_tasks', tasks_content)
    if not key:
        print 'Check your credintals!'
        sys.exit()
 
    # Retrieve list ids
    tasks_content_split_obj = re.search(r'<select(.*?)select>', tasks_content, re.IGNORECASE)
    tasks_content_split = tasks_content_split_obj.group(1)
    listids = re.findall(r'[0-9]{20}:[0-9]:[0-9]', tasks_content_split)
 
    # 4. Fetch all lists
    for listid in listids:
        # List content
        list_content = opener.open(tasks_url + "?listid=%s" % listid).read()
 
        # Only tasks remain
        list_content_split_obj = re.search('(.*?)name="numa"', list_content, re.IGNORECASE | re.DOTALL)
        list_content_split = list_content_split_obj.group(1)
 
        # Get Tasks in
<tr></tr>
tasks_in_tr = re.findall(r'
<tr(.*?)tr>', list_content_split, re.IGNORECASE | re.DOTALL)
 
        # Work with this dirty tasks
        for task_in_tr in tasks_in_tr:
            # Retrieve task
            task_obj = re.search(r'
<td class="text">(.*?)</td>
', task_in_tr, re.IGNORECASE)
            task = task_obj.group(1)
 
            # Indent
            indent = len(re.findall(r'
<td class="checkbox"', task_in_tr)) - 1
 
            # HTML entities
            task = unescape(task)
            task = task.strip()
 
            # 5. At last output
            if task  != '': 
                print '  ' * indent, bullet, task

Скрипт выводит список задач Google Tasks в довольно приятном виде.

Как же работает этот скрипт? Всё довольно просто: для того чтобы пройти аутентификацию Google, нужны адрес электронной почты и пароль. При этом Google проверяет, как происходит вход, поэтому каждый раз генерирует специальное значение GALX, которое затем сверяет. Но обо всём (почти обо всём по порядку):

  • 31-56, функция unescape (). Необходима для конвертации т.н. HTML entities, которые присутствуют в мобильной версии Google Tasks. Переводит #&123; в нормальные символы. Реализацию подсмотрел на W3C.
  • 59-80, функция main () нам нужна для того, чтобы прочесть аргументы, переданные в командной строке. Идея из онлайн-книги Dive Into Python.
  • 106-118, определение, каким является аккаунт — Google Accounts или Google Apps. Происходит путём банальной проверки домена.
  • 134-151, аутентификация Google как раз с использованием GALX. Кроме того, необходимо использовать cookies. Идея блога Every Day Scripting. У меня получалось реализовать и с помощью wget. Если кому-то интересно, то могу поделиться.
  • А дальше происходит анализ HTML и поиск задач.

Чтобы воспользоваться скриптом, надо его просто положить в нужное вам место, сделать исполняемым (chown +x) и запускать со следущими параметрами:

google_tasks.py -e bob@gmail.com -p yaroslavl

Кроме того, можно менять оформление задач с помощью ключа -b: по-умолчанию стоит звёзточка.

Метки: , , , ,

Google Short Links

Этой осенью Google включил Labs в Apps не только для своей почты, но и вообще во всём сервисе. Теперь там появляются новые интересные сервисы от различных разработчиков. В качестве примера я расскажу о Google Shortlinks.

Чтобы включить что-нибудь из Labs в Google Apps надо сначала, конечно, включить английский (US) интерфейс, а затем перейти по ссылке добавление новых сервисов (на этой же странице можно подключить Google App Engine или, например, недавно там появились контакты):

Добавление нового сервиса в Google Apps

Добавление нового сервиса в Google Apps

Затем нажать на ссылку See more services from Google Labs:

See more services from Google Labs

See more services from Google Labs

А уже там можно выбрать нужный сервис. Их пока всего три: Moderator, Short Links и Code Review:

Google Labs

Google Labs

Итак, мы включаем Short Links, указываем нужный нам адрес, прописываем всё в DNS и… а для чего нужен этот сервис? Это похоже на сервисы, подобные TinyURL: указываем длинную ссылку, а нам дают короткую. В принципе, это и есть полное описание. Кстати, Short Links написан на python и расположен на Google App Engine.

Google Short Links

Google Short Links

Однако, есть ряд преимуществ и существенных отличий:

  • Собственный домен для коротких ссылок.
  • Учёт количества переходов по ссылке (можно использовать для анализа чего-либо).
  • Личные ссылки (для удобства, что-то вроде быстрого набора).

Недавно добавились полезные букмарклеты, а также хэшированные ссылки (не надо самому придумывать короткую ссылку).

Я пользуюсь. Удобная замена TinyURL :)

Метки: , , , , , , , , , ,

SSH через браузер

У нас в университете открыты только 80 и 443 порты, да и вообще «выход в сеть» осуществляется через прокси. Естественно, что почти не идёт речи о подключении к консоли нашего сервера через обычный SSH или закачать файл по протоколу sFTP. Да, я пробовал различные способы: например, туннелирование (хорошо описано в статье Using SSH Tunneling). Однако, у нас и это не помогает.

Возникла идея сделать консоль прямо в браузере. И оказывается, что подобное уже есть: данные программы называются Web-based SSH.

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

Ajaxterm написан на python, поэтому только его по сути и требует для работы. Уже есть в репозитариях многих распространённых систем. Чтобы запустить его, достаточно выполнить следующую команду:

#ajaxterm -d

Что переведёт его в режим демона, прослушивающего порт 8022 (можно изменить при запуске). Но — в целях безопасности — он принимает только локальные запросы, что нам, конечно, не подходит.

Стоит отдельно отметить вопрос безопасности данных решений: это не SSH, следовательно по-умолчанию ничего не шифруется. Конечно, с такими программами надо работать, используя только HTTPS.

В качестве сервера HTTPS я выбрал lighttpd и пропустил ajaxterm как прокси:

 $HTTP["host"] == "term.yourhost.ru" {
        proxy.server = ( "" =>
                (
                 ( "host" => "127.0.0.1",
                   "port" => 8022
                 )
                )
        )
 }

— это я разместил в секции настройки HTTPS. После этого я смог заходить на специальную достаточно хорошо защищённую страницу с любого места.

Ajaxterm

Ajaxterm

Что же предлагает Ajaxterm и удобно ли с ним работать? Да, удобно, а предлагает практически полноценную консоль (работает автодополнение по Tab, есть подсветка), так как всё реализовано с использованием AJAX. В некоторых браузерах может не работать вставка и копирование (например, в Firefox), но если же нужно ввести какие-то специфичные пароли, то я использую KeePass с автонабором.

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

Метки: , , , , , , , , , , ,

NetBeans и UTF-8

Решил я поредактировать проект на python в NetBeans, а все комментарии на русском языке вылезли иероглифами: какие-то проблемы с кодировкой, а кодировка должна быть UTF-8.

Я потом прочитал, что по-умолчанию NetBeans не ставит кодировку UTF-8 (интересно, почему?). Но, покопавшись в настройках, я не нашёл пункта её смены. Оказалось, что нужно отредактировать файл etc/netbeans.conf, который расположен в директории программы. Необходимо добавить -J-Dfile.encoding=UTF-8 к строчке netbeans_default_options:

# Options used by NetBeans launcher by default, can be overridden by explicit
# command line switches:
netbeans_default_options="-J-client -J-Xverify:none -J-Xss2m -J-Xms32m -J-XX:PermSize=32m -J-XX:MaxPermSize=200m -J-Dapple.laf.useScreenMenuBar=true -J-Dsun.java2d.noddraw=true -J-Dfile.encoding=UTF-8"

— после этого всё работает.

Метки: , ,

Google App Engine открыт теперь для всех

Блог Google System сообщил о том, что теперь Google App Engine (GAE) открыт для всех разработчиков. GAE позволяет создавать практически любые web-приложения с лёгким масштабированием.

Google App Engine

Бесплатно Google предоставляет 500 Mb свободного места и процессорной мощности на пять миллионов просмотров страниц в месяц. Скоро (или уже?) появятся и платные услуги:

$0.10 — $0.12 per CPU core-hour
$0.15 — $0.18 per GB-month of storage
$0.11 — $0.13 per GB outgoing bandwidth
$0.09 — $0.11 per GB incoming bandwidth

— что очень похоже на Amazon, о котором я недавно писал.

Я уже активировал себе GAE. Большой плюс для тех, кто пользуется Google Apps (Службы Google): можно привязать GAE к своему домену.

Google App Engine in Google Apps

Я уже придумал, что сделаю — это, скорее всего, будет что-то связанное с проектом Face Detection. Писать же пока можно только на питоне, который я совершенно не знаю, но от этого ещё более интересно что-то делать.

Можно использовать и фреймворк Django. Нашёл интересные статьи для быстрого старта:

Хоть Google и обещает сделать поддержку и других языков, но чем питон плох? Буду изучать!

Метки: , , , , , ,