Как мне загрузить файл через HTTP, используя Python?


875

У меня есть небольшая утилита, которую я использую для загрузки файла MP3 с веб-сайта по расписанию, а затем для создания / обновления файла XML подкаста, который я добавил в iTunes.

Обработка текста, который создает / обновляет файл XML, написана на Python. Тем не менее, я использую wget внутри .batфайла Windows для загрузки самого файла MP3. Я бы предпочел, чтобы вся утилита была написана на Python.

Я изо всех сил пытался найти способ действительно загрузить файл в Python, поэтому я прибег к использованию wget.

Итак, как мне загрузить файл с помощью Python?



Многие из ответов ниже не являются удовлетворительной заменой wget. Помимо прочего, wget(1) сохраняет метки времени (2) автоматически определяет имя файла по URL, добавляя .1(и т. Д.), Если файл уже существует (3), имеет много других опций, некоторые из которых вы, возможно, добавили в свой .wgetrc. Если вы хотите что-то из этого, вы должны сами реализовать их в Python, но проще просто вызвать wgetиз Python.
ShreevatsaR

2
Краткое решение для Python 3:import urllib.request; s = urllib.request.urlopen('http://example.com/').read().decode()
Basj

Ответы:


450

В Python 2 используйте urllib2, который поставляется со стандартной библиотекой.

import urllib2
response = urllib2.urlopen('http://www.example.com/')
html = response.read()

Это самый простой способ использования библиотеки, за исключением обработки ошибок. Вы также можете делать более сложные вещи, такие как смена заголовков. Документацию можно найти здесь.


11
Это не будет работать, если в предоставленной вами ссылке есть пробелы. В этом случае вам нужно будет проанализировать URL-адрес и urlencode пути.
Джейсон Сундрам

91
Вот Python 3 Решение: stackoverflow.com/questions/7243750/...
tommy.carstensen

6
Просто для справки. Путь к urlencode путиurllib2.quote
Андре Пуэль

11
@JasonSundram: Если в нем есть пробелы, это не URI.
Zaz

1
Это не работает на окнах с большими файлами. Вам нужно прочитать все блоки!
Авиа

1115

Еще один, используя urlretrieve:

import urllib
urllib.urlretrieve ("http://www.example.com/songs/mp3.mp3", "mp3.mp3")

(для Python 3+ используйте import urllib.requestи urllib.request.urlretrieve)

Еще один, с "прогрессбаром"

import urllib2

url = "http://download.thinkbroadband.com/10MB.zip"

file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)

file_size_dl = 0
block_sz = 8192
while True:
    buffer = u.read(block_sz)
    if not buffer:
        break

    file_size_dl += len(buffer)
    f.write(buffer)
    status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
    status = status + chr(8)*(len(status)+1)
    print status,

f.close()

1
Как ни странно, это работало для меня в Windows, когда метод urllib2 не работал. Однако метод urllib2 работал на Mac.
InFreefall

6
Ошибка: file_size_dl + = block_sz должен быть + = len (буфер), так как последнее чтение часто не полное block_sz. Также в Windows вам нужно открыть выходной файл как «wb», если это не текстовый файл.
Баклажан Джефф

1
Я тоже urllib и urllib2 не работали, но urlretrieve работал хорошо, расстраивался - спасибо :)
funk-shun

2
Оберните все это (кроме определения file_name), if not os.path.isfile(file_name):чтобы избежать перезаписи подкастов! полезно, когда запускается как cronjob с URL-адресами, найденными в файле .html
Sriram Murali

2
@PabloG это чуть больше, чем 31 голос;) В любом случае, строка состояния была забавной, поэтому я буду +1
Cinder

340

В 2012 году используйте библиотеку запросов Python

>>> import requests
>>> 
>>> url = "http://download.thinkbroadband.com/10MB.zip"
>>> r = requests.get(url)
>>> print len(r.content)
10485760

Вы можете бежать, pip install requestsчтобы получить это.

Запросы имеют много преимуществ по сравнению с альтернативами, потому что API намного проще. Это особенно верно, если вам нужно сделать аутентификацию. urllib и urllib2 довольно неинтуитивны и болезненны в этом случае.


