Введение
Был полезный пост "Автоматизация закачки подкастов на mp3 плеер". Полезный для меня, поскольку ну не использую iTunes и прочий подобный софт (я не хочу это обсуждать :). Мне только нужно скачать пачку подкастов, которые периодически собираются в ленте ридера. И PHPу предпочитаю Python.
Хотелось бы услышать советы — я только изучаю Python. И мне нравится писать посты с примерами для начинающих. Замечаний бы, критики… Но к делу.
Организация процесса
Список подкастерских лент я храню в Google Reader. Ленты помечены своим тегом, и аккуратно лежат в своей папке:Для выкачивания новых подкастов, попавших в папку Podcasts, написал небольшой скрипт на Python. В качестве основы взял библиотеку />pyrfeed, в которой реализован полезный класс GoogleReader.
Исходный код библиотеки доступен и включает небольшой пример работы с ней. Есть документация. Правда, я нашел документацию только по API к Google Reader, а не работе с самой библиотекой. Там же есть пример утилиты с Gui-интерфейсом для чтения RSS-лент.
Исходный код
Ссылка на архив с исходником в конце.А здесь исходник главного скрипта:
import sys import os import time import urlparse import urllib import progressBar import GoogleReader downloadDir = "myDownloadDir"; logFile = downloadDir + "PodcastsDownloadTool.log"; tag = "Podcasts"; login = "myGoogleReaderLogin"; password = "myGoogleReaderPassword"; def GetLocalFileNameFromURL(fullpath): (filepath, filename) = os.path.split(urlparse.urlparse(fullpath).path) return downloadDir + filename def LogMessage(message): f = open(logFile,"a") print >> f, message; f.close(); pass def DownloadFile(url, filename): progressBar.ResetProgressBar(); urllib.urlretrieve(url, filename, reporthook = progressBar.ProgressBarReportHook); pass def ProcessPodcastDownloading(): # Check and create dir if not os.path.exists(downloadDir): os.mkdir(downloadDir); # Login to Google Reader gr = GoogleReader.GoogleReader(); gr.identify(login, password); if gr.login(): print "Login OK"; else: print "Login KO"; return xmlfeed = gr.get_feed(feed = "user/-/label/%s" % tag, n = 17, xt = "user/-/state/com.google/read"); for entry in xmlfeed.get_entries(): try: googleID = entry['google_id']; if entry.has_key('enclosure'): # Prepare vars and print info URLToDownload = entry['enclosure']; localFilePath = GetLocalFileNameFromURL(URLToDownload); print "Title: %s" % entry['title']; print "Download from URL: %s ..." % URLToDownload; print "Local file: %s" % localFilePath; # Download file DownloadFile(url=URLToDownload,filename=localFilePath) # Log message LogMessage("%s %s %s %s\n" % (time.strftime('%x %X'), URLToDownload, googleID, entry['published'])); print "Downloaded."; # Mark as readed gr.set_read(googleID); print "Marked."; except: #Print and log error print "Error: ", sys.exc_info(); LogMessage("%s\nError: %s\nEntry: %s\nException info: %s\n%s\n" % ("="*80, time.strftime('%x %X'), entry, sys.exc_info(), "="*80)); pass if __name__=='__main__' : ProcessPodcastDownloading();
Пояснения к коду
Основные параметры задаются в начале скрипта:- downloadDir – каталог, куда будут скачиваться подкасты
- logFile – лог файл
- tag – имя тега/папки в Google Reader, в которой будут просматриваться ленты
- login и password – логин и пароль в Google Reader
А дальше ничего сложного:
- аутентификация в сервисе Google Reader
- получение распарсенной RSS-ленты
- цикл по записям с выводом информации и записью в лог
- собственно, скачивание файлов
- метка записей прочитанными
Саму библиотеку pyrfeed в приложение не включаю. Её достаточно скачать, внести пару строчек (о которых далее), и положить в допустимое для import’а место. Например, в директорию Lib каталога, куда установлен Python — тогда библиотека станет доступна всем скриптам.
У меня каталоги GoogleReader и web располагаются в той же директории, где находится мой скрипт.
Интерфейс
Это консольная утилита. Делайте выводы.Просто отображение процесса загрузки выглядит так:
Прогресс-бар взят из какого-то примера. Точно не помню откуда. Примеров в интернете много и большинство друг на друга похожи. Исходник есть в приложении.
Патч для feed.py
К сожалению, класс GoogleFeed не извлекает из полученного XML ссылку на файл для скачивания.Я решил эту проблему, добавив к парсингу XML после такого фрагмента:
elif dom_entry_element.localName == 'link' : if dom_entry_element.getAttribute('rel')=='alternate' : entry['link'] = dom_entry_element.getAttribute('href')
Такой кусочек:
if dom_entry_element.getAttribute('rel')=='enclosure' : entry['enclosure'] = dom_entry_element.getAttribute('href')
Получилось вот так:
elif dom_entry_element.localName == 'link' : if dom_entry_element.getAttribute('rel')=='alternate' : entry['link'] = dom_entry_element.getAttribute('href') if dom_entry_element.getAttribute('rel')=='enclosure' : entry['enclosure'] = dom_entry_element.getAttribute('href') elif dom_entry_element.localName == 'category' :
Недостатки, которые меня устраивают
- Не поддерживается докачка. В случае сбоя RSS-запись не будет помечена как прочитанная. При следующем запуске скрипта файл будет выкачан заново.
- Отсюда следующий момент – при следующем запуске скрипта, ошибочные записи (например, в записи нет ссылки на файл для скачивания, так бывает) будут повторно обрабатываться. Можно бы их метить специальным тегом и пропускать в дальнейшем. Но меня устраивает последующий ручной просмотр ленты на предмет не прочитанных записей.
- Хранение конфигурации – логин и пароль в открытом виде. Логин еще не так страшно, а вот пароль… Можно использовать функцию getpass() или хранить его в другом месте.
Можно автоматизировать запуск скрипта при подключении USB-флешки или плеера, например при помощи утилиты USB Detect & Launch (о нем уже было сказано на хабре).
Финиш
И Исходники в архиве.А заметку скопипастил со своей страницы.