Как проверить, загружается ли URL-адрес в запросах

#python #python-3.x #url #download #python-requests

#python #python-3.x #url #Скачать #python-запросы

Вопрос:

Я создаю это приложение-загрузчик с помощью tkinter и запросов, и недавно я обнаружил ошибку в своей программе. По сути, я хочу, чтобы моя программа проверяла, загружается ли данный URL-адрес или нет, прежде чем начинать загрузку содержимого URL-адреса. Раньше я делал это, получая заголовки URL-адреса и проверяя, существует ли ‘Content-Length’, и это работает для некоторых URL-адресов (например: https://www.google.com ) но для других (например, ссылка на видео на YouTube) это не так, и это приводит к сбою моей программы. Я видел, что кто-то сказал один stackoverflow, что я могу проверить наличие «вложения» в «Расположении содержимого» заголовков, но у меня это не сработало и вернуло то же самое для загружаемого и не загружаемого URL. Каков наилучший способ сделать это? Код, упомянутый в другой проблеме stackoverflow, который я пробовал и не работал:

 import requests
url = 'https://www.google.com'
headers=requests.head(url).headers
downloadable = 'attachment' in headers.get('Content-Disposition', '')
 

Мой прежний код:

 headers = requests.head(url, headers={'accept-encoding': ''}).headers
try:
    print(type(headers['Content-Length']))
    file_size = int(headers['Content-Length'])
except KeyError:
    # Just a class that I defined to raise an exception if the URL was not downloadable
    raise NotDownloadable()
 

ОБНОВЛЕНИЕ: URL:
https://aspb1.cdn.asset.aparat.com/aparat-video/a5e07b7f62ffaad0c104763c23d7393215613675-360p.mp4?wmsAuthSign=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6IjUzMGU0Mzc3ZjRlZjVlYWU0OTFkMzdiOTZkODgwNGQ2IiwiZXhwIjoxNjExMzMzMDQxLCJpc3MiOiJTYWJhIElkZWEgR1NJRyJ9.FjMi_dkdLCUkt25dfGqPLcehpaC32dBBUNDC9cLNiu0
Этот URL-адрес я использовал для тестирования. Если вы откроете URL-адрес, он напрямую приведет вас к видео, которое вы можете загрузить, но при проверке на «Расположение содержимого» он вернул «Нет», как и большинство загружаемых и не загружаемых URL-адресов, которые я пробовал.

Комментарии:

1. можете ли вы предоставить некоторые URL-адреса для тестирования? затем мы можем отладить фактический сценарий.

2. Есть только один способ убедиться, что вы можете загрузить файл: вы пытаетесь загрузить его. И если это приводит к сбою вашей программы, вы должны это исправить.

3. Я просмотрел ваш предыдущий вопрос о загрузке. Какие типы файлов вы заинтересованы в загрузке?

4. @Lifeiscomplex все, что содержит URL-адрес. Это может быть docx, csv, html или что угодно.

5. @OmidKetabollahi Спасибо. если конечный пользователь может загрузить что угодно, тогда зачем вам проверять заголовки страницы?

Ответ №1:

В соответствии с запросом на комментарий (RFC) 6266 поле заголовка Content-Disposition:

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

Поскольку заголовок Content-Disposition не всегда доступен, вы можете использовать решение, которое не только ищет этот конкретный заголовок, но и просматривает отдельные типы файлов в заголовке Content-Type

Вот список типов контента.

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

Я также добавил проверку длины содержимого, потому что это может быть полезно при разделении загружаемого файла на фрагменты.

Рассматривали ли вы возможность создания вложенных папок загрузки?

  • download_folder/text_files
  • download_folder/pdf_files

или

  • download_folder/01242021/text_files
  • download_folder/01242021/pdf_files
 import requests

urls = ['https://www.stats.govt.nz/assets/Uploads/Annual-enterprise-survey/Annual-enterprise-survey-2019-financial'
        '-year-provisional/Download-data/annual-enterprise-survey-2019-financial-year-provisional-csv.csv',
        'http://www.pdf995.com/samples/pdf.pdf', 'https://jeroen.github.io/files/sample.rtf',
        'https://www.cnn.com/2021/01/23/opinions/biden-climate-change-gillette-wyoming-coal-sutter/index.html',
        'https://www.google.com',
        'https://thumbs-prod.si-cdn.com/d4e3zqOM5KUq8m0m-AFVxuqa5ZM=/800x600/filters:no_upscale():focal(554x699:555x700)/https://public-media.si-cdn.com/filer/a4/04/a404c799-7118-459a-8de4-89e4a44b124f/img_1317.jpg',
        'https://www.blank.org']

for url in urls:
    headers = requests.head(url).headers
    Content_Length = [value for key, value in headers.items() if key == 'Content-Length']
    if len(Content_Length) > 0:
        Content_Size = ''.join(map(str, Content_Length))
    else:
        Content_Size = 'The content size was not available.'


    Content_Disposition_Exists = bool({key: value for key, value in headers.items() if key == 'Content_Disposition'})
    if Content_Disposition_Exists is True:
        # do something with the file
       pass
    else:
        Content_Type = {value for key, value in headers.items() if key == 'Content-Type'}

        compression_formats = ['application/gzip', 'application/vnd.rar', 'application/x-7z-compressed',
                               'application/zip', 'application/x-tar']
        compressed_file = bool([file_format for file_format in compression_formats if file_format in Content_Type])

        image_formats = ['image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/svg xml', 'image/tiff',
                         'image/webp']
        image_file = bool([file_format for file_format in image_formats if file_format in Content_Type])

        text_formats = ['application/rtf', 'text/plain']
        text_file = bool([file_format for file_format in text_formats if file_format in Content_Type])

        if compressed_file is True:
            print('Compressed file')
            print(Content_Size)
        elif image_file is True:
            print('Image file')
            print(Content_Size)
        elif text_file is True:
            print('Text file')
             print(Content_Size)
        elif 'application/pdf' in Content_Type:
            print('PDF file')
            print(Content_Size)
        elif 'text/csv' in Content_Type:
            print('CSV File')
            print(Content_Size)
 

Вот еще одна версия с функциями

 import requests

urls = ['https://www.stats.govt.nz/assets/Uploads/Annual-enterprise-survey/Annual-enterprise-survey-2019-financial'
        '-year-provisional/Download-data/annual-enterprise-survey-2019-financial-year-provisional-csv.csv',
        'http://www.pdf995.com/samples/pdf.pdf', 'https://jeroen.github.io/files/sample.rtf',
        'https://www.cnn.com/2021/01/23/opinions/biden-climate-change-gillette-wyoming-coal-sutter/index.html',
        'https://www.google.com',
        'https://thumbs-prod.si-cdn.com/d4e3zqOM5KUq8m0m-AFVxuqa5ZM=/800x600/filters:no_upscale():focal(554x699:555x700)/https://public-media.si-cdn.com/filer/a4/04/a404c799-7118-459a-8de4-89e4a44b124f/img_1317.jpg',
        'https://www.blank.org']


def query_headers(webpage):
    response = requests.get(webpage, stream=True)
    headers = response.headers
    file_name = webpage.rsplit('/', 1)[-1]

    Content_Disposition_Exists = bool({key: value for key, value in headers.items() if key == 'Content_Disposition'})
    if Content_Disposition_Exists is True:
        # do something with the file
        pass
    else:
        Content_Type = {value for key, value in headers.items() if key == 'Content-Type'}

        compression_formats = ['application/gzip', 'application/vnd.rar', 'application/x-7z-compressed',
                               'application/zip', 'application/x-tar']
        compressed_file = bool([file_format for file_format in compression_formats if file_format in Content_Type])

        image_formats = ['image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/svg xml', 'image/tiff',
                         'image/webp']
        image_file = bool([file_format for file_format in image_formats if file_format in Content_Type])

        text_formats = ['application/rtf', 'text/plain']
        text_file = bool([file_format for file_format in text_formats if file_format in Content_Type])
        nl = 'n'

        if compressed_file is True:
            download_file(file_name, response)
            content_size = get_content_size(headers)
            return f'File Information: file_type: Compressed file, File size: {content_size}, File name: {file_name}'
        elif image_file is True:
            download_file(file_name, response)
            content_size = get_content_size(headers)
            return f'File Information: file_type: Image file, File size: {content_size}, File name: {file_name}'
        elif text_file is True:
            download_file(file_name, response)
            content_size = get_content_size(headers)
            return f'File Information: file_type: Text file, File size: {content_size}, File name: {file_name}'
        elif 'application/pdf' in Content_Type:
            download_file(file_name, response)
            content_size = get_content_size(headers)
            return f'File Information: file_type: PDF file, File size: {content_size}, File name: {file_name}'
        elif 'text/csv' in Content_Type:
            download_file(file_name, response)
            content_size = get_content_size(headers)
            return f'File Information: file_type: CSV file, File size: {content_size}, File name: {file_name}'
        elif 'text/html' in "".join(str(Content_Type)):
            download_file(file_name, response)
            content_size = get_content_size(headers)
            return f'File Information: file_type: HTML file, File size: {content_size}, File name: {file_name}'
        else:
            content_size = get_content_size(headers)
            return f'File Information: file_type:  no file type found, File size: {content_size}, File name: {file_name}'


def get_content_size(headers):
    Content_Length = [value for key, value in headers.items() if key == 'Content-Length']
    if len(Content_Length) > 0:
        Content_Size = ''.join(map(str, Content_Length))
        return int(Content_Size)
    else:
        return 0


def download_file(filename, file_stream):
    with open(f'{filename}', 'wb') as f:
        f.write(file_stream.content)


for url in urls:
    download_info = query_headers(url)
    print(download_info)
    # output
    File Information: file_type: CSV file, File size: 253178, File name: annual-enterprise-survey-2019-financial-year-provisional-csv.csv
    File Information: file_type: PDF file, File size: 433994, File name: pdf.pdf
    File Information: file_type: Text file, File size: 9636, File name: sample.rtf
    File Information: file_type: HTML file, File size: 185243, File name: index.html
    File Information: file_type: HTML file, File size: 0, File name: www.google.com
    File Information: file_type: Image file, File size: 78868, File name: img_1317.jpg
    File Information: file_type: HTML file, File size: 170, File name: www.blank.org

 

Ответ №2:

Content-Disposition предоставляет информацию о имени файла, если она не указана в URL. Но эта информация не всегда присутствует, как в случае с вашим URL. Решением является фильтрация по типу контента, см. Пример ниже. Вы можете добавить фильтры, если хотите загрузить определенные типы контента, такие как video/mp4 .

 import requests

url = 'https://aspb1.cdn.asset.aparat.com/aparat-video/a5e07b7f62ffaad0c104763c23d7393215613675-360p.mp4?wmsAuthSign=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6IjUzMGU0Mzc3ZjRlZjVlYWU0OTFkMzdiOTZkODgwNGQ2IiwiZXhwIjoxNjExMzMzMDQxLCJpc3MiOiJTYWJhIElkZWEgR1NJRyJ9.FjMi_dkdLCUkt25dfGqPLcehpaC32dBBUNDC9cLNiu0'
headers=requests.head(url, allow_redirects=True).headers
content_type = headers.get('content-type')

if 'text' in content_type.lower():
    downloadable = False
elif 'html' in content_type.lower():
    downloadable =  False
else:
    downloadable = True

print(downloadable)
 

Ответ №3:

Я думаю, что ваш прежний код работает, но с небольшими изменениями. Он пытается загрузить полный файл, из-за чего он зависает при каждом запуске

 import requests
url = 'https://aspb1.cdn.asset.aparat.com/aparat-video/a5e07b7f62ffaad0c104763c23d7393215613675-360p.mp4?wmsAuthSign=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6IjUzMGU0Mzc3ZjRlZjVlYWU0OTFkMzdiOTZkODgwNGQ2IiwiZXhwIjoxNjExMzMzMDQxLCJpc3MiOiJTYWJhIElkZWEgR1NJRyJ9.FjMi_dkdLCUkt25dfGqPLcehpaC32dBBUNDC9cLNiu0'
r = requests.get(url,stream=True)


try:
    print(r.headers)
    #if "Content-Length" in r.headers:
    file_size = int(r.headers["Content-Length"])
except KeyError:
    # Just a class that I defined to raise an exception if the URL was not downloadable
    raise NotDownloadable()
 

Используйте stream=True

 r = requests.get(url,stream=True)
 

Это не объясняется в документации пользователя. Но, по предположению, мы можем сказать, что выполняется фрагментированное кодирование передачи, поток данных разделяется на серию неперекрывающихся «фрагментов». Куски отправляются сервером независимо друг от друга.

Ответ №4:

Вы можете проверить заголовок ответа типа содержимого. Этот заголовок определяет тип носителя запрашиваемого ресурса. Здесь показаны наиболее распространенные типы.

content-type Заголовок определяется type "/" subtype , а некоторые также включают параметр, задающий ему формат type "/" subtype ";" parameter , с параметром в форме attribute "=" value . Значение параметра не является обязательным, но тип и подтип являются.

В настоящее время существует 7 типов, определенных в RFC 134:

текст составное приложение сообщение изображение аудио видео

Заголовок, который вы ищете, зависит от ожидаемого ресурса, но некоторые примеры вы можете использовать.

Примеры

Загрузить изображение

 import requests

response = requests.head(url)
response_headers = response.headers
response_content_type = response_headers.get("content-type")

# you could use this code to search for all images using just the type

if response_content_type.lower().split("/")[0] == "image":
    is_image = True
else:
    is_image = False

# alternatively you could specify your expected content-types including the subtype

CONTENT_TYPES = ["image/gif", "image/jpeg", "image/png", "image/tiff", "image.svg xml"...]

if response_content_type.lower() in CONTENT_TYPES:
    is_image = True
else:
    is_image = False

if is_image:
    # code to download image
 

Этот код можно легко адаптировать для разных типов и подтипов.

Примечание

Стоит отметить, что типы являются фиксированными, вы не можете определить новый подтип, но вы можете определить новый подтип.