2015-12-30

Люди выразили восхищение прогресс-баром. Это круто, конечно. В настоящее время существует несколько готовых решений, в том числе tqdm:

from tqdm import tqdm
import requests

url = "http://download.thinkbroadband.com/10MB.zip"
response = requests.get(url, stream=True)

with open("10MB", "wb") as handle:
    for data in tqdm(response.iter_content()):
        handle.write(data)

По сути, это реализация @kvance, описанная 30 месяцев назад.


как сохранить или распаковать, если файл zip на самом деле является папкой с большим количеством файлов?
Абдул Мьюнер

6
Как это обрабатывает большие файлы, все ли хранится в памяти или это может быть записано в файл без большого объема памяти?
bibstha

8
Для потоковой передачи больших файлов можно указать в запросе значение stream = True. Затем вы можете вызвать iter_content () в ответе, чтобы прочитать порцию за раз.
Кванс

7
Почему в библиотеке URL должна быть возможность распаковать файл? Прочитайте файл с URL-адреса, сохраните его, а затем распакуйте его любым способом, который плавает на вашей лодке. Также zip-файл - это не папка, как в Windows, это файл.
Харель

2
@Ali: r.textдля текстового или юникодного контента. Возвращено как Unicode. r.content: Для двоичного содержимого. Возвращается в байтах. Прочитайте об этом здесь: docs.python-requests.org/en/latest/user/quickstart
hughdbrown

159
import urllib2
mp3file = urllib2.urlopen("http://www.example.com/songs/mp3.mp3")
with open('test.mp3','wb') as output:
  output.write(mp3file.read())

wbВ open('test.mp3','wb')открывает файл (и удаляет любой существующий файл) в двоичном режиме , так что вы можете сохранить данные с него , а не просто текст.


30
Недостаток этого решения заключается в том, что перед загрузкой на диск весь файл загружается в оперативную память, что следует иметь в виду, если использовать это для больших файлов в небольшой системе, такой как маршрутизатор с ограниченной памятью.
tripplet

2
@ tripplet, как бы мы это исправили?
Лукас Энрике

11
Чтобы избежать чтения всего файла в память, попробуйте передать аргумент file.read, равный количеству байтов для чтения. Смотрите: gist.github.com/hughdbrown/c145b8385a2afa6570e2
hughdbrown

@hughdbrown Я нашел ваш сценарий полезным, но у меня есть один вопрос: могу ли я использовать файл для постобработки? предположим, что я загружаю файл jpg, который хочу обработать с помощью OpenCV, могу ли я использовать переменную data для продолжения работы? или я должен снова прочитать его из загруженного файла?
Родриго Э. Принсипи

5
Используйте shutil.copyfileobj(mp3file, output)вместо этого.
Орелиен Оомс

130

Python 3

  • urllib.request.urlopen

    import urllib.request
    response = urllib.request.urlopen('http://www.example.com/')
    html = response.read()
  • urllib.request.urlretrieve

    import urllib.request
    urllib.request.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')

    Примечание: согласно документации, urllib.request.urlretrieveэто «устаревший интерфейс» и «может устареть в будущем» (спасибо, геррит )

Python 2

  • urllib2.urlopen(спасибо Кори )

    import urllib2
    response = urllib2.urlopen('http://www.example.com/')
    html = response.read()
  • urllib.urlretrieve(спасибо, ПаблоГ )

    import urllib
    urllib.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')

2
Конечно, это заняло какое-то время, но вот, наконец-то, это простой и понятный API, который я ожидаю от python stdlib :)
ThorSummoner

Очень хороший ответ для python3, см. Также docs.python.org/3/library/…
Эдуард Тиль

@EdouardThiel Если вы нажмете на urllib.request.urlretrieveвыше, это приведет вас к этой точной ссылке. Ура!
bmaupin

2
urllib.request.urlretrieveзадокументировано как «устаревший интерфейс» и «может устареть в будущем».
Gerrit

@gerrit Я добавил записку, спасибо за внимание!
bmaupin


