Скачать файл из интернета без использования WinAPI

Часто требуется макросом скачать некий файл из интернета.
Обычно в этом помогает WinAPI-функция URLDownloadToFile, но есть также возможность загрузить файл без её использования:

Чем чревато использование функции URLDownloadToFile - по сути, ничем, кроме как необходимостью прописывать её в 2 вариантах, для обеспечения совместимости с 64-битной Windows

#If VBA7 Then        '  Office 2010-2013
    Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
            (ByVal pCaller As LongPtr, ByVal szURL As String, ByVal szFileName As String, _
             ByVal dwReserved As LongPtr, ByVal lpfnCB As LongPtr) As LongPtr
#Else        '  Office 2003-2007
    Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
                                       (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, _
                                        ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
#End If
 
Function DownLoadFileFromURL(ByVal URL$, ByVal LocalPath$, Optional ByVal DisableCache As Boolean = False) As Boolean
    On Error Resume Next
    If (LocalPath$ = "") Or (URL$ = "") Then Exit Function
    If Not LocalPath$ Like "*\*" Then LocalPath$ = Environ("TEMP") & "\" & LocalPath$
    Kill LocalPath$
    If DisableCache Then Randomize: URL$ = URL$ & "?rnd=" & Left(Rnd(Now) * 1E+15, 10)
    DownLoadFileFromURL = URLDownloadToFile(0, URL$, LocalPath$, 0, 0) = 0
End Function

Я же предлагаю другое решение - функцию DownloadFile с использованием объектов Microsoft.XMLHTTP и ADODB.Stream:

 

Sub ПримерИспользования()
    СсылкаНаФайл$ = "http://excelvba.ru/sites/default/files/3.jpg"
    ПутьДляСохранения$ = "C:\1.jpg"
 
    ' скачиваем файл из интернета
    DownloadFile СсылкаНаФайл$, ПутьДляСохранения$
 
    ' открываем скачанный файл
    CreateObject("wscript.shell").Run """" & ПутьДляСохранения$ & """"
End Sub

Function DownloadFile(ByVal URL$, ByVal LocalPath$) As Boolean
    ' Функция скачивает файл по ссылке URL$
    ' и сохраняет его под именем LocalPath$
    Dim XMLHTTP, ADOStream, FileName
    On Error Resume Next: Kill LocalPath$
 
    Set XMLHTTP = CreateObject("Microsoft.XMLHTTP")
    XMLHTTP.Open "GET", Replace(URL$, "\", "/"), "False"
    XMLHTTP.send
    If XMLHTTP.statustext = "OK" Then
        Set ADOStream = CreateObject("ADODB.Stream")
        ADOStream.Type = 1: ADOStream.Open
        ADOStream.Write XMLHTTP.responseBody
 
        ADOStream.SaveToFile LocalPath$, 2
        ADOStream.Close: Set ADOStream = Nothing
        DownloadFile = True
    Else
        'MsgBox "Не удаётся скачать файл " & XMLHTTP.statustext
    End If
    Set XMLHTTP = Nothing
End Function

Комментарии

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Ребят, подскажите как парсером скачивать файл, который доступен только после авторизации?

CreateObject("Microsoft.XMLHTTP") Вот такой объект кэшировал ответ от сервера где-то в excel.. CreateObject("WinHttp.WinHttpRequest.5.1") вот это решило проблему.

как вариант, можно добавлять в конец ссылки что-то типа ?a=123
тогда кешироваться не будет

Здравствуйте
Скачиваю файлы этим способом, но похоже файлы кэшируются и при ежедневном запуске макроса у некоторых файлов последняя дата создания и изменения висит по 2-3 недели без изменений, т.е. актуальный файл скачать не получается. Подскажите, пожалуйста, как это можно исправить?
Сейчас макрос выглядит так: http://prntscr.com/jr2pxr
Пробовал добавлять в него строчки заголовков, но не помогает

Доброго времени суток, если пытаюсь выполнять эту функцию через определенный промежуток времени, то файл сохраняется самый первый(хотя на сайте обновляется).
Public NextTime As Date
sub DLoad
sFile="d:\!\test.xls"
СсылкаНаФайл$ = "http://.../test.xls"
ПутьДляСохранения$ = sFile
DownloadFile СсылкаНаФайл$, ПутьДляСохранения$
Set xlWbk1 = Workbooks.Open(sFile)
...
sFileItog= "d:\!\test"&"_n"&".xls"
xlWbk1.SaveAs (sFileItog)
xlWbk1.Close
Set xlWbk1 = Nothing

NextTime = Now() + TimeValue("00:10:00")
Application.OnTime NextTime, "DLoad"
end sub

Надо применить к ссылке преобразование URLencode
http://excelvba.ru/code/URLEncode

Добрый день. Спасибо за отличную функцию, работает на ура! Но не работает, если в ссылке есть русские буквы. Можете подсказать что делать в этом случае? Пожалуйста.

В коде по-русски написано, что последняя команда открывает скачанный файл:

 ' открываем скачанный файл
    CreateObject("wscript.shell").Run """" & ПутьДляСохранения$ & """"

Попробовал скачать .jpg вашим макросом. По окончании скачки почему-то картинка открывается программой просмотра фотографий. Как устранить это?

Здравствуйте Игорь. Благодарю Вас за предложение. Я уже решил свою проблему благодаря Вашей функции GetURLstatus, спасибо и за неё.

Здравствуйте, Кирилл.
Это сделать можно (чтобы таймаут можно было задать), - но код надо полностью переписывать.
Могу написать функцию для скачивания файлов под заказ, если готовы оплатить

Добрый дань! Спасибо за макрос. Подскажите пожалуйста , как решить проблему, Ваш код файлы качает на ура но, как только сайт начинает виснуть или отключатся макрос зависает вместе с ним. Как выходить из макроса если он не отвечает больше 5 секунд? Заранее спасибо

Проблема непонятна, - не понимаю, как может быть такое:
1) во внутренностях файла Эксель не хватает 2 файлов
(когда там в принципе никаких файлов во внутренностях быть не должно)
2) какая программа выдает сообщение, что диск не может быть открыт?
причем тут устаревший браузер?
Всё это не должно иметь никакого отношения к файлу Excel

