Форум сайта python.su
0
Привет,
Учу py3k, пишу на 3.2.2 скрипт:
В одном файле построчно записаны несколько ссылок, по этим ссылкам скрипт качает файлы во временную директорию, измеряет их размер и пишет во второй файл построчно. В следующие запуски скрипт так же качает и измеряет файлы, и если их размер меняется (читает из записанного в прошлый раз второго файла), то обновлённые файлы копируются в другую директорию, а новые размеры пишутся во второй файл. (Т.е. проверяет, обновились ли файлы, и если да - то кладёт их в отдельное место)
Если в конец первого файла дописать новую ссылку, то скрипт файл этот выкачает и измерит. И вот тут я упёрся лбом в стену - а что если ссылка протухнет, и по ней больше не нужно будет проверять изменения. Тогда её надо удалить из первого файла и желательно тем же скриптом её размер во втором. Пока стараюсь пользоваться только стандартной библиотекой и вижу несколько вариантов решения:
1. Изменять в файле размеров конкретную строку и плясать уже от этого. Но как я понял, writelines() нельзя сказать, типа запиши мне в третью строчку, например
2. Писать в файл что-то типа словарей (но я не совсем представляю, как их потом читать, может как-то хитро парсить)
3. Существуют простые key-value хранилища? В идеале хотелось бы писать что-то типа “id ссылки, краткое описание файла по ссылке, старый размер, новый размер” И соответственно менять последние два пункта. А потом удобно прочитать. Или configparser, но наоборот, с записью в конфигурационный файл.
4. Писать всё в бд (но очень не хочется), ссылок больше пары-тройки десятков не будет.
p.s. классы пока не осилил.
Отредактировано Kane (Апрель 22, 2012 21:49:20)
Офлайн
173
Не совсем понял описание скрипта, но безотносительно алгоритма его работы, отвечу про варианты хранения.
В стандартной библиотеке есть shelve - простое key-value хранилище, значениями могут быть объекты Питона поддерживающие pickle (ссылка ниже).
Также можно сериализовать данные с помощью json или pickle, и писать их в файл самому.
Баз данных тоже боятся не надо, т.к. есть простая реляционная sqlite3.
В документации хранению данных посвящен целый раздел Data Persistence.
Отредактировано reclosedev (Апрель 19, 2012 18:26:02)
Офлайн
0
Собственно, я попытался (видимо не очень хорошо, или очень длинно
) описать его алгоритм, но делает он следующее: я ему подсовываю раздачи на рутрекере, запускаю кроном раз в сутки, а он проверяет, не обновились ли .торрент файлы в раздаче, обновлённые сохраняет в директорию, которую rtorrent мониторит на наличие новых файлов. Таким образом после добавления новой серии в раздачу сериала, она сама и качается.
Код в текущем состоянии могу на пастбин например выложить, если вдруг интересно. Он, наверно, абы как написан, но по тестам работает, нельзя лишь из списка удалять адрес раздачи.
На бд в своём случае искоса гляжу лишь потому, что ради сотни изредка изменяющихся записей, смысла её держать наверно немного. Кстати, изначально именно на sqlite как вариант с бд и смотрел 
В общем, большое спасибо за наводку.
updКажется модули pickle и shelve то, что мне подходит.
Отредактировано Kane (Апрель 19, 2012 19:42:17)
Офлайн
0
Дописал скрипт
#Загрузка обновлённых торрентов с rutracker.org #python 3.2 #обозначаем переменные и пр. import http.cookiejar, urllib.request, urllib.parse torrent_link = 'http://dl.rutracker.org/forum/dl.php?t=' thread_link = 'http://rutracker.org/forum/viewtopic.php?t=' tmp_dir = 'tmp\\' true_dir = 'torr\\' db_file = 'db' rls_file = "releases.txt" list_id = [] # сюда читаем id раздач из файла .txt load = [] # (id, размер), прочитанные из загруженных файлов read = [] # (id, размер), прочитанные из файла бд #load_updated = [] # (id, размер), прочитанные из загруженных файлов и отличающиеся от прочитанных из бд # def log_write(log_message): """Пишем лог""" from datetime import datetime log_time = datetime.now() text_file = open("rutracker_downloader.log", "a") write_message = str(log_time) + " " + str(log_message) + "\n" text_file.write(write_message) text_file.close() def cp_file(torrent_id): '''копируем обновлённый .торрент''' import shutil shutil.copyfile(tmp_dir + '\\' + torrent_id + '.torrent', true_dir + '\\' + torrent_id + '.torrent') print('обновлённый файл', torrent_id + '.torrent скопирован в постоянную директорию') def check_file_size(file_check): '''измеряем размер файла''' import os size_of_file = os.path.getsize(file_check) return size_of_file def releases_read(): '''читаем файл с ссылками на треды''' releases_file = open(rls_file, "r") for releases_text_line in releases_file: #перебираем строки файла strip_url = releases_text_line.strip('http://rutracker.org/forum/viewtopic.php?t=\n') #вычленяем id раздачи list_id.append(strip_url) #пишем id раздач в кортеж releases_file.close() #закрываем файл return list_id def db_read(): '''читаем файл бд''' import shelve, pickle try: db = shelve.open(db_file, 'r') count = 0 read = [] list_db = list(db) while count < len(db): value = list_db[count] read.append((value, db[value][1])) count += 1 return read except: read = [] return read def db_write(torrent_id, torrent_size): '''обновляем файл бд''' import shelve, pickle db = shelve.open(db_file) tmp_list = ['описание', torrent_size] db[torrent_id] = tmp_list db.close() def db_remove(torrent_id): '''удаляем из бд отсутствующие в releases.txt раздачи''' '''обновляем файл бд''' import shelve, pickle db = shelve.open(db_file) del(db[torrent_id]) print('db', torrent_id, 'удалён') db.close() read = db_read() releases_read() #авторизируемся на рутрекере LOGIN = ' ' PASS = ' ' # генерация cookies post_argument = urllib.parse.urlencode({ 'login_username' : LOGIN, 'login_password' : PASS, 'login' : '%C2%F5%EE%E4' }) ###post_argument = post_argument.encode('utf8') post_argument = str.encode(post_argument) # обработка cookies cookie = http.cookiejar.CookieJar() opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie)) # прикидываемся браузером, не обязательно opener.addheaders = [('User-agent', 'Opera/9.80 (Windows NT 6.1; WOW64; U; ru) Presto/2.10.229 Version/11.62')] # коннект urllib.request.install_opener(opener) # авторизация и подсовывание cookies tracker = opener.open('http://login.rutracker.org/forum/login.php', post_argument) ###data = tracker.read() ###print("Подсовываемый линк к рутрекеру:", post_argument) #качаем все .торренты count = 0 while count < len(list_id): torrent = opener.open(torrent_link + list_id[count], post_argument) #генерация линка на .торрент #т.к. рутрекер не отдаёт .торренты по директ линкам, говорим, что пришли с треда torrent.addheaders = [('Referer', thread_link + list_id[count])] data = torrent.read() #путь до файла + его имя f = open(tmp_dir + list_id[count] + '.torrent', 'wb') f.write(data) f.close() file_check = check_file_size(tmp_dir + list_id[count] + '.torrent')#передаём функции измерения размера файла текущий файл load.append((list_id[count], file_check)) #записываем размеры в кортеж print('Файл ' + list_id[count] + '.torrent размером', file_check, 'загружен') count += 1 print(len(load)) print(len(read)) #проверяем удаление ссылок из releases.txt if len(load) < len(read): count = 0 while count < len(read): if read[count] not in load: print(read[count]) db_remove(read[count][0]) count += 1 #копирование обновлённых .torrent elif read == []: count = 0 while count < len(load): print('#если файл бд не открылся, то все .torrent считаются новыми') cp_file(load[count][0]) db_write(load[count][0], load[count][1]) count += 1 #предыдущие условия не выполнились, тогда сравним кортежи else: count = 0 while count < len(load): if load[count] not in read: print('этот обновился, качаем', load[count][0]+'.torrent') cp_file(load[count][0]) db_write(load[count][0], load[count][1]) count += 1

Офлайн
173
KaneНу тогда начнем
Буду рад критике кода

KaneВ Win тоже можно использовать ‘/’ но в названиях директории они вообще ни к чему.
В линуксах бэкслеши нужно заменить на слеши (\ на /): в строках 9, 10, 32
Kanelogging из стандартной библиотеки - FTW!
В коде есть самописный логгер …
def check_file_size(file_check): '''измеряем размер файла''' import os size_of_file = os.path.getsize(file_check) return size_of_file
Склеивать строки - не очень хорошая практика, лучше использовать форматирование или устаревающее:write_message = str(log_time) + " " + str(log_message) + "\n"
'%s %s\n' % (log_time, log_message)
Для работы с путями есть os.path, например:tmp_dir + '\\' + torrent_id + '.torrent', ... ... f = open(tmp_dir + list_id[count] + '.torrent', 'wb')
os.path.join(tmp_dir, '{}.torrent'.format(torrent_id))
KaneА еще можно добавить возможность указывать этот файл в командной строке (sys.argv, argparse, optparse)
rls_file = “releases.txt” Вместо releases.txt можно указать название файла с ссылками на раздачи. Файл должен находиться в папке со скриптом.
Отредактировано reclosedev (Апрель 23, 2012 18:37:14)
Офлайн
0
reclosedevЗамечательно
Ну тогда начнем

reclosedevУ меня почему-то относительный путь без пары слешей на конце не принимался
В Win тоже можно использовать ‘/’ но в названиях директории они вообще ни к чему.
reclosedevя свой “логгер” сначала пользовал, просто чтобы писать в файлик время начала и окончания скрипта писать
logging из стандартной библиотеки - FTW!
reclosedev
Импортировать модули внутри функции - не хорошо. Такое бывает необходимо очень редко. Функция отдельная здесь не нужна (или там что-то еще было?). Достаточно вызывать os.path.getsize(file_check)
reclosedev
А еще можно добавить возможность указывать этот файл в командной строке (sys.argv, argparse, optparse)
Отредактировано Kane (Апрель 24, 2012 20:07:23)
Офлайн
173
KaneЕсли вручную склеивать, то да. С os.path.join() таких проблем нет.
У меня почему-то относительный путь без пары слешей на конце не принимался
KanePEP 8. Imports
Ок, а почему не хорошо?
Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.- Когда импорт сверху, сразу видно какие есть зависимости.
Офлайн
0
Ясно, буду os.path.join() пользовать.
А с configparser подстава прямо как здесь, оказалось в 3.1 и 3.2 конфиги немного по-разному читаются 3.1 ex:
config['bitbucket.org']['User'] = 'hg'
config.get('Section1', 'foo')
Отредактировано Kane (Апрель 24, 2012 22:24:14)
Офлайн