Наша статья - это отрывок из книги Ричарда Лоусона «Web Scraping with Python». Эта книга содержит пошаговые инструкции по использованию методов программирования Python для этичного парсинга веб-страниц.
Распространенной практикой парсинга является загрузка, хранение и дальнейшая обработка медиаконтента (не веб-страниц или файлов данных). Они могут включать изображения, аудио и видео. Чтобы хранить контент локально (или в такой службе, как S3) и делать это правильно, нам нужно знать тип медиа, и этого недостаточно, чтобы доверять расширению файла в URL-адресе. Следовательно, мы должны знать, как загрузить и правильно представить тип мультимедиа на основе информации с веб-сервера.
Еще одна распространенная задача - создание миниатюр изображений, видео или даже страницы веб-сайта. Мы рассмотрим несколько методов создания эскизов и скриншотов страниц веб-сайта. Часто они используются на новом веб-сайте в качестве миниатюрных ссылок на скопированные файлы, которые хранятся локально.
Наконец, часто бывает необходимо иметь возможность перекодировать медиафайлы, например, преобразовывать видео в формат MP4 или изменять скорость передачи данных или разрешение видео. Другой сценарий - извлечь из видеофайла только звук. Мы не будем рассматривать перекодирование видео, но мы скопируем звук в формате MP3 из файла MP4 с помощью ffmpeg. По аналогии можно легко перекодировать видео с помощью ffmpeg.
Загрузка медиаконтента из интернета
Загрузка мультимедийного контента из интернета - это простой процесс: используйте Requests или другую библиотеку и загрузите его так же, как и HTML-контент.
В модуле urls.py в папке util есть класс с именем URLUtility. Этот класс обрабатывает несколько сценариев, описанных в этой главе, с загрузкой и анализом URL-адресов. Мы будем использовать этот класс в этом и некоторых других сценариях. Убедитесь, что папка модулей находится в вашем пути Python. Кроме того, пример этого сценария находится в файле 04/01_download_image.py.
Вот как мы приступаем к сценарию:
1. Класс URLUtility может загружать контент по URL-адресу. Код в файле сценария следующий:
import const
from util.urls import URLUtility
util = URLUtility(const.ApodEclipseImage())
print(len(util.data))
1. При запуске вы увидите следующий вывод:
Reading URL: https://apod.nasa.gov/apod/image/1709/BT5643s.jpg Read 171014 bytes 171014
В примере читается 171014 байт данных.
Как это работает
URL-адрес определяется как константа const.ApodEclipseImage() в модуле const:
def ApodEclipseImage():
return "https://apod.nasa.gov/apod/image/1709/BT5643s.jpg"
Конструктор класса URLUtility имеет следующую реализацию:
def __init__(self, url, readNow=True):
""" Construct the object, parse the URL, and download now if
specified"""
self._url = url
self._response = None
self._parsed = urlparse(url)
if readNow:
self.read()
Конструктор сохраняет URL-адрес, анализирует его и загружает файл с помощью метода read(). Ниже приведен код метода read():
def read(self):
self._response = urllib.request.urlopen(self._url)
self._data = self._response.read()
Эта функция использует urlopen для получения ответа от объекта, а затем читает поток и сохраняет его как свойство объекта. Затем эти данные можно получить с помощью свойства data:
@property
def data(self):
self.ensure_response()
return self._data
Затем код просто сообщает о длине этих данных, 171014.
Есть еще кое-что
Этот класс будет использоваться для других задач, таких как определение типов содержимого, имени файлов и расширений для этих файлов. Далее мы рассмотрим синтаксический анализ URL-адресов для имен файлов.
Разбор URL-адреса с помощью urllib для получения имени файла
При загрузке контента с URL-адреса мы часто хотим сохранить его в файле. Часто бывает достаточно сохранить файл в файле с именем, указанным в URL-адресе. Но URL-адрес состоит из ряда фрагментов, поэтому как мы можем найти фактическое имя файла из URL-адреса, особенно если после имени файла часто бывает много параметров?
Мы снова будем использовать класс URLUtility для этой задачи. Файл с кодом для этого сценария - 04/02_parse_url.py.
Как это сделать
Запустите файл сценария с помощью интерпретатора Python. Будет запущен следующий код:
util = URLUtility(const.ApodEclipseImage())
print(util.filename_without_ext)
This results in the following output:
Reading URL: https://apod.nasa.gov/apod/image/1709/BT5643s.jpg
Read 171014 bytes
The filename is: BT5643s
Как это работает
В конструкторе URLUtility есть вызов urlib.parse.urlparse. Следующий пример демонстрирует интерактивное использование функции:
>>> parsed = urlparse(const.ApodEclipseImage())
>>> parsed
ParseResult(scheme='https', netloc='apod.nasa.gov',
path='/apod/image/1709/BT5643s.jpg', params='', query='', fragment='')
Объект ParseResult содержит различные композитные элементы URL-адреса. Элемент path содержит путь и имя файла. Вызов свойства .filename_without_ext возвращает только имя файла без расширения:
@property
def filename_without_ext(self):
filename = os.path.splitext(os.path.basename(self._parsed.path))[0]
return filename
Вызов os.path.basename возвращает только часть имени файла в пути (включая расширение). os.path.splittext() затем разделяет имя файла и расширение, и функция возвращает первый элемент этого кортежа/списка (имя файла).
Есть еще кое-что
Может показаться странным, что этот вызов не возвращает также расширение как часть имени файла. Это связано с тем, что мы не можем предположить, что полученный контент действительно соответствует предполагаемому типу из расширения. Лучше определить это, используя заголовки, возвращаемые веб-сервером. Это наш следующий сценарий.
Определение типа контента для URL
При выполнении запросов GET для содержимого с веб-сервера сервер возвращает несколько заголовков, один из которых идентифицирует тип содержимого с точки зрения веб-сервера. В этом сценарии мы учимся использовать это, чтобы определять, что веб-сервер считает типом контента.
Мы снова используем класс URLUtility. Код сценария находится в 04/03_determine_content_type_from_response.py.
Как это сделать
Действуем следующим образом:
1. Запустите сценарий. Он содержит следующий код:
util = URLUtility(const.ApodEclipseImage())
print("The content type is: " + util.contenttype)
1. Со следующим результатом:
Reading URL: https://apod.nasa.gov/apod/image/1709/BT5643s.jpg
Read 171014 bytes
The content type is: image/jpeg
Как это работает
Свойство .contentype реализовано следующим образом:
@property
def contenttype(self):
self.ensure_response()
return self._response.headers['content-type']
Свойство .headers объекта _response представляет собой класс заголовков, подобный словарю. Ключ типа содержимого будет извлекать тип содержимого, указанный сервером. Этот вызов метода sure_response() просто обеспечивает выполнение функции .read ().
Есть еще кое-что
Заголовки ответа содержат большой объем информации. Если мы более внимательно посмотрим на свойство заголовков ответа, мы увидим, что возвращаются следующие заголовки:
>>> response = urllib.request.urlopen(const.ApodEclipseImage())
>>> for header in response.headers: print(header)
Date
Server
Last-Modified
ETag
Accept-Ranges
Content-Length
Connection
Content-Type
Strict-Transport-Security
И мы можем видеть значения для каждого из этих заголовков.
>>> for header in response.headers: print(header + " ==> " +
response.headers[header])
Date ==> Tue, 26 Sep 2017 19:31:41 GMT
Server ==> WebServer/1.0
Last-Modified ==> Thu, 31 Aug 2017 20:26:32 GMT
ETag ==> "547bb44-29c06-5581275ce2b86"
Accept-Ranges ==> bytes
Content-Length ==> 171014
Connection ==> close
Content-Type ==> image/jpeg
Strict-Transport-Security ==> max-age=31536000; includeSubDomains
Многие из них мы не будем рассматривать в этой статье, но полезно знать, что они существуют.
Определение расширения файла по типу содержимого
Хорошей практикой является использование заголовка типа содержимого для определения типа содержимого и определения расширения, которое следует использовать для хранения содержимого в виде файла.
Мы снова используем созданный нами объект URLUtility. Сценарий - 04/04_determine_file_extension_from_contenttype.py) :.
Как это сделать
Продолжите, запустив скрипт. Расширение для типа мультимедиа можно найти с помощью свойства .extension:
util = URLUtility(const.ApodEclipseImage())
print("Filename from content-type: " + util.extension_from_contenttype)
print("Filename from url: " + util.extension_from_url)
This results in the following output:
Reading URL: https://apod.nasa.gov/apod/image/1709/BT5643s.jpg
Read 171014 bytes
Filename from content-type: .jpg
Filename from url: .jpg
Этот код сообщает как расширение, определенное на основе типа файла, так и на основе URL-адреса. Они могут быть разными, но в данном случае они одинаковы.
Как это работает
Ниже представлена реализация свойства .extension_from_contenttype:
@property
def extension_from_contenttype(self):
self.ensure_response()
map = const.ContentTypeToExtensions()
if self.contenttype in map:
return map[self.contenttype]
return None
Первая строка гарантирует, что мы прочитали ответ от URL. Затем функция использует словарь Python, определенный в модуле const, который содержит словарь типов содержимого для расширения:
def ContentTypeToExtensions():
return {
"image/jpeg": ".jpg",
"image/jpg": ".jpg",
"image/png": ".png"
}
Если тип содержимого находится в словаре, будет возвращено соответствующее значение. В противном случае возвращается None. Обратите внимание на соответствующее свойство .extension_from_url:
@property
def extension_from_url(self):
ext = os.path.splitext(os.path.basename(self._parsed.path))[1]
return ext
При этом используется тот же метод, что и в свойстве .filename для анализа URL-адреса, но вместо этого возвращается элемент [1], который представляет расширение вместо базового имени файла.
Подводя итог, мы обсудили, насколько эффективно мы можем парсить аудио, видео и изображения из Интернета с помощью Python.
Комментариев нет:
Отправить комментарий