В любом случае, проблема тут не в коде
Возможно, для вашей задачи код нужен несколько иной, - посложнее, что-то типа такого
http://excelvba.ru/code/DownloadFileWithAuth

Обнаружил следующую проблему:
Скачиваю с яндекс-диска по публичной ссылке следующего вида: https://yadi.sk/d/7Hi5WQ79_тратата....
Обычный файл с расширением xls. У меня стоит уже устаревшая Windows XP и 2007 офис.

Файл файл скачивается, но при открытии экселевского файла он сообщает что в его внутренностях не хватает как минимум 2 файлов. И демонстрирует мне следующую надпись: ДИСК НЕ МОЖЕТ БЫТЬ ОТКРЫТ, тк ваш браузер устарел. А содержимое файла отсутствует...

В то же время с со старого сайта на НАРОДЕ -нормальные ссылки с полным указанием пути к файлу - качаются без проблем.

Т.е оба кода не качают ПУБЛИЧНЫЕ ССЫЛКИ, в случае работы в устаревшей системе с устаревшим IE... Или есть какая то иная возможность?

Спасибо большое за отличный пример!
У меня вопрос такой: с одного источника файлы скачиваются нормально, а с другого - вместо того содержимого, которое должно быть, файл содержит кусок html-кода веб-страницы, с которой я его беру. При этом, если просто подставить в браузер ссылку, файл скачивается.
И zip-файл, скачанный с этого же сайта, не хочет открываться.
Подскажите, пожалуйста, почему такое может происходить?

узнать размер файла после его скачивания - не проблема,
для этого в VBA есть функция filelen(ПолныйПутьКФайлу)

Благодарю, работает! Если не секрет, то как узнать размер загружаемого файла???

Сергей, проблема не в моём макросе, — а в том, что каспер распознает ваш файл как подозрительный.
Как макрос не переделывай, - тут ничем не помочь.
Разбирайтесь либо с файлом, либо с техподдержкой касперского.

Доброго, вначале работало на ура, но сейчас еба***й касперский удаляет файл сходу...
Есть вариант реализовать скачивание без подобных акцессов?

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

Здравствуйте, JeyCi.
Код можно использовать и в VBS (только там есть некоторые отличия, не все из которых я помню)
Например, надо убрать все объявления переменных, в частности, удалить строку Dim XMLHTTP, ADOStream, FileName
А в остальном, - должно работать.

кстати, только один небольшой вопрос есть - можно ли этот код использовать для vbs скрипта?.. я попробовала, но получила ошибку в строке1 "XMLHTTP" объект как-то не задаётся видимо CreateObject'ом... этот код вообще в скрипт не переделать или есть какой-то способ?

