Как скачать картинку с сайта на Python?
Название файла парси из ссылки, принты на логгинг меняй. И логи на английском. Вынеси логику и клиентский код отдельно. Не вижу обработки ошибок для запросов. Библиотеку поменяй на httpx. Почему не class based, переписывай и сделай абстракцию для http клиента от которой и наследуйся. Если отдельный файл со скриптом то где if __name__ . ? Если запросов таких много будет, юзай асинхронку сразу лучше.
Так в целом по верхам прошлись, тегнешь после некст коммита, пока денаю пулл реквест.
P.s. Добавь ещё pillow, чекай что картинка валидная, пережми в jpeg и задай Макс разрешение по ширине и высоте чтобы большие картинки не сохранять в оригинале. Место на сторедже беречь надо, а не закидывать картинки по 50Мб.
Развернуть ветку
Как много лишних действий, лишь бы на С++ не переписывать.
Развернуть ветку
Лиду и тестировщику тоже самое скажешь? Не долго проработаешь на бэке, сударь.
Развернуть ветку
Человек хотел скачать картинку — человек скачал картинку. Нет, давайте переусложнять. Ясен пень, что без кафки и телебота сервис делать нельзя, плюс мониторинг в графану.
Развернуть ветку
А если картинка не png, то и обойдемся
Четыре метода загрузки изображений с веб-сайта с помощью Python
Недавно пришлось по работе написать простенький парсер на питоне, который бы скачивал с сайта изображения (по идее тот же самый парсер может качать не только изображения, но и файлы других форматов) и сохранял их на диске. Всего я нашел в интернете четыре метода. В этой статье я их решил собрать все вместе.
1-ый метод
Первый метод использует модуль urllib (или же urllib2). Пусть имеется ссылка на некое изображение img. Метод выглядит следующим образом:
import urllib resource = urllib.urlopen(img) out = open(". \img.jpg", 'wb') out.write(resource.read()) out.close()
Здесь нужно обратить внимание, что режим записи для изображений — ‘wb’ (бинарный), а не просто ‘w’.
2-ой метод
Второй метод использует тот же самый urllib. В дальнейшем будет показано, что этот метод чуть медленнее первого (отрицательный оттенок фактора скорости парсинга неоднозначен), но достоин внимания из-за своей краткости:
import urllib urllib.urlretrieve(img, ". \img.jpg")
Притом стоит заметить, что функция urlretrieve в библиотеке urllib2 по неизвестным мне причинам (может кто подскажет по каким) отсутствует.
3-ий метод
Третий метод использует модуль requests. Метод имеет одинаковый порядок скорости выгрузки картинок с первыми двумя методами:
import requests p = requests.get(img) out = open(". \img.jpg", "wb") out.write(p.content) out.close()
При этом при работе с веб в питоне рекомендуется использовать именно requests вместо семейств urllib и httplib из-за его краткости и удобства обращения с ним.
4-ый метод
Четвертый метод по скорости кардинально отличается от предыдущих методов (на целый порядок). Основан на использовании модуля httplib2. Выглядит следующим образом:
import httplib2 h = httplib2.Http('.cache') response, content = h.request(img) out = open('. \img.jpg', 'wb') out.write(content) out.close()
Здесь явно используется кэширование. Без кэширования (h = httplib2.Http()) метод работает в 6-9 раза медленнее предыдущих аналогов.
Тестирование скорости проводилось на примере скачивания картинок с расширением *.jpg c сайта новостной ленты lenta.ru. Выбор картинок, подпадающих под этот критерий и измерение времени выполнения программы производились следующим образом:
import re, time, urllib2 url = "http://lenta.ru/" content = urllib2.urlopen(url).read() imgUrls = re.findall('img .*?src="https://habr.com/ru/articles/210238/(.*?)"', сontent) start = time.time() for img in imgUrls: if img.endswith(".jpg"): """реализация метода по загрузке изображения из url""" print time.time()-start
Постоянно меняющиеся картинки на сайте не повлияли на чистоту измерений, поскольку методы отрабатывали друг за другом. Полученные результаты таковы:
| Метод 1, с | Метод 2, с | Метод 3, с | Метод 4, с (без кэширования, с) |
|---|---|---|---|
| 0.823 | 0.908 | 0.874 | 0.089 (7.625) |
Данные представлены как результат усреднения результатов семи измерений.
Просьба к тем, кто имел дело с библиотекой Grab (и с другими), написать в комментариях аналогичный метод по скачиванию изображений с помощью этой и других библиотек.
- питон и парсинг
- изображения
Статья Парсим и скачиваем «нескучные обои» с использованием потоков в Python
». Привет, Денис Попов . Ну, а если более серьезно, то скачаем картинки с обоями с сайта, на котором их очень и очень много. Конечно же, для того, чтобы скачать картинки мы будем использовать Python, а загрузка картинок будет происходить в многопоточном режиме. Ну и наиболее полезная часть данной статьи состоит в том, что мы немного попрактикуемся в парсинге.
Что понадобиться?
Для отправки запросов и скачивание картинок будем использовать библиотеку requests. Для того, чтобы распарсить полученные результаты и получить из них ссылки на картинки BeautifulSoup и lxml. Поэтому, для начала их нужно установить:
pip install bs4 requests lxml
В работе скрипта так же потребуется библиотека для создания потоков threading, а так же библиотеки time, os и json. Поэтому перед началом работы давайте все это импортируем в наш скрипт.
import json import os.path import threading import time import requests from bs4 import BeautifulSoup
Сайт, с которого будет происходить скачивание обоев,
Ссылка скрыта от гостей
, на самом деле очень лоялен ко всякого рода попыткам его парсить. Он не сбрасывает соединение, не психует и не нервничает, когда к нему прилетает слишком много запросов. Идеальный пациент, можно сказать. Думаю, что он будет отдавать все данные даже в том случае, если мы не станем указывать заголовки запроса. Но, все же, чтобы работать по правилам и попрактиковаться, создадим их. Идем на сайт и смотрим заголовки в любом запросе. Забираем user-agent и accept.
Для этого щелкаем правой кнопкой мыши и в Яндекс.Браузере выбираем пункт «Исследовать элемент». Если же это будет Edge, то данный пункт называется «Проверить». Ну и так далее. Суть в том, что нужно попасть в инструменты разработчика.
Заголовки для запроса
Копируем их и вставляем в скрипт:
headers = < 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.119 ' 'YaBrowser/22.3.0.2434 Yowser/2.5 Safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,' 'application/signed-exchange;v=b3;q=0.9 ' >
Функция получения пагинации
Давайте для начала создадим функцию получения пагинации со страницы из категории. Назову ее get_page_count(url). На вход она принимает только лишь один параметр, это ссылка на страницу категории. Далее выполняется запрос и полученные данные передаются в BeautifulSoup. После этого ищем блок с тэгом div, у которого id=pages. Получаем из этого блока все ссылки на страницы и забираем последнюю ссылку. Далее, разделяем ее и обрезаем лишние пробелы. Но, опытным путем было выявлено, что в некоторых категориях нет стрелки в тексте последней ссылки. Так как количество страниц с картинками помещается в блок пагинации целиком. И в этом случае скрипт падает с ошибкой. Для этого добавим блок try – except, чтобы эту ошибку отловить и просто забрать текст, без обрезки, из последней ссылки в блоке пагинации.
Полный код функции получения пагинации
def get_page_count(url): req = requests.get(url=url, headers=headers) soup = BeautifulSoup(req.text, 'lxml') try: page_count = int(soup.find('div', ).find_all('a')[-1].text.split(" ")[1].strip()) except: page_count = int(soup.find('div', ).find_all('a')[-1].text.strip()) return page_count
Получение ссылок на категории, названия категорий и количества страниц в каждой из них
Для того, чтобы названия категорий, ссылки на них и количество страниц в каждой категории хранились локально, нужно их во что-то сохранить. Я решил, что удобнее всего это будет сделать в файл JSON. А для того, чтобы ускорить получение ссылок на категории и прочих параметров, буду обрабатывать это в многопоточном режиме.
Для начала напишем функцию для нахождения названия категории, ссылки на нее и количества в ней страниц. Назвал я ее def get_link_category(url). На вход она получает ссылку на категорию. И дальше в коде для начала формируется общая ссылка на категорию. Потом ссылка, которую передали в функцию разделяется по слэшу и снова собирается для записи этой ссылки, в которой уже нет привязки к определенной странице, для записи в JSON. Это будет нужно для того, чтобы впоследствии брать ссылку из JSON и формировать из нее ссылку на загрузку пагинации и прочих параметров с определенной страницы.
Ну и последний я нахожу наименование категории. Именно оно будет служить ключом в словаре, который запишется в JSON. Формирую словарь и записываю в файл.
Вот полный код функции
def get_link_category(url): url_cats = 'https://w-dog.ru' + url.find('div', class_='word').find('a')['href'] url_cat = str('https://w-dog.ru' + url.find('div', class_='word').find('a')['href']).split("/") url_cat_s = f'/////' name_category = url.find('div', class_='word').find('a').text.strip() p_count = get_page_count(url_cats) category_dict[name_category] = < 'url_category': url_cat_s, 'page_count': p_count >with open('category_res.json', 'w', encoding='utf-8') as file: json.dump(category_dict, file, indent=4, ensure_ascii=False)
Чтобы было понятнее. В данной функции получаются данные только из одной категории. А вот каждая последующая ссылка на категорию передается из функции, в которой запускаются потоки. Назвал я ее def thread_func_category(). На входе она ничего не принимает. В ней есть ссылка, по которой делается запрос на страницу и собираются все ссылки на категории, которые потом записываются в словарь.
На следующем этапе запускается цикл для перебора значений полученного словаря. И в данном цикле формируется поток с указанием на функцию и параметрами, которые в данную функцию передаются. Ну, а дальше, потоки просто стартуют.
Полный код функции старта потоков загрузки ссылок на категории:
def thread_func_category(): url = 'https://w-dog.ru/' req = requests.get(url=url, headers=headers) soup = BeautifulSoup(req.text, 'lxml') all_category = soup.find_all('div', class_='wpitem category') for url in all_category: t = threading.Thread(target=get_link_category, kwargs=) t.start()
Загрузка картинок из категории, выбранной пользователем
Теперь можно перейти к важной части, а именно к загрузке картинок обоев из категории, которую указал пользователь. Назову ее get_pict_download(item, name_cat). Здесь на вход прилетает объект из найденных ссылок страницы, а так же имя категории. Оно будет нужно для того, чтобы указать папку с именем категории и загрузить в нее картинки. На первом этапе ищется имя картинки, под которым она будет сохранена и это имя очищается от мусора в виде всяких специальных символов, которые не совместимы с сохранением файлов в операционной системе.
Затем делается проверка на наличие картинки с данным названием в папке. Если такого названия нет, то картинка загружается. Если же есть, то не делается ничего. Это позволяет не дублировать загрузку одних и тех же картинок с перезаписью. Что позволяет, в случае, если вы не докачали категорию, а вы можете ее не докачать, так как картинок там о-о-очень много, ускорить работу скрипта. Ну и дальше формируется ссылка на картинку, после чего происходит ее загрузка и запись на диск.
Код загрузки картинки:
def get_pict_download(item, name_cat): name_pict = item.find('b', class_='word').text.strip().replace("/", " ").replace('"', ''). \ replace("'", "").replace(".", "") if not os.path.isfile(os.path.join(name_cat, f'.jpg')): url_pict = 'https://w-dog.ru' + item.find('div', class_='action-buttons').find('a')['href'] req = requests.get(url=url_pict, headers=headers) with open(os.path.join(name_cat, f'.jpg'), 'wb') as file: file.write(req.content)
А дальше нужно создать функцию, в которой будут запускаться потоки для скачивания картинок. Назовем ее thread_func(url_cat, count_cat, name_cat). На входе данная функция принимает ссылку на категорию, пагинацию и имя категории.
Затем получаем время старта функции, для измерения скорости загрузки картинок. После выводим сообщение о том, что загружается категория такая-то, такая-то. И количество в ней страниц. От вывода сообщения о загрузке определенной картинки я отказался, потому, что порою вывод просто не успевает отобразиться в терминале. И получается перемешанная каша. Так как в итоге отображается все. Поэтому здесь я ограничился выводом сообщения о загрузке определенной страницы. Далее в цикле запускаем запрос, в котором получаем ссылки на картинки с первой и последующих страниц. Затем данные из запроса передаются в объект супа. Проверяется, если ли папка с именем категории. Если нет, создается. И в цикле запускаются потоки, которые привязаны к функции загрузки картинки и передают в нее необходимые параметры. А после того, как скрипт завершит свою работу, выводиться время загрузки картинок.
Полный код запуска потоков для загрузки картинок
def thread_func(url_cat, count_cat, name_cat): start_time = time.monotonic() print(f'[+] Загружаю категорию "". Количество страниц: \n') if not os.path.isdir(name_cat): os.mkdir(name_cat) for nc in range(1, count_cat + 1): print(f'[+] Загружаю >> Страница: /. ') req = requests.get(url=f"/best/", headers=headers) soup = BeautifulSoup(req.text, 'lxml') all_url_page = soup.find_all('div', class_='wpitem') for item in all_url_page: t = threading.Thread(target=get_pict_download, kwargs=) t.start() print(f'\nВремя загрузки файлов: ')
Осталась только функция main(). В ней для начала запускается функция обновления словаря. Мало ли что, может быть за время, пока не использовался скрипт появились новые категории или добавились обои и поэтому количество страниц может измениться. Тут есть один нюанс, который заключается в том, что запись категорий в словарь будет каждый раз производиться в произвольном порядке, а именно в порядке завершения работы потока. Поэтому отображение категорий всегда будет выводиться по-разному. Ну, или почти всегда. Далее делается небольшая пауза, чтобы дать сохраниться файлу на диск. Так как если этой паузы не делать, скрипт продолжает свою работу, файл еще не успевает сохраниться. А так как на следующем этапе этот словарь открывается для чтения, скрипт вываливается с ошибкой, что файл не найден. Потом формируется словарь из открытого JSON, с помощью которого определяется название категории, а так же ссылка и количество страниц.
Затем выводятся названия категорий на экран с определенными номерами. После чего выводиться сообщение с просьбой ввести номер категории для загрузки. Выполняется проверка, есть ли данный номер словаре. Если нет, ничего не делаем. Если есть, запускаем загрузку картинок.
Код функции main()
def main(): print('[+] Обновляю словарь. \n') thread_func_category() time.sleep(2) with open('category_res.json', 'r', encoding='utf-8') as file: cat_dict = json.load(file) dict_cat = <> for num, cat in enumerate(cat_dict): print(f'. | страниц. ') dict_cat[num] = < 'url_category': cat_dict[cat]["url_category"], 'page_count': cat_dict[cat]["page_count"], 'name_cat': cat >num_cat = int(input('\n[+] - Введите номер категории для загрузки: ')) # передача данных для запуска потоков загрузки картинок if num_cat in dict_cat: thread_func(f"/", dict_cat[num_cat]['page_count'], dict_cat[num_cat]['name_cat']) else: print('[-] Вы ввели неверный номер категории для загрузки.') exit(0)
На этом все. Нужно сказать, что я немного поэкспериментировал и первоначальную версию парсера обоев сделал без использования многопоточной загрузки. И попробовал загрузить какую-либо категорию. Надо сказать, что с такой скоростью загрузки, когда все выполняется последовательно, категорию можно грузить сутками. После чего было решено добавить многопоточность. И о чудо, скорость загрузки увеличилась в разы.
Я пробовал загрузить самую маленькую категорию, в которой шесть страниц. На каждой странице по двенадцать картинок. То есть, получается, что загружается 69. Скорее всего, потому, что некоторые картинки просто повторяются на страницах. Будем считать, что так, я просто не стал проверять. Так как это слишком долго и муторно. Так вот скрипт без потоков работает примерно от одной до двух минут. В зависимости от загруженности сети. А вот скрипт с потоками от пяти до семи секунд.
Вот небольшое видео, в котором я даю некоторые пояснения по сбору данных со страницы, а так же сравниваю скорость работы скрипта:
Полный код скрипта загрузки обоев
import json import os.path import threading import time import requests from bs4 import BeautifulSoup # заголовки для запроса headers = < 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.119 ' 'YaBrowser/22.3.0.2434 Yowser/2.5 Safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,' 'application/signed-exchange;v=b3;q=0.9 ' >category_dict = <> # получение пагинации # находим последнюю станицу и чистим от мусора # исключение добавлено потому, что есть разделы, у которых # меньше 7 страниц. В этом случае пагинация немного отличается def get_page_count(url): req = requests.get(url=url, headers=headers) soup = BeautifulSoup(req.text, 'lxml') try: page_count = int(soup.find('div', ).find_all('a')[-1].text.split(" ")[1].strip()) except: page_count = int(soup.find('div', ).find_all('a')[-1].text.strip()) return page_count # получаем ссылки на категории и сохраняем с JSON # получение ссылок происходит при каждом запуске программы # так как считывается так же и количество стараниц в каждой категории # и по прошествии времени оно может изменяться def get_link_category(url): url_cats = 'https://w-dog.ru' + url.find('div', class_='word').find('a')['href'] url_cat = str('https://w-dog.ru' + url.find('div', class_='word').find('a')['href']).split("/") url_cat_s = f'/////' name_category = url.find('div', class_='word').find('a').text.strip() p_count = get_page_count(url_cats) category_dict[name_category] = < 'url_category': url_cat_s, 'page_count': p_count >with open('category_res.json', 'w', encoding='utf-8') as file: json.dump(category_dict, file, indent=4, ensure_ascii=False) def thread_func_category(): url = 'https://w-dog.ru/' req = requests.get(url=url, headers=headers) soup = BeautifulSoup(req.text, 'lxml') all_category = soup.find_all('div', class_='wpitem category') for url in all_category: t = threading.Thread(target=get_link_category, kwargs=) t.start() # загрузка картинок из категории # получение названия категории # создание папки с именем категории куда будут загружаться картинки # поиск всех ссылок настранице, скачивание их в цикле # и сохранение в созданную папку def get_pict_download(item, name_cat, count_cat): name_pict = item.find('b', class_='word').text.strip().replace("/", " ").replace('"', ''). \ replace("'", "").replace(".", "") if not os.path.isfile(os.path.join(name_cat, f'.jpg')): url_pict = 'https://w-dog.ru' + item.find('div', class_='action-buttons').find('a')['href'] req = requests.get(url=url_pict, headers=headers) with open(os.path.join(name_cat, f'.jpg'), 'wb') as file: file.write(req.content) def thread_func(url_cat, count_cat, name_cat): start_time = time.monotonic() print(f'[+] Загружаю категорию "". Количество страниц: \n') for nc in range(1, count_cat + 1): print(f'[+] Загружаю >> Страница: /. ') req = requests.get(url=f"/best/", headers=headers) soup = BeautifulSoup(req.text, 'lxml') name_cat = soup.find('div', ).find('h2').text.strip() if not os.path.isdir(name_cat): os.mkdir(name_cat) all_url_page = soup.find_all('div', class_='wpitem') for item in all_url_page: t = threading.Thread(target=get_pict_download, kwargs=) t.start() print(f'\nВремя загрузки файлов: ') def main(): print('[+] Обновляю словарь. \n') thread_func_category() time.sleep(2) with open('category_res.json', 'r', encoding='utf-8') as file: cat_dict = json.load(file) dict_cat = <> for num, cat in enumerate(cat_dict): print(f'. | страниц. ') dict_cat[num] = < 'url_category': cat_dict[cat]["url_category"], 'page_count': cat_dict[cat]["page_count"], 'name_cat': cat >num_cat = int(input('\n[+] - Введите номер категории для загрузки: ')) # передача данных для запуска потоков загрузки картинок if num_cat in dict_cat: thread_func(f"/", dict_cat[num_cat]['page_count'], dict_cat[num_cat]['name_cat']) else: print('[-] Вы ввели неверный номер категории для загрузки.') exit(0) if __name__ == "__main__": main()
Как скачивать изображения с помощью библиотеки requests в Python?
Я установил библиотеку requests в Python 3.6. Необходимо скачать изображения по предоставленной ссылке на файл. Подскажите, пожалуйста, как это можно реализовать? Доступ к изображению я получаю следующим образом: img = r.get(‘URL here’)
Отслеживать
задан 8 мар 2017 в 12:34
77 1 1 золотой знак 3 3 серебряных знака 6 6 бронзовых знаков
Связанный вопрос: How to download image using requests
8 мар 2017 в 12:56
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
В респонсе будет атрибут content в котором содержится изображение в двоичном виде. Таким образом после получения ответа необходимо будет сохранить в его файл.
img = r.get("img_url") img_file = file('path_to_image', 'w') img_file.write(img.content) img_file.close()
Отслеживать
ответ дан 13 мар 2017 в 3:03
Ivan Panov Ivan Panov
173 1 1 серебряный знак 6 6 бронзовых знаков
во второй строчке нужно исправить по моему img_file = open(‘path_to_image’, ‘wb’)
20 дек 2019 в 18:11
- python
- requests
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.15.1019
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.