многопоточный downloader python
Есть у меня ссылка на сегмент такого типа url = https://link-to-seg/segments/seg{}.mp4,
как вы поняли, если подставить вместо {} цифры от 1 до какого - то числа, то будут скачиваться разные сегменты.
Сначала я написал совсем простенький код:
for i in range(1, 10000):
r = requests.get(url.format(i), headers=headers)
if int(r.status_code) not in [200, 404]:
import sys
print(f'Error, {r.status_code}')
sys.exit()
if r.status_code == 404:
print('[+] Успех, все куски скачались')
break
with open(f'{i}.mp4', 'wb') as f:
f.write(r.content)
with open(f'to_ffmpeg.txt', 'a', encoding='utf-8') as f:
f.write(f"file '{i}.mp4'\n")
Как вы поняли, потом я объединю все сегменты с помощью ffmpeg.
Этот код работал нормально,
скачивал 400 сегментов за +- 2 минуты.
Но я решил написать 50 поточную версию этой программы.
Получился этот код:
def make_request(i):
for i in range(i, i + 1):
r = requests.get(url.format(i), headers=headers)
if int(r.status_code) not in [200, 404]:
import sys
print(f'Error, {r.status_code}')
sys.exit()
if int(r.status_code == 404):
print('[+] Успех, все куски скачались')
break
with open(f'{i}.mp4', 'wb') as f:
f.write(r.content)
with open(f'to_ffmpeg.txt', 'a', encoding='utf-8') as f:
f.write(f"file '{i}.mp4'\n")
def runner():
while True:
i_val = mdl_q.get()
make_request(i_val)
mdl_q.task_done()
if __name__ == '__main__':
import queue
mdl_q = queue.Queue()
for i in range(50):
threading.Thread(target=runner, daemon=True).start()
for i in range(1, 10000):
mdl_q.put(i)
mdl_q.join()
Если я получаю status_code 404, в консоль выводится сообщение что всё скачено, и пока очередь полностью не будет отработана, скрипт продолжит получать 404 status_code и выводить это сообщение. Как сделать так, чтобы при получении кода 404, новые значения из очереди не брались, а программа завершалась только когда все старые потоки отработают и какой-то поток получит status_code 404?
Ещё раз, итог должен быть таким: скрипт получает код 404, после этого, программа не берёт новые значения из очереди и ждёт пока все ранее созданные потоки отработают, потом программа завершается.
Если что, я против пиратства, я скачиваю фильм с собственного сайта.
-Зачем?
Просто интересно, учится ни когда не позно.
Пожалуйста, помогите.
Ответы (1 шт):
Апи через пул мне нравится больше, оно компактнее. Добавил 2 синхронизации.
Lock чтоб стартовала (до получения хидеров) одновременно только одна закачка. Также она тормозит генератор последовательности.
Event чтоб сообщить генератору когда последовательность завершенна.
url = "https://link-to-seg/segments/seg{}.mp4"
from multiprocessing.dummy import Pool, Lock, Event, Semaphore
import requests
import sys
headers = {....}
lock = Semaphore(50) # 50 - сколько сегментов ставить в очередь
finished = Event()
def i_generator():
i = 0
while finished.is_set() is False:
lock.acquire()
i+=1
yield i
def make_request(i):
r = requests.get(url.format(i), headers=headers, stream=True) # получаем только хидер по одному
print(r)
if int(r.status_code) not in [200, 404]:
print(f'Error, {r.status_code}')
sys.exit()
if int(r.status_code == 404):
print('[+] Успех, все куски скачались')
finished.set()
lock.release()
with open(f'{i}.mp4', 'wb') as f:
f.write(r.content) # докачка самого файла
with open(f'to_ffmpeg.txt', 'a', encoding='utf-8') as f:
f.write(f"file '{i}.mp4'\n")
return i
pool = Pool(10) # 10 - сколько качать паралельно
a = pool.imap( make_request, i_generator())
for i in a:
print(i)
pool.join()
Этот код работает на потоках, одновременно качается 8 фрагментов.