Время прочтения: 4 мин.

Sharepoint – один из наиболее встречающихся инструментов для организации веб-порталов в больших организациях. Ключевым преимуществом системы является удобное прототипирование, создание списков с данными и т.д.

В рамках одной из систем в организации реализована такая схема – есть проект и по каждому проекту хранятся файлы в материалах Sharepoint.

Была поставлена задача – нужно выкачать файлы за весь период, а за 5 лет их накопилось очень много (примерно 122 тысячи файлов), и,соответственно, решать эту задачу руками – равноценно тому, чтобы остаться жить на работе на ближайшие десять лет.

Для начала приведем схему, как сейчас реализовано взаимодействие.

Ссылки на материалы обычно имеют такой вид:

Для начала из БД сервиса через SQL запрос сформируем ссылки на материалы для всего необходимого списка.

Как хранятся данные может сильно отличаться в зависимости от организации, в моем случае я просто сджойнил ИД документов с БД Sharepoint и на выходе получил необходимые ссылки.

Итак, у нас есть csv файл (ссылки по понятным причинам пришлось скрыть), который хранит в себе все ссылки на необходимые нам материалы.

Возьмем python и для скачивания будем использовать библиотеку requests.

Первым делом авторизуемся по логину/паролю Windows. Для этого используем модуль авторизации HttpNtlmAuth из пакета requests_ntlm.

Чтобы получить список файлов – используем запрос к api, который вернет нам xml-документ с ссылками на материалы.

@staticmethod
def is_remote_host_available(host_link, username, password):
    return requests.get(host_link + '/_api/files/', auth=HttpNtlmAuth(username, password), verify=False)

Полученный xml хранит в себе уже непосредственные ссылки на файлы, которые мы можем распарсить, а потом скачать.

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

@staticmethod
def download_specified_files(response, username, password, output_path):

    try:
        doc = ET.fromstring(response.text)
    except AttributeError:
        return False

    tree = ET.ElementTree(doc)
    xml_to_parse = tree.findall('{http://www.w3.org/2005/Atom}entry')

    # если пустая, пропускаем номер и даем False
    if len(xml_to_parse) < 1:
        return False

    temp_links_list = SharepointRequest.__generate_download_links(xml_to_parse, output_path)

    for file_name, file_link in temp_links_list.items():
        status = False

        status = SharepointRequest.download_files(file_link, file_name, username, password)

        if status:
            continue
        else:
            time.sleep(5)
staticmethod
def __generate_download_links(xml_from_request, output_path):
    temp_links_list = {}
    for entry in xml_from_request:
        content = entry.find('{http://www.w3.org/2005/Atom}content')
        properties = content.find('{http://schemas.microsoft.com/ado/2007/08/dataservices/metadata}properties')
        url = properties.find('{http://schemas.microsoft.com/ado/2007/08/dataservices}Url')

        if (url.text != ""):
            # Получаем ссылку на файл из xml и вытаскиваем все нужное
            download_link = url.text
            download_file_name = ''.join([output_path,'/', url.text.split('/')[-1]])

            temp_links_list[download_file_name] = download_link

    return temp_links_list

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

Для визуализации прогресса, используем iter_content

def download_files(host_link, file_name, username, password):
    with open(file_name, 'wb') as file_loading:
        print(f"Downloading {file_name}")
        response = requests.get(host_link, auth=HttpNtlmAuth(username, password), verify=False, stream=True)
        total_file_length = response.headers.get('Content-Length')
        # Берем длину файла
        if total_file_length is None:
            file_loading.write(response.content)
        else:
            dl = 0
            total_file_length = int(total_file_length)
            for data in response.iter_content(chunk_size=8192):
                dl += len(data)

                file_loading.write(data)
                done = int(50 * dl / total_file_length)
                sys.stdout.write("\r[%s%s]" % ('=' * done, ' ' * (50 - done)))
                sys.stdout.flush()
                if dl == total_file_length:
                    return True

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

На этом все. Данное решение позволило мне выкачать около 500 гб файлов за 2 дня. Используйте в работе!