21

Улучшенная версия кода PabloG для Python 2/3:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import ( division, absolute_import, print_function, unicode_literals )

import sys, os, tempfile, logging

if sys.version_info >= (3,):
    import urllib.request as urllib2
    import urllib.parse as urlparse
else:
    import urllib2
    import urlparse

def download_file(url, dest=None):
    """ 
    Download and save a file specified by url to dest directory,
    """
    u = urllib2.urlopen(url)

    scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
    filename = os.path.basename(path)
    if not filename:
        filename = 'downloaded.file'
    if dest:
        filename = os.path.join(dest, filename)

    with open(filename, 'wb') as f:
        meta = u.info()
        meta_func = meta.getheaders if hasattr(meta, 'getheaders') else meta.get_all
        meta_length = meta_func("Content-Length")
        file_size = None
        if meta_length:
            file_size = int(meta_length[0])
        print("Downloading: {0} Bytes: {1}".format(url, file_size))

        file_size_dl = 0
        block_sz = 8192
        while True:
            buffer = u.read(block_sz)
            if not buffer:
                break

            file_size_dl += len(buffer)
            f.write(buffer)

            status = "{0:16}".format(file_size_dl)
            if file_size:
                status += "   [{0:6.2f}%]".format(file_size_dl * 100 / file_size)
            status += chr(13)
            print(status, end="")
        print()

    return filename

if __name__ == "__main__":  # Only run if this file is called directly
    print("Testing with 10MB download")
    url = "http://download.thinkbroadband.com/10MB.zip"
    filename = download_file(url)
    print(filename)

Я бы убрал скобки из первой строки, потому что это не слишком старая функция.
Арпад Хорват

21

Простой, но Python 2 & Python 3совместимый способ поставляется с sixбиблиотекой:

from six.moves import urllib
urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")

1
Это лучший способ сделать это для совместимости 2 + 3.
Фуш

21
import os,requests
def download(url):
    get_response = requests.get(url,stream=True)
    file_name  = url.split("/")[-1]
    with open(file_name, 'wb') as f:
        for chunk in get_response.iter_content(chunk_size=1024):
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)


download("https://example.com/example.jpg")

17

Написал библиотеку wget на чистом Python как раз для этого. Он накачивается urlretrieveс этими функциями , начиная с версии 2.0.


3
Нет возможности сохранить с пользовательским именем файла?
Алекс

2
@Alex добавил опцию -o FILENAME в версию 2.1
анатолий techtonik

Индикатор выполнения не появляется, когда я использую этот модуль под Cygwin.
Джо Кодер

Вам следует изменить с -oна, -Oчтобы избежать путаницы, как в GNU wget. Или, по крайней мере, оба варианта должны быть действительными.
Эрик

@eric Я не уверен, что хочу сделать wget.pyзамену на месте по-настоящему wget. -oУже ведет себя по- разному - это совместимо с curlэтим способом. Поможет ли примечание в документации решить проблему? Или для утилиты с таким именем важно совместимость с командной строкой?
анатолий техтоник

16

Ниже приведены наиболее часто используемые вызовы для загрузки файлов в Python:

  1. urllib.urlretrieve ('url_to_file', file_name)

  2. urllib2.urlopen('url_to_file')

  3. requests.get(url)

  4. wget.download('url', file_name)

Примечание: urlopenи urlretrieveобнаружено, что он работает относительно плохо при загрузке больших файлов (размер> 500 МБ). requests.getсохраняет файл в памяти до завершения загрузки.


14

Я согласен с Кори, urllib2 является более полным, чем urllib, и, вероятно, должен использоваться как модуль, если вы хотите делать более сложные вещи, но чтобы сделать ответы более полными, urllib - более простой модуль, если вам нужны только основы:

import urllib
response = urllib.urlopen('http://www.example.com/sound.mp3')
mp3 = response.read()

Будет работать нормально. Или, если вы не хотите иметь дело с объектом «response», вы можете вызвать read () напрямую:

import urllib
mp3 = urllib.urlopen('http://www.example.com/sound.mp3').read()