"приведённый в этой статье код может давать ошибку"... вот и у меня такое случилось... методом высоко-научного тыка, прочитав MsgBox(Text), спустя пол-дня, увидев что до значения 200 код добирается, но всё равно выдает ошибку, всё-таки дополнила текстом ошибки строку If XMLHTTP.statustext = "OK" Or XMLHTTP.statustext = "SAMEORIGIN" Then ... SAMEORIGIN - был этим текстом... и оказалось, что как минимум такая ошибка совсем не страшна, её можно обойти (оператором or как дополнила строку указанную)... и у меня всё сохранилось!... вобщем не все ошибки страшные... Игорю спасибо за код

Если файл доступен для скачивания (вы копируете этот адрес в браузер, нажимаете Enter, - и файл начинает скачиваться), - то функция DownloadFile сможет загрузить этот файл.

Если же для загрузки этого файла нужна авторизация на FTP-сервере, - то тогда нужно уже совсем другое решение
(макросы для работы с FTP у меня тоже есть на сайте)

Подскажите, а если URL ведет на ftp://aaa.bbb.ru/111.jpg, то есть возможность что-то подправить в функции DownloadFile чтобы макрос заработал? Или есть какой обходной путь чтобы скачивать не только по http но и по ftp? Спасибо.

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

Спасибо, очень полезная функция. Но у меня несколько расширенная задача: прежде чем скачивать файл, необходимо вывести список файлов, находящихся на фтп сервере. Закачанные файлы сохраняются строго в одни и тот же каталог и переименовываются по определенному алгоритму. Поэтому использование интернет эксплорера является не самым удобным вариантом для меня.

Если сайт требует авторизации, и вы авторизовались, используя браузер InternetExplorer, — то им и надо скачивать файл.
Авторизация же не распространяется на любые соединения с компа (согласитесь, если после авторизации открыть ту же страницу в другом браузере, вы же не будете авторизованы...)
Вот и тут так. (хоть тут скачивание производится не браузером, а средствами компонентов Windows, - авторизация, выполненная через IE, никакого отношения к этому макросу не имеет)
Т.е. этот макрос вам не нужен.
В том макросе, где происходит авторизация, добавьте код скачивания файла (какой конкретно код - не знаю, ни разу не приходилось качать файлы через IE)

я попал именно в этот 1%...
C помощью указанной функции удачно авторизовался на сайте, а файл скачивать отказывается (пишет что доступ закрыт и неоходимо залогиниться), что я делаю не правильно:
URL = "https://www.atsenergo.ru/reporting/personal/sib/sell_norem/20130331/имя файла/"
Dest = "путь сохранения.xls"
If URLDownloadToFile(0, URL, Dest, 0, 0) = 0 Then MsgBox "файл скачан в " & Dest
Причем тот же самый код с ссылкой на файл из незакрытой зоны закачивает правильно.

Есть возможность запрашивать список файлов с сервера автоматически.
А остальное можно автоматизировать.

Вроде бы, мне уже приходилось делать подобную программу для этого сайта, для загрузки файлов и обработки данных с atsenergo.ru
Только давно было, не помню подробностей.
Если готовы оплатить работу - обращайтесь в личку, доработаем существующую программу, или напишем новую.

"А зачем вам вход по логину-паролю, если все файлы для скачивания доступны и без этого?"

На этом же сайте есть персональный раздел: https://www.atsenergo.ru/auth
С которого идет скачка и последующая обработка персональных данных.

На счет ответа #20: каждый раз человек не может делать одну и туже операцию которая описана в ответе. Есть ли возможность запрашивать список автоматически? (далее процедура будет запускаться по времени и работать ночью)

А зачем вам вход по логину-паролю, если все файлы для скачивания доступны и без этого?

А получить список файлов можно веб-запросом в Excel
(и обновлять этот запрос макросом, или при открытии файла)

Как сделать веб-запрос в Excel для получения списка файлов на сервере:
(щелкните на картинке для увеличения)

веб-запрос в Excel - список файлов на сервере

Спасибо. Попробую)

Еще вопрос по скачке, в директории на сервере лежат в одной папке несколько отчетов:
https://www.atsenergo.ru/reporting/public/eur/carana_sell_units/20130215

День ото дня количество и названия отчетов могут меняться: Как правило добавляется один файл с новым названием.
Какой механизм лучше использовать для загрузки полного списка файлов. Может быть подойдет что-то вроде Dir()?

Да, можно и это сделать.
Есть 2 варианта:

1) использовать эту функцию, при этом логин\пароль прописывать в строке URL в определённом формате (после http://)
не уверен, что сработает (далеко не все сайты принимают этот стандартный для http метод авторизации) - но можно попробовать

2) использовать функцию для авторизации на сайте
http://excelvba.ru/code/ConnectServer
и после авторизации скачивать файл средствами Internet Explorer
(этот способ в вашем случае сработает с вероятностью 99%)

