le087 10.01.2011 22:54

СкриптыМой почти первый и совсем кривой велосипед на Python

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

Ну и так, раз уж недавно взялся за изучение Python (ибо так нам проповедывал Эрик Реймонд; по ссылке он вам расскажет, откуда в сыре дырки =), и почему именно стоит брать например python, как первый язык программирования), то и решил написать маленький скрипт на этом замечательном языке.

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

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

 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
# -*- coding: utf-8 -*-

#Импортируем необходимые модули
import os #требуется для работы с файлами
import shutil #для того же
import string #работа со строками
import urllib #библиотека для работы с web
import re #бибилиотека для работы с регулярными выражениями


web_str = urllib.urlopen("http://site-podcasta.com") #получаем странички из интернета
filetext = web_str.read() #создаем из странички объект, с которым можно будет после работать с использованием рег.выражения
pattern = r'http\:\/\/site-podcast\.com\/downloads\/file\_podcast<0-9>+\.mp3' #это наша регулярное выражение. Особое внимание стоит уделить на r' после знака "="
podcast_re = re.compile(pattern) #компилируем наше регулярное выражение, нафиг надо, непонятно, но умные люди говорят, что это надо сделать до того, как пойдут циклы
podcast_list = podcast_re.findall(filetext) #ищем в объекте filetext строчки, которые нам нужны, в данном случае в список собираются все совпадения с регулярным выражением

url = podcast_list<0> #так мы получаем из списка адрес нашего файла, как правило он будет первым, потому что последний пост всегда находиться наверху, а кроме новых подкастов, на нашем сайте других записей почти и не бывает

print "Качаю файл ", url

#качаем подкаст с инета, способ этот честно слизал с какого-то сайта
webFile = urllib.urlopen(url) #качаем файл
localFile = open(url.split('/')<-1>, 'w') #вот тут, я не совсем сообразил что делается, но кажется тут обрезается имя файла от его урла до нормального вида
localFile.write(webFile.read()) #записываем скаченное в файл на жестком диске
webFile.close() #наверно, это у программистов называется освободить память
localFile.close() #дубль два

spisok_files = os.listdir(".") #получаем список файла в директории, из которой запускался скрипт, что бы затем этот файл перекинуть в нужный нам каталог

print "Перемещение файла в каталог подкастов"
for i in spisok_files: #цикл перебирает список файлов, и находит тот, который нам нужен
if 'file_podcast' in i:
shutil.move(i, "/home/user/Music/podcasts/") #перемещаем файл в указанный каталог

#объявляем переменные
numsp =
#список номеров подкастов
podcast = 0 #последний номер подкаста
podcast_up = '/media/disk/music/podcast'#путь до каталога копирования, а именно в плеер, который монтируется автоматом, как обычная флешка и имеет метку "disk"

list_files = os.listdir("/home/user/music/podcasts/") #получаем список файлов в директории, где хранятся подкасты


#имена подкастов имеют примерный вид file_podcastXXX.mp3, где XXX - цифры с каждым новым подкастом просто увеличиваются на единицу
#обрабатываем имя файла, оставляя только числовую часть, преобразуем ее в целый тип. Таким образом я выбираю только самый новый подкаст, по его порядковому номеру
for el_sp in list_files:
if 'file_podcast' in el_sp:
num = int(el_sp<10:-4>) #выбираем номер из имени файла
numsp.append(num) #добавляем этот номер в список для дальнейшей сортировки

#ищем наибольшее значение в списке, присваеваем это значение переменной podcast
for el_sp2 in numsp:
if el_sp2 &gt; podcast:
podcast = el_sp2
print 'Последний скаченный выпуск:',podcast

path_podc = '/home/user/music/podcasts/'+'file_podcast'+str(podcast)+'.mp3' #формируем путь до файла для дальнейшего копирования

#копирование подкаста на плеер, с проверкой на существование каталога disk, если он есть, значит плеер примонтирован
if os.path.exists("/media/disk"):
print "Плеер подключен"
print "Внимание! Не отсоединяйте плеер до окончания копирования"
shutil.copy(path_podc, podcast_up) #операция копирования
print "Копирование завершено!"
else:
print "Включите плеер и попробуйте снова"



Скрипт, как обычно, сохраняется в файл с расширением .py и запускается командой:
1
$python script.py



Теперь давайте рассмотрим потенциальные проблемы и темные моменты, которые мне уже подсказали ребата из welinux@conference.jabber.ru (огромное им спасибо), а так же те, до коих я дошел сам, пока писал этот пост.

1. Мне кажется, или все же, если при скачивании по ссылке файл окажется 8.5 гигабайтным образом двухслойного DVD-диска, то оперативная память и своп быстро кончаться?
1
webFile = urllib.urlopen(url)