10

В python3 вы можете использовать библиотеки urllib3 и shutil. Загрузите их, используя pip или pip3 (в зависимости от того, используется ли python3 по умолчанию)

pip3 install urllib3 shutil

Затем запустите этот код

import urllib.request
import shutil

url = "http://www.somewebsite.com/something.pdf"
output_file = "save_this_name.pdf"
with urllib.request.urlopen(url) as response, open(output_file, 'wb') as out_file:
    shutil.copyfileobj(response, out_file)

Обратите внимание, что вы загружаете, urllib3но используете urllibв коде


7

Вы также можете получить отзыв о прогрессе с помощью urlretrieve:

def report(blocknr, blocksize, size):
    current = blocknr*blocksize
    sys.stdout.write("\r{0:.2f}%".format(100.0*current/size))

def downloadFile(url):
    print "\n",url
    fname = url.split('/')[-1]
    print fname
    urllib.urlretrieve(url, fname, report)

7

Если у вас установлен wget, вы можете использовать parallel_sync.

pip install parallel_sync

from parallel_sync import wget
urls = ['http://something.png', 'http://somthing.tar.gz', 'http://somthing.zip']
wget.download('/tmp', urls)
# or a single file:
wget.download('/tmp', urls[0], filenames='x.zip', extract=True)

Документ: https://pythonhosted.org/parallel_sync/pages/examples.html.

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


Обратите внимание, что это только для Linux
JJJ

4

Если скорость имеет значение для вас, я сделал небольшой тест производительности для модулей urllibи wget, и относительноwget я попробовал один раз с строкой состояния и один раз без. Я взял три разных файла размером 500 МБ для тестирования (разные файлы - чтобы исключить вероятность того, что под капотом происходит какое-то кэширование). Протестировано на машине Debian, с python2.

Во-первых, это результаты (они похожи в разных прогонах):

$ python wget_test.py 
urlretrive_test : starting
urlretrive_test : 6.56
==============
wget_no_bar_test : starting
wget_no_bar_test : 7.20
==============
wget_with_bar_test : starting
100% [......................................................................] 541335552 / 541335552
wget_with_bar_test : 50.49
==============

Я выполнил тест с использованием декоратора профиля. Это полный код:

import wget
import urllib
import time
from functools import wraps

def profile(func):
    @wraps(func)
    def inner(*args):
        print func.__name__, ": starting"
        start = time.time()
        ret = func(*args)
        end = time.time()
        print func.__name__, ": {:.2f}".format(end - start)
        return ret
    return inner

url1 = 'http://host.com/500a.iso'
url2 = 'http://host.com/500b.iso'
url3 = 'http://host.com/500c.iso'

def do_nothing(*args):
    pass

@profile
def urlretrive_test(url):
    return urllib.urlretrieve(url)

@profile
def wget_no_bar_test(url):
    return wget.download(url, out='/tmp/', bar=do_nothing)

@profile
def wget_with_bar_test(url):
    return wget.download(url, out='/tmp/')

urlretrive_test(url1)
print '=============='
time.sleep(1)

wget_no_bar_test(url2)
print '=============='
time.sleep(1)

wget_with_bar_test(url3)
print '=============='
time.sleep(1)

urllib кажется самым быстрым


Должно быть, что-то совершенно ужасное происходит под капотом, чтобы штанга так сильно увеличивала время.
Алистер Карскэдден

4

Для полноты картины можно также вызвать любую программу для получения файлов с помощью subprocessпакета. Программы, предназначенные для извлечения файлов, более мощные, чем функции Python urlretrieve. Например, wgetможет загружать каталоги рекурсивно ( -R), может иметь дело с FTP, перенаправлениями, HTTP-прокси, может избежать повторной загрузки существующих файлов ( -nc) и aria2может выполнять загрузку с несколькими подключениями, что потенциально может ускорить загрузку.

import subprocess
subprocess.check_output(['wget', '-O', 'example_output_file.html', 'https://example.com'])

В Jupyter Notebook можно также вызывать программы напрямую с помощью !синтаксиса:

!wget -O example_output_file.html https://example.com

3

Исходный код может быть:

import urllib
sock = urllib.urlopen("http://diveintopython.org/")
htmlSource = sock.read()                            
sock.close()                                        
print htmlSource  

3

Вы можете использовать PycURL на Python 2 и 3.

import pycurl

FILE_DEST = 'pycurl.html'
FILE_SRC = 'http://pycurl.io/'

with open(FILE_DEST, 'wb') as f:
    c = pycurl.Curl()
    c.setopt(c.URL, FILE_SRC)
    c.setopt(c.WRITEDATA, f)
    c.perform()
    c.close()

2

Я написал следующее, которое работает в ванильном Python 2 или Python 3.


import sys
try:
    import urllib.request
    python3 = True
except ImportError:
    import urllib2
    python3 = False


def progress_callback_simple(downloaded,total):
    sys.stdout.write(
        "\r" +
        (len(str(total))-len(str(downloaded)))*" " + str(downloaded) + "/%d"%total +
        " [%3.2f%%]"%(100.0*float(downloaded)/float(total))
    )
    sys.stdout.flush()

def download(srcurl, dstfilepath, progress_callback=None, block_size=8192):
    def _download_helper(response, out_file, file_size):
        if progress_callback!=None: progress_callback(0,file_size)
        if block_size == None:
            buffer = response.read()
            out_file.write(buffer)

            if progress_callback!=None: progress_callback(file_size,file_size)
        else:
            file_size_dl = 0
            while True:
                buffer = response.read(block_size)
                if not buffer: break

                file_size_dl += len(buffer)
                out_file.write(buffer)

                if progress_callback!=None: progress_callback(file_size_dl,file_size)
    with open(dstfilepath,"wb") as out_file:
        if python3:
            with urllib.request.urlopen(srcurl) as response:
                file_size = int(response.getheader("Content-Length"))
                _download_helper(response,out_file,file_size)
        else:
            response = urllib2.urlopen(srcurl)
            meta = response.info()
            file_size = int(meta.getheaders("Content-Length")[0])
            _download_helper(response,out_file,file_size)

import traceback
try:
    download(
        "https://geometrian.com/data/programming/projects/glLib/glLib%20Reloaded%200.5.9/0.5.9.zip",
        "output.zip",
        progress_callback_simple
    )
except:
    traceback.print_exc()
    input()

Ноты:

  • Поддерживает обратный вызов «индикатор выполнения».
  • Скачайте 4 МБ теста .zip с моего сайта.

отлично работает, прогнать его через Jupyter получил то, что я хочу :-)
Самир Ouldsaadi

1

Это может быть немного поздно, но я видел код pabloG и не мог не добавить os.system ('cls'), чтобы он выглядел УДИВИТЕЛЬНО! Проверьте это:

    import urllib2,os

    url = "http://download.thinkbroadband.com/10MB.zip"

    file_name = url.split('/')[-1]
    u = urllib2.urlopen(url)
    f = open(file_name, 'wb')
    meta = u.info()
    file_size = int(meta.getheaders("Content-Length")[0])
    print "Downloading: %s Bytes: %s" % (file_name, file_size)
    os.system('cls')
    file_size_dl = 0
    block_sz = 8192
    while True:
        buffer = u.read(block_sz)
        if not buffer:
            break

        file_size_dl += len(buffer)
        f.write(buffer)
        status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
        status = status + chr(8)*(len(status)+1)
        print status,

    f.close()

Если вы работаете в среде, отличной от Windows, вам придется использовать что-то другое, чем «cls». В MAC OS X и Linux это должно быть «ясно».


3
clsничего не делает ни на моей OS X, ни на моем сервере Ubuntu. Некоторые разъяснения могут быть хорошими.
Кв Кв

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

4
этот ответ просто копирует другой ответ и добавляет вызов к устаревшей функции ( os.system()), которая запускает подпроцесс, чтобы очистить экран с помощью специальной команды платформы ( cls). Как это имеет какие-либо голоса ?? Совершенно бесполезный "ответ" ИМХО.
Кори Голдберг

1

