Найти - Пользователи
Полная версия: Загрузка обновленных .torrent с rutracker [решено]
Начало » Python для новичков » Загрузка обновленных .torrent с rutracker [решено]
1
Kane
Привет,
Учу py3k, пишу на 3.2.2 скрипт:
В одном файле построчно записаны несколько ссылок, по этим ссылкам скрипт качает файлы во временную директорию, измеряет их размер и пишет во второй файл построчно. В следующие запуски скрипт так же качает и измеряет файлы, и если их размер меняется (читает из записанного в прошлый раз второго файла), то обновлённые файлы копируются в другую директорию, а новые размеры пишутся во второй файл. (Т.е. проверяет, обновились ли файлы, и если да - то кладёт их в отдельное место)
Если в конец первого файла дописать новую ссылку, то скрипт файл этот выкачает и измерит. И вот тут я упёрся лбом в стену - а что если ссылка протухнет, и по ней больше не нужно будет проверять изменения. Тогда её надо удалить из первого файла и желательно тем же скриптом её размер во втором. Пока стараюсь пользоваться только стандартной библиотекой и вижу несколько вариантов решения:

1. Изменять в файле размеров конкретную строку и плясать уже от этого. Но как я понял, writelines() нельзя сказать, типа запиши мне в третью строчку, например
2. Писать в файл что-то типа словарей (но я не совсем представляю, как их потом читать, может как-то хитро парсить)
3. Существуют простые key-value хранилища? В идеале хотелось бы писать что-то типа “id ссылки, краткое описание файла по ссылке, старый размер, новый размер” И соответственно менять последние два пункта. А потом удобно прочитать. Или configparser, но наоборот, с записью в конфигурационный файл.
4. Писать всё в бд (но очень не хочется), ссылок больше пары-тройки десятков не будет.

p.s. классы пока не осилил.
reclosedev
Не совсем понял описание скрипта, но безотносительно алгоритма его работы, отвечу про варианты хранения.

В стандартной библиотеке есть shelve - простое key-value хранилище, значениями могут быть объекты Питона поддерживающие pickle (ссылка ниже).

Также можно сериализовать данные с помощью json или pickle, и писать их в файл самому.

Баз данных тоже боятся не надо, т.к. есть простая реляционная sqlite3.

В документации хранению данных посвящен целый раздел Data Persistence.
Kane
Собственно, я попытался (видимо не очень хорошо, или очень длинно ) описать его алгоритм, но делает он следующее: я ему подсовываю раздачи на рутрекере, запускаю кроном раз в сутки, а он проверяет, не обновились ли .торрент файлы в раздаче, обновлённые сохраняет в директорию, которую rtorrent мониторит на наличие новых файлов. Таким образом после добавления новой серии в раздачу сериала, она сама и качается.

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

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

В общем, большое спасибо за наводку.

updКажется модули pickle и shelve то, что мне подходит.
Kane
Дописал скрипт
#Загрузка обновлённых торрентов с 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

Для работы скрипт нужно немного подредактировать:

Строка 9 tmp_dir = ‘tmp\\’ Вместо tmp можно указать путь до временной директории
Строка 10 true_dir = ‘torr\\’ Вместо torr можно указать путь до директории, которую загрузчик торрентов будет проверять на наличие новых файлов
Строка 12 rls_file = “releases.txt” Вместо releases.txt можно указать название файла с ссылками на раздачи. Файл должен находиться в папке со скриптом.
Строка 87 LOGIN = ‘ваш_логин’ Внутри кавычек написать свой логин от рутрекера
Строка 88 PASS = ‘ваш_пароль’ Внутри кавычек написать свой пароль от рутрекера

Создать директории tmp_dir и true_dir. Проверял на относительных ссылках, думаю, на абсолютных тоже заработает.
Создать файлик rls_file, заполнить его построчно ссылками на раздачи

В линуксах бэкслеши нужно заменить на слеши (\ на /): в строках 9, 10, 32

Требования: python 3 (проверен в python 3.2.2 и 3.2.3 в окошках и 3.1 в debian 6)