2. Можно было вполне организовать проверку полученных ссылок на выбор самого последнего выложенного подкаста. И сразу же возникает вопрос, а вообще возможно, не скачивая файл, через протокол http определить дату создания этого файла?

3. Также наверное стоит переписать момент, где файл сначала сохраняется на жестком диске, в том месте, откуда был запущен скрипт, а затем перемещается с помощью shutil.move() в нужный каталог. Согласен, не достаточно хорошо изучил urllib, просто я еще учусь. Правильным делом будет сразу сохранять его в нужном месте.

4. Ребята из конференции мне уже подсказали, что Rhythmbox умеет все, что я тут наизобретал, прямо-таки из коробки. Но я вот думаю повесить этот скрипт в обязанности cron'у, и будет мне сачстье =)

Ну вот и все. Все желающие можете кидать в меня камни, а я постараюсь увернуться и обязательно прислушаюсь к вашим предложениям.


Тэги: python shutil urllib велосипед
+ 7 -
Похожие Поделиться

wiz 10.01.2011 23:04 #
Я бы дёргал всё нужное из rss/atom лент, благо там данные специально подготовлены для парсеров.
le087 10.01.2011 23:10 #
Rhythmbox вроде именно так и поступает. Но вот я об этом не подумал. =) отличная мысль.
wiz 10.01.2011 23:11 #
Дабы не городить ещё один велик, возьми сразу feedparser
le087 10.01.2011 23:15 #
Ух ты, прикольно. Этой штуке место самое в моих заметках...
Дабы не городить ещё один велик...

Все же пару шишек набить иногда полезно!
derfenix 10.01.2011 23:12 #
Особо не вчитывался, но есть небольшое замечание по стилистике, для упрощения написания и чтения кода
web_str = urllib.urlopen("http://site-podcasta.com")
filetext = web_str.read()
лишние переменные и лишний текст. лучше делать так
filetext = urllib.urlopen("http://site-podcasta.com").read()
Особенно, если нужен один только read().Ты завёл web_str и воспользовался этой переменной лишь раз, добавив к ней всего один вызов метода.
le087 10.01.2011 23:21 #
Так, с этим теперь ясно. В масштабах небольшого проекта, я так понимаю, это сэкономит нам немного ресурсов компьютера. В следующий раз постараюсь обосновывать создание объекта.
derfenix 10.01.2011 23:25 #
это с опытом придёт :) просто после написания кода, просматривай его и ищи где и что можно упростить или сократить. Иногда полчаса вдумчивого чтения помогают из 100 строк сделать 50 ;) ну а потом сразу в эти же 50 строк будешь умещать %)
и тут дело не только в производительности... через пару месяцев будет проще разобраться в коде, если там нет лишних конструкций и переменных.
krig 10.01.2011 23:37 #
localFile = open(url.split('/')<-1>, 'w') #вот тут, я не совсем сообразил что делается, но кажется тут обрезается имя файла от его урла до нормального вида

Тут урл бьется на куски с разделителем '/', в результате получаем список (или кортеж, не помню точно), из которого берем первый элемент с конца (т.е. самый последний). Отрицательный индекс говорит о том, что счет идет с конца.

webFile.close() #наверно, это у программистов называется освободить память

В данном случае это называется "закрыть поток". С файлом то же самое. А открывал ты его тут:

webFile = urllib.urlopen(url) #качаем файл

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

/home/user/music/podcasts/


Этот кусок строки у тебя повторяется много раз. Поэтому лучше вынеси его "константой" в шапку скрипта, и в дальнейшем использовать ее в скрипте. Например как-то так:

PODCAST_PATH = '/home/user/music/podcasts/'

Вроде все, что разглядел в скрипте из того, что можно поправить или дсказать =)
le087 10.01.2011 23:45 #
...качать ты начинаешь только тогда, когда читаешь из потока.

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

Об этом я тоже думал, потому как ждать, пока скачается аж 70-80 мб, ну не прикольно со всем =). Это будет домашним заданием.

Благодарю за ценный коммент.
booley 11.01.2011 02:43 #
он вам расскажет, откуда в сыре дырки =)

Так откуда же?
le087 11.01.2011 07:50 #
По ссылке находится статья Эрика Реймонда, "Как стать хакером". В этой статье он объясняет, кто эти хакеры на самом деле, и как ими стать. Это целое руководство к действию.