urlretrieve иquesses.get просты, однако реальность нет. Я получил данные для пары сайтов, включая текст и изображения, два из которых, вероятно, решают большинство задач. но для более универсального решения я предлагаю использовать урлопен. Поскольку он включен в стандартную библиотеку Python 3, ваш код может работать на любом компьютере, на котором запущен Python 3 без предварительной установки site-package.

import urllib.request
url_request = urllib.request.Request(url, headers=headers)
url_connect = urllib.request.urlopen(url_request)

#remember to open file in bytes mode
with open(filename, 'wb') as f:
    while True:
        buffer = url_connect.read(buffer_size)
        if not buffer: break

        #an integer value of size of written data
        data_wrote = f.write(buffer)

#you could probably use with-open-as manner
url_connect.close()

Этот ответ обеспечивает решение HTTP 403 Запрещено при загрузке файла через http с использованием Python. Я пробовал только запросы и модули urllib, другой модуль может обеспечить что-то лучшее, но именно этот я использовал для решения большинства проблем.


0

Поздний ответ, но для python>=3.6вас можно использовать:

import dload
dload.save(url)

Установить dloadс помощью:

pip3 install dload

0

Я хотел скачать все файлы с веб-страницы. Я пытался, wgetно это не удавалось, поэтому я выбрал маршрут Python и нашел эту ветку.

Прочитав его, я сделал небольшое приложение для командной строки, подробно остановившисьsoupget на превосходных ответах PabloG и Stan и добавив несколько полезных опций.

Он использует BeatifulSoup, чтобы собрать все URL-адреса страницы, а затем загрузить те с нужным расширением (ями). Наконец он может загружать несколько файлов параллельно.

Вот:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import (division, absolute_import, print_function, unicode_literals)
import sys, os, argparse
from bs4 import BeautifulSoup

# --- insert Stan's script here ---
# if sys.version_info >= (3,): 
#...
#...
# def download_file(url, dest=None): 
#...
#...

# --- new stuff ---
def collect_all_url(page_url, extensions):
    """
    Recovers all links in page_url checking for all the desired extensions
    """
    conn = urllib2.urlopen(page_url)
    html = conn.read()
    soup = BeautifulSoup(html, 'lxml')
    links = soup.find_all('a')

    results = []    
    for tag in links:
        link = tag.get('href', None)
        if link is not None: 
            for e in extensions:
                if e in link:
                    # Fallback for badly defined links
                    # checks for missing scheme or netloc
                    if bool(urlparse.urlparse(link).scheme) and bool(urlparse.urlparse(link).netloc):
                        results.append(link)
                    else:
                        new_url=urlparse.urljoin(page_url,link)                        
                        results.append(new_url)
    return results

if __name__ == "__main__":  # Only run if this file is called directly
    # Command line arguments
    parser = argparse.ArgumentParser(
        description='Download all files from a webpage.')
    parser.add_argument(
        '-u', '--url', 
        help='Page url to request')
    parser.add_argument(
        '-e', '--ext', 
        nargs='+',
        help='Extension(s) to find')    
    parser.add_argument(
        '-d', '--dest', 
        default=None,
        help='Destination where to save the files')
    parser.add_argument(
        '-p', '--par', 
        action='store_true', default=False, 
        help="Turns on parallel download")
    args = parser.parse_args()

    # Recover files to download
    all_links = collect_all_url(args.url, args.ext)

    # Download
    if not args.par:
        for l in all_links:
            try:
                filename = download_file(l, args.dest)
                print(l)
            except Exception as e:
                print("Error while downloading: {}".format(e))
    else:
        from multiprocessing.pool import ThreadPool
        results = ThreadPool(10).imap_unordered(
            lambda x: download_file(x, args.dest), all_links)
        for p in results:
            print(p)

Пример его использования:

python3 soupget.py -p -e <list of extensions> -d <destination_folder> -u <target_webpage>

И реальный пример, если вы хотите увидеть это в действии:

python3 soupget.py -p -e .xlsx .pdf .csv -u https://healthdata.gov/dataset/chemicals-cosmetics
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.