В коде есть самописный логгер (функция log_write; правда в стандартной библиотеке пайтона есть свой логгер), он не включён (т.е. вызовы убрал, но пока тестировал скрипт - работал ) + много сообщений выдаётся в консоль.

Буду рад критике кода

Скачать можно здесь
reclosedev
Kane
Буду рад критике кода
Ну тогда начнем

Kane
В линуксах бэкслеши нужно заменить на слеши (\ на /): в строках 9, 10, 32
В Win тоже можно использовать ‘/’ но в названиях директории они вообще ни к чему.

Kane
В коде есть самописный логгер …
logging из стандартной библиотеки - FTW!

def check_file_size(file_check):
   '''измеряем размер файла'''
   import os
   size_of_file = os.path.getsize(file_check)
   return size_of_file

Импортировать модули внутри функции - не хорошо. Такое бывает необходимо очень редко. Функция отдельная здесь не нужна (или там что-то еще было?). Достаточно вызывать os.path.getsize(file_check)

 write_message = str(log_time) + " " + str(log_message) + "\n"
Склеивать строки - не очень хорошая практика, лучше использовать форматирование или устаревающее:
'%s %s\n' % (log_time, log_message)

tmp_dir + '\\' + torrent_id + '.torrent', ...
...
f = open(tmp_dir + list_id[count] + '.torrent', 'wb')
Для работы с путями есть os.path, например:
os.path.join(tmp_dir, '{}.torrent'.format(torrent_id))
В os.path подставятся правильные слэши.

Kane
rls_file = “releases.txt” Вместо releases.txt можно указать название файла с ссылками на раздачи. Файл должен находиться в папке со скриптом.
А еще можно добавить возможность указывать этот файл в командной строке (sys.argv, argparse, optparse)
Kane
reclosedev
Ну тогда начнем
Замечательно
reclosedev
В Win тоже можно использовать ‘/’ но в названиях директории они вообще ни к чему.
У меня почему-то относительный путь без пары слешей на конце не принимался
reclosedev
logging из стандартной библиотеки - FTW!
я свой “логгер” сначала пользовал, просто чтобы писать в файлик время начала и окончания скрипта писать

reclosedev
Импортировать модули внутри функции - не хорошо. Такое бывает необходимо очень редко. Функция отдельная здесь не нужна (или там что-то еще было?). Достаточно вызывать os.path.getsize(file_check)

Ок, а почему не хорошо? В той функции я ещё строку возвращал, когда несколько return в одной функции ковырял.

Форматирование и os.path попробую

reclosedev
А еще можно добавить возможность указывать этот файл в командной строке (sys.argv, argparse, optparse)

Я сейчас configparser пробую, собирался хранить все настройки скрипта и нескольких дополнительных трекеров в отдельном файле, там же и линк на файл с ссылками. Вроде уже и реализовал подсовывание нужных строк, но при проверке оказалось, что у остальных крупных TorrentPier трекеров (fto, tfile, etc) id треда не совпадает с id торрент файла, систематики различий я не нашёл, парсить страничку - лишний гемор, а писать в файлик с релизами прямой линк на торрент не интересно.

Спасибо!
reclosedev
Kane
У меня почему-то относительный путь без пары слешей на конце не принимался
Если вручную склеивать, то да. С os.path.join() таких проблем нет.
Kane
Ок, а почему не хорошо?
PEP 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.
- Когда импорт сверху, сразу видно какие есть зависимости.
- Если функция вызывается несколько раз, зачем ей лишняя работа.
- Еще, например, скрипт сделал половину работы, а потом в одной функции - бац, и нет нужного модуля.
Kane
Ясно, буду os.path.join() пользовать.

А с configparser подстава прямо как здесь, оказалось в 3.1 и 3.2 конфиги немного по-разному читаются 3.1 ex:
 config['bitbucket.org']['User'] = 'hg'
и 3.2 ex:
config.get('Section1', 'foo')

Вариант из 3.1 работает в 3.2, наоборот - нет
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB