Наверняка, хоть раз в жизни вам хотелось загрузить все изображения с понравившейся веб‑страницы? Сейчас вы узнаете, как создать парсер Python, который извлекает все изображения с веб‑страницы по ее URL‑адресу и загружает их с помощью библиотек
Для начала нам понадобится довольно много зависимостей, давайте установим их:
pip3 install requests bs4 tqdm
Откройте новый файл с именем download_images.py и импортируйте необходимые модули:
import requests import os from tqdm import tqdm from bs4 import BeautifulSoup as bs from urllib.parse import urljoin, urlparse
Первое, что мы сделаем, создадим валидатор URL‑адреса, который проверяет, является ли переданный URL‑адрес действительным, поскольку есть некоторые веб‑сайты, которые помещают закодированные данные вместо URL‑адреса, поэтому нам нужно пропустить их:
def is_valid(url): """ Проверяет, является ли url допустимым URL """ parsed = urlparse(url) return bool(parsed.netloc) and bool(parsed.scheme)
Функция urlparse()
разбирает URL‑адрес на шесть составных частей, а нам просто нужно увидеть, есть ли там netloc (имя домена) и scheme (протокол).
Во-вторых, я собираюсь написать основную функцию, которая определет все URL‑адреса изображений на веб‑странице:
def get_all_images(url): """ Возвращает все URL‑адреса изображений по одному `url` """ soup = bs(requests.get(url).content, "html.parser")
HTML-содержимое веб‑страницы находится в объекте soup
, чтобы извлечь все теги img
в HTML, нам нужно использовать метод soup.find_all("img")
. Посмотрим на него в действии:
urls = [] for img in tqdm(soup.find_all("img"), "Extracting images"): img_url = img.attrs.get("src") if not img_url: # если img не содержит атрибута src, просто пропустите continue
Это получит все элементы img в виде списка Python.
Я обернул его в объект tqdm
, чтобы напечатать индикатор выполнения. Чтобы получить URL‑адрес тега img
, есть атрибут src
. Однако есть некоторые теги, которые не содержат атрибута src
, мы пропускаем, используя оператор continue
.
Теперь нам нужно убедиться, что URL‑адрес является абсолютным:
# сделать URL абсолютным, присоединив домен к только что извлеченному URL img_url = urljoin(url, img_url)
Встречаются URL‑адреса, содержащие пары ключ-значение метода GET HTTP‑протокола, которые нам не нравятся (заканчиваются чем-то вроде этого «/image.png?c=3.2.5«), удалим их:
try: pos = img_url.index("?") img_url = img_url[:pos] except ValueError: pass
Получаем позицию символа «?
«, а затем после него всё удаляем, а если его нет, то возникает исключение ValueError
, поэтому я заключил его в блок try/except
(конечно, вы можете реализовать сей момент лучше, поделитесь своим решением в комментарии).
Теперь давайте убедимся, что каждый URL‑адрес действителен и возвращает все URL‑адреса изображений:
# наконец, если URL действителен if is_valid(img_url): urls.append(img_url) return urls
Теперь, когда у нас есть функция, которая получает все URL‑адреса изображений, нам нужна
def download(url, pathname): """ Загружает файл по URL‑адресу и помещает его в папку `pathname` """ # если путь не существует, сделать этот путь dir if not os.path.isdir(pathname): os.makedirs(pathname) # загружаем тело ответа по частям, а не сразу response = requests.get(url, stream=True) # get the total file size file_size = int(response.headers.get("Content-Length", 0)) # получаем имя файла filename = os.path.join(pathname, url.split("/")[-1]) # индикатор выполнения, изменение единицы измерения на байты вместо итераций (по умолчанию tqdm) progress = tqdm(response.iter_content(1024), f"Downloading {filename}", total=file_size, unit="B", unit_scale=True, unit_divisor=1024) with open(filename, "wb") as f: for data in progress.iterable: # записываем прочитанные данные, в файл f.write(data) # обновить вручную индикатор выполнения progress.update(len(data))
Вышеупомянутая функция принимает URL‑адрес файла для загрузки и путь к папке, в которую этот файл будет сохранен.
Наконец, вот основная функция:
def main(url, path): # получить все изображения imgs = get_all_images(url) for img in imgs: # для каждого изображения, загрузите его download(img, path)
Посмотрите код файла download_images.py целиком:
import requests import os from tqdm import tqdm from bs4 import BeautifulSoup as bs from urllib.parse import urljoin, urlparse def is_valid(url): """ Проверяем, является ли url действительным URL """ parsed = urlparse(url) return bool(parsed.netloc) and bool(parsed.scheme) def get_all_images(url): """ Возвращает все URL‑адреса изображений по одному `url` """ soup = bs(requests.get(url).content, "html.parser") urls = [] for img in tqdm(soup.find_all("img"), "Получено изображение"): img_url = img.attrs.get("src") if not img_url: # если img не содержит атрибута src, просто пропускаем continue # сделаем URL абсолютным, присоединив имя домена к только что извлеченному URL img_url = urljoin(url, img_url) # удалим URL‑адреса типа '/hsts-pixel.gif?c=3.2.5' try: pos = img_url.index("?") img_url = img_url[:pos] except ValueError: pass # наконец, если URL действителен if is_valid(img_url): urls.append(img_url) return urls def download(url, pathname): """ Загружает файл по URL‑адресу и помещает его в папку `pathname` """ # если путь не существует, создать dir if not os.path.isdir(pathname): os.makedirs(pathname) # загружаем тело ответа по частям, а не сразу response = requests.get(url, stream=True) # получить общий размер файла file_size = int(response.headers.get("Content-Length", 0)) # получаем имя файла filename = os.path.join(pathname, url.split("/")[-1]) # индикатор выполнения, изменение единицы измерения на байты вместо итераций (по умолчанию tqdm) progress = tqdm(response.iter_content(1024), f"Загружен {filename}", total=file_size, unit="B", unit_scale=True, unit_divisor=1024) with open(filename, "wb") as f: for data in progress.iterable: # записываем прочитанные данные в файл f.write(data) # обновление индикатора выполнения вручную progress.update(len(data)) def main(url, path): # получить все изображения imgs = get_all_images(url) for img in imgs: # скачать для каждого img download(img, path) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="Этот скрипт загружает все изображения с веб‑страницы.") parser.add_argument("url", help="URL‑адрес веб‑страницы, с которой вы хотите загрузить изображения.") parser.add_argument("-p", "--path", help="Каталог, в котором вы хотите хранить изображения, по умолчанию - это домен переданного URL.") args = parser.parse_args() url = args.url path = args.path if not path: # если путь не указан, используйте доменное имя этого url в качестве имени папки path = urlparse(url).netloc main(url, path)
Получение всех URL‑адресов изображений с этой страницы и загрузка каждого из них по одному. Давайте проверим:
main("https://waksoft.susu.ru/", "waksoft-susu-ru")
Будут загружены все изображения с указанного URL‑адреса и сохранены в папке «waksoft‑susu‑ru», которая будет создана автоматически.
Однако обратите внимание, что есть некоторые веб‑сайты, которые загружают свои данные с помощью Javascript, в этом случае вы должны вместо этого использовать библиотеку requests_html. Я уже сделал другой скрипт, который вносит некоторые изменения в исходный и обрабатывает рендеринг Javascript, и записал его в файл download_images_js.py, посмотрите:
from requests_html import HTMLSession import requests from tqdm import tqdm from bs4 import BeautifulSoup as bs from urllib.parse import urljoin, urlparse import os def is_valid(url): """ Проверяем, является ли url действительным URL """ parsed = urlparse(url) return bool(parsed.netloc) and bool(parsed.scheme) def get_all_images(url): """ Возвращает все URL‑адреса изображений по одному `url` """ # инициализировать сеанс session = HTMLSession() # делаем HTTP‑запрос и получаем ответ response = session.get(url) # выполнить Javascript с таймаутом 20 секунд response.html.render(timeout=20) # создаем парсер soup soup = bs(response.html.html, "html.parser") urls = [] for img in tqdm(soup.find_all("img"), "Извлечено изображение"): img_url = img.attrs.get("src") or img.attrs.get("data-src") or img.attrs.get("data-original") print(img_url) if not img_url: # если img не содержит атрибута src, просто пропустим continue # сделаем URL абсолютным, присоединив имя домена к только что извлеченному URL img_url = urljoin(url, img_url) # удалим URL‑адреса типа '/hsts-pixel.gif?c=3.2.5' try: pos = img_url.index("?") img_url = img_url[:pos] except ValueError: pass # наконец, если URL действителен if is_valid(img_url): urls.append(img_url) # закрыть сеанс, чтобы завершить процесс браузера session.close() return urls def download(url, pathname): """ Загружает файл по URL‑адресу и помещает его в папку `pathname` """ # если папка не существует, создадим папку с именем dir if not os.path.isdir(pathname): os.makedirs(pathname) # загружаем тело ответа по частям, а не сразу response = requests.get(url, stream=True) # получить общий размер файла file_size = int(response.headers.get("Content-Length", 0)) # получаем имя файла filename = os.path.join(pathname, url.split("/")[-1]) # индикатор выполнения, изменяем единицы измерения на байты вместо итераций (по умолчанию tqdm) progress = tqdm(response.iter_content(1024), f"Downloading {filename}", total=file_size, unit="B", unit_scale=True, unit_divisor=1024) with open(filename, "wb") as f: for data in progress.iterable: # записываем прочитанные данные в файл f.write(data) # обновим индикатор выполнения вручную progress.update(len(data)) def main(url, path): # получить все изображения imgs = get_all_images(url) for img in imgs: # скачать для каждого img download(img, path) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="Этот скрипт загружает все изображения с веб‑страницы.") parser.add_argument("url", help="URL‑адрес веб‑страницы, с которой вы хотите загрузить изображения.") parser.add_argument("-p", "--path", help="Каталог, в котором вы хотите хранить изображения, по умолчанию - это домен переданного URL") args = parser.parse_args() url = args.url path = args.path if not path: # если путь не указан, в качестве имени папки используйте доменное имя rl path = urlparse(url).netloc main(url, path)
Хорошо, всё на сегодя!
Как, используя Python, загрузить все изображения с веб-страницы, опубликовано К ВВ, лицензия — Creative Commons Attribution-NonCommercial 4.0 International.
Респект и уважуха