В отношении языков программирования, он предлагает изучить четыре. Первым из них должен быть Python, как простой в понимании и написании, но все же очень мощный. Следующим должен идти Си, потому что это язык операционной системы UNIX (по его мнению, у начинающего компьютерщика обязательно должен быть дома один из UNIX'ов), заканчивают список Perl и Lisp. Вот например Lisp мне тоже хочется поковырять, причина в emacs, который я последнее время использую все чаще. Статья очень давняя, но в большинстве своем не потеряла актуальности. Рекомендую к прочтению всем начинающим IT-специалистам.
dr_lo 11.01.2011 03:01 #
filename = url.split('/')<-1>
shutil.move(filename, "/home/user/Music/podcasts/")

# И вот этого можно не делать:spisok_files = os.listdir(".") #получаем список файла в директории, из которой запускался скрипт, что бы затем этот файл перекинуть в нужный нам каталог

print "Перемещение файла в каталог подкастов"
for i in spisok_files: #цикл перебирает список файлов, и находит тот, который нам нужен
if 'file_podcast' in i:
shutil.move(i, "/home/user/Music/podcasts/") #перемещаем файл в указанный каталог
dr_lo 11.01.2011 03:10 #
PS вы бы поделились с сообществом адресом подкаста, а? :-)
le087 11.01.2011 07:24 #
Просто подкаст давно всем известен, и я не стал лишний раз его упоминать. Вот ссылочка:
http://radio-t.com

Я его слушаю, аж с ноября 2007 года. Ведущие, вполне достойны внимания многих с welinux, но это мое, и только мое мнение. Если кто еще не слушал, очень рекомендую попробовать.
le087 11.01.2011 07:30 #
Черт, все гениальное просто! =) Многие вещи ООП в python у меня еще не осели на стенки головного мозга. Еще одна прекрасная оптимизация. Обязательно добавлю, вернее убавлю лишние строчки.
dr_lo 11.01.2011 15:47 #
да нет тут никакого ооп =)

спасибо на ссылку на подкаст
ladykosha 11.01.2011 08:32 #
Спасибо за ссылку на "проповедь" Эрика Рэймонда. :)
le087 11.01.2011 09:32 #
Только не увлекитесь ;). А то вон RMS тоже последнее время вызывает у широкой общественности, периодически, сомнения в его адекватности. Хотя ему простительно, чувак реально много сделал для Open Source, простите, для Free Software.
ladykosha 11.01.2011 10:05 #
:) Ну, текст замечательно увязывает мои интересы. :)

Подозреваю, что его самого эти сомнения довольно мало волнуют. :)
Raiden 12.01.2011 05:19 #
Это чем-то лучше , чем скриптинг с использованием шелла , седа, курла, вгета?
Имхо для таких задач не нужно изучать специально питон, можно использоват ьсредства которые используются в повседневной жизни в линукс (включая те что я перечислил). Если не прав - простите.

ps. Однажды Мастер Фу сказал заезжему программисту: "В одной строке кода shell-сценария больше духа UNIX, чем в десяти тысячах строк на языке С!"
uscr 12.01.2011 14:57 #
Ну а как ещё учить языки? Сразу начинать писать базу данных?
Raiden 12.01.2011 17:41 #
Хм, тогда беру свои слова обратно, если это цель. Хотя, если чесно, цель не очень понятная - прикладной софт на питоне совсем не то что на си. Говоря короче - тормоз заметный на глаз. Для автоматизации можно поучить... Но в общем-то для этого и шелла хватает. В общем я не вижу радости от того, что софта на питоне может стать больше :) И мне как юзеру скорость разработки не важна - только на сколько хорошо работает программа (включая скорость).

ps. Какой-то демотиватор из меня получился :)



le087 12.01.2011 19:35 #
Все правильно =). Я просто пытаюсь постичь основы программирования, решая попутно возникающие проблемы с помощью этого же программирования. Python прежде всего потому, что он легко читается, более прост в понимании. Уясню фундаментальные основы, начну чувствовать себя уютно среди строк программного кода, займусь следующим, например тем же Си. Это просто мое любопытство и желание разобраться.
leonike 12.01.2011 18:53 #
А еще есть метод urllib.urlretrieve(url, fileName).
Предпочтительнее использовать его. ЕМНИП, когда качаешь файл с помощью urlopen, то сначала все данные поступают в оперативку, а потом вы самостоятельно записываете на жесткий диск данные и "очищаете" оперативку. urlretrieve регулярно "сбрасывает" полученные данные в файл, не занимая место в оперативной памяти.
Да и телодвиженийстрок кода меньше :)
le087 12.01.2011 19:36 #
Вот это очень важный момент. Значит все же, если я буду качать так 8.5 гигабайт, то у меня кончится оперативная память. Или не кончиться? =) Вашим советом обязательно воспользуюсь.
leonike 12.01.2011 21:44 #
кончится, с urlretrieve такого не случится
le087 13.01.2011 20:22 #
Хм.. по совет philosoft сделал файл на 1 гиг, и попробовал закачать его тем методом, что у меня в скрипте.. Сожрало 2 гига памяти, всю свободную оперативную, а остатки допбило свапом. Ваш работает отлично =). Спасибо.