Файл прекрасно работает, но также есть необходимость скачивать данные с закрытого раздела сайта (вход по коду участника/логину/паролю). Это как-нибудь можно реализовать?

Strateg, в этом случае код усложнится.
Я в таких случаях подключаюсь в сайту через IE (Internet Explorer), где программно нажимаю согласие с сертификатом, и потом уже скачиваю файл.

Можно сделать это и с использованием объекта "Microsoft.XMLHTTP"

К сожалению, пример кода дать не могу (я как-то публиковал примеры на форумах, но оба форума у меня в данный момент недоступны. пробовал найти в кеше яндекса - не получилось)
Поищите в интернете - наверняка найдёте примеры.

Выяснилось, что проблема в сертификате узла, к которому подключаюсь. Ранее, подключаясь к не доверенному сертификату, возникало сообщение, хочу ли я продолжить. Теперь программа просто падает с ошибкой. Как исправить ситуацию?

Здравствуйте. Подскажите, почему может быть такая ситуация.
У меня есть ссылка на файл, если эту ссылку я вставляю в браузер, начинается загрузка файла.
Если использую данную функцию, на строке
XMLHTTP.send
возникает ошибка

Run-time error '-2147467259 (80004005)':
Неопознанная ошибка

Да, конечно можно.
Обычно так всегда и делаю.

После этой строки кода добавьте строку

debug.print СсылкаНаФайл$

и потом из окна Immediate скопируйте выведенную ссылку в браузер.

Сразу увидите, в чем проблема (может, имя файла некорректно формируется)

Добрый день!
Подскажите, пожалуйста, можно ли ссылку на файл указать в следующем виде:
СсылкаНаФайл$ = "http://excelvba.ru/sites/default/files/" & имя_файла
А имя_файла сформировать отдельно?
У меня данный метод не работает. Может подскажите как это осуществить.

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

C InstrRev пробовала, но т.к ссылки полностью нет, все по той же причине, то этот вариант не проходит.

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

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

Может можно как-то узнать имя файла которое было скачано? может эти ссылки где-то хранятся?

Путь (точнее, имя файла) для сохранения можно сформировать, взяв кусок текста из ссылки.
(например, при помощи функции InstrRev взяв текст после последнего слэша)
Главное, чтобы ссылка была полностью известна, - остальное уже мелочи.

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

Спасибо!

А подскажите тогда, как указать путь сохранения, если имя неизвестно? можно ли как-то указать путь без имени, чтобы файл сохранялся под тем же именем под которым скачивается?

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

Если на сайте где-то есть список доступных файлов (ссылок) - тогда другое дело, можно проанализировать этот список, и найти подходящее имя файла.
По-другому - увы, никак.

Добрый день!
Столкнулась со следующей проблемой.
Программа работает, если известно имя файла и расширение. но в моем случае о файле который скачивается известно только расширение. Имя файла составляется частично автоматически, частично в зависимости от данных таблицы. Можно ли как-то скачать такой файл и чтобы сохранялся файл именно с таким именем. Т.е. переименовывать файл нельзя.

Например:

вот имя файла C600965000263161.dsp
В этом имени заранее известно:
Первая буква она всегда С, вторая и третья цифра заранее не известны(в нашем случае 60), 0965 и 000263 надо взять из двух ячеек в таблице и последние 3 цифры тоже не известны (161).

Можно ли как-то скачать файл например по такому принципу
номер = A1 + A2 ' склеиваем ту часть что известна
имя файла = С** & номер & ***
СсылкаНаФайл$ = "http://ссылка/" & имя файла

Номеров идентичных не бывает. т.е нам все равно какие цифры под звездочками, главное чтобы номер, что мы берем из таблицы совпадал.

И как сохранить данный файл именно под тем именем, под которым он лежит в интернете.

Надеюсь, я более менее понятно объяснила что мне надо. Сразу предупрежу, что в vba я не сильна, поэтому не судите строго за данный вопрос.

Если кто-то вызовется помочь, могу скинуть конкретный пример на почту.

Здравствуйте, Алексей.

По ссылке расширение файла ну никак не узнать.
Веб-сервер получает по этой ссылке данные, и возвращает файл, но не тот, который написан в ссылке, а тот, какой считает нужным возвратить.

Т.е. даже если ссылка имеет вид /.../чего_то_там.jpg,
сервер может вам выдать файл СовсемДругоеИмя.exe

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

Впрочем, можно и не анализировать - Windows сама корректно распознаёт тип графического файла, не обращая внимание на расширение.
Т.е. вы можете переименовать файл JPG в BMP или PNG, потом открыть - и файл корректно откроется, без каких либо уведомлений.
Т.е. сохраняйте файл с любым расширением (я обычно ставлю JPG), и работайте с ним дальше как с файлом формата JPG,
- все программы (с вероятностью 99%) не заметят подвоха.

То, что по длинной ссылке не скачивается - тут ничего не могу подсказать.
Во-первых, тот километр текста, что вы привели в качестве примера, ссылкой не является (начинается с «data:image/jpeg;base64,/9j/4AAQ», а ссылки начинаются с «http://»),
а во-вторых, очень может быть, что функция не принимает ссылки длинее 255 символов (или 1024 сиволов)

Я про эти ограничения не знаю - ибо не сталкивался.

И вообще, лучше для скачивания файлов пользоваться другим кодом - с использованием WinAPI функции URLDownloadToFile
(как тут недавно выяснилось, URLDownloadToFile качает всё, а приведённый в статье код может выдавать ошибку при некоторых файлах или ссылках.
Не помню точно в чем проблема - скорее всего, в размере файла. А функция на основе URLDownloadToFile качает что угодно)

 

 

#If Win64 Then
    #If VBA7 Then    ' Windows x64, Office 2010
        Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
                (ByVal pCaller As LongLong, ByVal szURL As String, ByVal szFileName As String, _
                 ByVal dwReserved As LongLong, ByVal lpfnCB As LongLong) As LongLong
    #Else    ' Windows x64,Office 2003-2007
        Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
                                           (ByVal pCaller As LongLong, ByVal szURL As String, ByVal szFileName As String, _
                                            ByVal dwReserved As LongLong, ByVal lpfnCB As LongLong) As LongLong
    #End If
#Else
    #If VBA7 Then    ' Windows x86, Office 2010
        Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
                (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, _
                 ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
    #Else    ' Windows x86, Office 2003-2007
        Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
                                           (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, _
                                            ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
    #End If
#End If
 
Function DownLoadFileFromURL(ByVal URL$, ByVal LocalPath$) As Boolean
    On Error Resume Next: Kill LocalPath$
 
    shortFilename$ = Mid(LocalPath$, InStrRev(LocalPath$, "\") + 1)
    If shortFilename$ <> Replace_symbols(shortFilename$) Then
        Debug.Print "Wrong symbols in filename: " & shortFilename$
        Exit Function
    End If
 
    ' чтобы избежать кеширования
    URL$ = URL$ & "?rnd=" & Left(Rnd(Now) * 1E+15, 10)
 
    DownLoadFileFromURL = URLDownloadToFile(0, URL$, LocalPath$, 0, 0) = 0
End Function

Sub ПримерИспользования()
    Link$ = "http://ExcelVBA.ru/test.jpg"
    Debug.Print DownLoadFileFromURL(Link$, "c:\test2.test")
End Sub

PS: Километр текста из вашего комментария удалил

Столкнулся с проблемой -- не всегда при загрузке из интернета ссылка имеет вид http://excelvba.ru/.../чего_то_там.jpg
иногда попадаются (в случае с поиском по Гуглу) такие конструкции:
https://адрес сайта/images?q=iZBwCJ

и даже такие звери (простите за ужасный объем)
data:image/jpeg;base64,/9j/4AAQSkZJR ...очень-очень много букв... QCEIQCEIQCEIQf/2Q==

как в таких случаях узнать хотябы расширение привильное результирующего файла (jpg, png, gif и пр.)
ну и второй вопрос -- как закачать по второй ссылке (зверской)?
Ваш код отлично справляется с первой (если расширение jpg), а на вторую ругается

Спасибо.
Алексей.

Воспользовался этой функцией, но столкнулся с затруднением: при отсутствии интернета файл "достается" из локального кеша, в большинстве случаев это не проблема, но я проверяю дату последних обновлений, которая лежит в текстовом файле на сайте.
Решение: между строк
XMLHTTP.Open... и XMLHTTP.send
воткнуть
XMLHTTP.setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"

Спасибо! Работает.

Спасибо!

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
CAPTCHA
Подтвердите, пожалуйста, что вы - человек:
          ____            _____           ____  
__ _ | _ \ _ __ | ___| _ _ |___ \
/ _` | | |_) | | '_ \ | |_ | | | | __) |
| (_| | | _ < | |_) | | _| | |_| | / __/
\__, | |_| \_\ | .__/ |_| \__, | |_____|
|___/ |_| |___/
Введите код, изображенный в стиле ASCII-арт.

Не получается применить макрос? Не удаётся изменить код под свои нужды?

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