Message box not always visible - vba

I have the following VBA code that it is intended to download a file from the web, give me a message "Downloading Data from ..." and as soon as downloaded give me a message "Downloaded to ...". Here is my code:
Sub DownloadFileFromWeb()
Dim IE As Object
Dim links As Variant, lnk As Variant
Dim download_path As String
download_path = "\\xxxxx\Save Raw File here.xls"
Set IE = CreateObject("InternetExplorer.Application")
IE.Navigate "http://www.hkma.gov.hk/eng/market-data-and-statistics/monthly-statistical-bulletin/table.shtml#section2" 'load web page
While IE.Busy
DoEvents 'wait until IE is done loading page.
Wend
Set links = IE.document.getElementsByTagName("a")
For Each lnk In links
If Len(lnk.href) > 4 And Right(lnk.href, 4) = ".xls" And InStr(1, lnk.href, "http://www.hkma.gov.hk/media/eng/doc/market-data-and-statistics/monthly-statistical-bulletin/T080102.xls") <> 0 Then
MsgBox "Downloading Data from " & lnk.href
Download_File lnk.href, download_path
MsgBox "Downloaded to - " & download_path
Exit For
End If
Next
End Sub
Function Download_File(ByVal vWebFile As String, ByVal vLocalFile As String) As Boolean
Dim oXMLHTTP As Object, i As Long, vFF As Long, oResp() As Byte
Set oXMLHTTP = CreateObject("MSXML2.XMLHTTP")
oXMLHTTP.Open "GET", vWebFile, False 'Open socket to get the website
oXMLHTTP.Send 'send request
'Wait for request to finish
Do While oXMLHTTP.readyState <> 4
DoEvents
Loop
oResp = oXMLHTTP.responseBody 'Returns the results as a byte array
'Create local file and save results to it
vFF = FreeFile
If Dir(vLocalFile) <> "" Then Kill vLocalFile
Open vLocalFile For Binary As #vFF
Put #vFF, , oResp
Close #vFF
'Clear memory
Set oXMLHTTP = Nothing
End Function
The problem i have with this one is that most of the times i will not get any message box appearing and nothing gets downloaded in the meantime. Can you please help me in order to get the message box all of the time?
Thank you very much!

Tested your code on my end and I can see no errors. I've downloaded it like a hundred times already and it doesn't break. However, I made some minor modifications.
Change your main subroutine to the following:
Sub DownloadFileFromWeb()
Dim IE As Object
Dim links As Variant, lnk As Variant
Dim download_path As String
download_path = "C:\...\SavedFile.xls" 'Modify.
Set IE = CreateObject("InternetExplorer.Application")
IE.Navigate "http://www.hkma.gov.hk/eng/market-data-and-statistics/monthly-statistical-bulletin/table.shtml#section8" 'load web page
While IE.Busy
DoEvents 'wait until IE is done loading page.
Wend
Set links = IE.document.getElementsByTagName("a")
For Each lnk In links
If Len(lnk.href) > 4 And Right(lnk.href, 4) = ".xls" And InStr(1, lnk.href, "T080102.xls") <> 0 Then
If MsgBox("Downloading Data from " & lnk.href, vbOKOnly) = vbOK Then
Download_File lnk.href, download_path
MsgBox "Downloaded to - " & download_path
Exit For
End If
End If
Next
End Sub
Basically, I just changed one thing: the message box will wait for your input before it downloads the file. Notice how I did If MsgBox(...) = vbOKOnly. This way, it will wait for your input and not break.
Minor change as well to URL. Changed section2 to section8, since that's the table you want (not going to affect anything, IMHO).
Let us know if this helps.

Related

How can you get VBA to start downloading a file and not wait for it to finish before continuing to the next line code

I'm trying to optimize a VBA application that generates a report.
This report requires the application to download and embed multiple images.
I've identified this as the biggest bottle neck in the application.
My first attempt was to get VBA to execute a Powershell command that would download the images early on in the generation of the report and then the application would embed them from the HD after it was done crunching data.
Do to obvious security concerns my work environment prevents VBA from executing shell scripts.
After a few unimportant failed attempts (trying to open another/this xlsm workbook with a new excel application independently of my vba thread with an on open execution and variations of this) I've come here asking for suggestions.
How would you use vba to start downloading image (using any native windows 10 application/command/process/...) and not wait for the download to complete before moving onto the next line of code?
Later on in the application I'll have code to scan the destination directory to determine if the files are done being downloaded if not it'll sleep and repeat x times before fail.
Update: Based on the comments I think I'm very close to a solution. I've included the code I'm currently working with at the bottom of this update. The problem now is that it quickly downloads the file as long as I have made a request to the same url at least once before.
On the first request it hangs on 'oXMLHTTP.send' for a period of time a little greater than what I would expect it would take to download the file through a browser and then for some reason resizes itself.
Could anyone help me with this hanging issue and / or explain why this code calls 'Workbook_WindowResize'?
This happens on and off my works VPN. Looking at Fiddler I can tell that only two requests get sent out.
Result 200: http://ipv4.download.thinkbroadband.com/50MB.zip?randomizer=ff%2014
Result 200: http://ipv4.download.thinkbroadband.com/50MB.zip?randomizer=ee%20761
Result and Code
In a brand new workbook I pasted the code found at the end of this update.
This is what it I got in the immediate window.
A took: 33375milliseconds
Pre DoEvents
Workbook_WindowResized
Post DoEvents
B took: 593milliseconds
Pre DoEvents
Post DoEvents
C took: 33797milliseconds
Pre DoEvents
Workbook_WindowResized
Post DoEvents
Do work
Pre DoEvents
Post DoEvents
a done
b done
c done
ThisWorkbook Code
Private mlngStart As Long
Private Declare Function GetTickCount Lib "kernel32" () As Long
Public Sub StartTimer()
mlngStart = GetTickCount
End Sub
Public Function EndTimer() As Long
EndTimer = (GetTickCount - mlngStart)
End Function
Function StartDownload(ByVal vWebFile As String, sPath As String) As Object
Dim oXHTTP As Object
Dim oStream As Object
Set oXHTTP = CreateObject("MSXML2.XMLHTTP.3.0")
Set oStream = CreateObject("ADODB.Stream")
Application.StatusBar = "Fetching " & vWebFile & " as " & sPath
oXHTTP.Open "GET", vWebFile, False
oXHTTP.send
With oStream
.Type = 1 'adTypeBinary
.Open
.Write oXHTTP.responseBody
.SaveToFile sPath, 2 'adSaveCreateOverWrite
.Close
End With
Set StartDownload = oXHTTP
Set oStream = Nothing
Application.StatusBar = False
End Function
Sub FinishDownload(ByRef oXMLHTTP, ByVal vLocalFile As String)
'Wait for request to finish
Do While oXMLHTTP.readyState <> 4
DoEvents
Loop
End Sub
Function foo()
Dim dest As String
dest = "C:\sandbox\"
Dim a, b, c As Object
DoEvents
Url = "http://ipv4.download.thinkbroadband.com/50MB.zip?randomizer=ff" & Str(Math.Round(Math.Rnd(12) * 1000, 0))
Call StartTimer
Set a = StartDownload(Url, dest & "a.zip")
Debug.Print "A took: " & EndTimer & "milliseconds"
Debug.Print "Pre DoEvents"
DoEvents
Debug.Print "Post DoEvents"
Call StartTimer
Set b = StartDownload(Url, dest & "b.zip")
Debug.Print "B took: " & EndTimer & "milliseconds"
Debug.Print "Pre DoEvents"
DoEvents
Debug.Print "Post DoEvents"
Url = "http://ipv4.download.thinkbroadband.com/50MB.zip?randomizer=ee" & Str(Math.Round(Math.Rnd(12) * 1000, 0))
Call StartTimer
Set c = StartDownload(Url, dest & "c.zip")
Debug.Print "C took: " & EndTimer & "milliseconds"
Debug.Print "Pre DoEvents"
DoEvents
Debug.Print "Post DoEvents"
Debug.Print ("Do work")
Call bar
Debug.Print "Pre DoEvents"
DoEvents
Debug.Print "Post DoEvents"
Call FinishDownload(a, dest & "a.zip")
Debug.Print ("a done")
Call FinishDownload(b, dest & "b.zip")
Debug.Print ("b done")
Call FinishDownload(c, dest & "c.zip")
Debug.Print ("c done")
End Function
Function Download_File(ByVal vWebFile As String, ByVal vLocalFile As String) As Boolean
Dim oXMLHTTP As Object, i As Long, vFF As Long, oResp() As Byte
'You can also set a ref. to Microsoft XML, and Dim oXMLHTTP as MSXML2.XMLHTTP
Set oXMLHTTP = CreateObject("MSXML2.XMLHTTP")
oXMLHTTP.Open "GET", vWebFile, False 'Open socket to get the website
oXMLHTTP.send 'send request
'Wait for request to finish
Do While oXMLHTTP.readyState <> 4
DoEvents
Loop
oResp = oXMLHTTP.responseBody 'Returns the results as a byte array
'Create local file and save results to it
vFF = FreeFile
If Dir(vLocalFile) <> "" Then Kill vLocalFile
Open vLocalFile For Binary As #vFF
Put #vFF, , oResp
Close #vFF
'Clear memory
Set oXMLHTTP = Nothing
End Function
Sub bar()
Dim F As Integer
F = FreeFile
Open "C:\sandbox\" & "\example.txt" For Output As F
Close #F
End Sub
Private Sub Workbook_WindowResize(ByVal Wn As Window)
Debug.Print "Workbook_WindowResized"
End Sub
From the links provided by #Tim Williams in the comments I created this and it works.
Function StartDownload(ByVal vWebFile As String) As Object
Dim oXMLHTTP As Object, i As Long, vFF As Long, oResp() As Byte
'You can also set a ref. to Microsoft XML, and Dim oXMLHTTP as MSXML2.XMLHTTP
Set oXMLHTTP = CreateObject("MSXML2.XMLHTTP")
oXMLHTTP.Open "GET", vWebFile, True'Open socket to get the website
oXMLHTTP.Send 'send request
Set StartDownload = oXMLHTTP
End Function
Sub FinishDownload(ByRef oXMLHTTP, ByVal vLocalFile As String)
'Wait for request to finish
Do While oXMLHTTP.readyState <> 4
DoEvents
Loop
oResp = oXMLHTTP.responseBody 'Returns the results as a byte array
'Create local file and save results to it
vFF = FreeFile
If Dir(vLocalFile) <> "" Then Kill vLocalFile
Open vLocalFile For Binary As #vFF
Put #vFF, , oResp
Close #vFF
'Clear memory
Set oXMLHTTP = Nothing
End Sub
Function foo()
Dim dest As String
dest = "C:\sandbox\"
url = "http://ipv4.download.thinkbroadband.com/200MB.zip"
Dim a, b, c As Object
DoEvents
Set a = DownloadManager.StartDownload(url)
DoEvents
Set b = DownloadManager.StartDownload(url)
DoEvents
Set c = DownloadManager.StartDownload(url)
DoEvents
Debug.Print ("Do Something")
Call FinishDownload(a, dest & "a.zip")
Debug.Print ("a done")
Call FinishDownload(b, dest & "b.zip")
Debug.Print ("b done")
Call FinishDownload(c, dest & "c.zip")
Debug.Print ("c done")
End Function

Pause script till website fully loaded - Excel VBA

I'm currently trying to create a sheet which will extract tracking information for parcels sent out. I've worked out the following code for the time being but encounter the following issues:
The code continues before the page fully loads, I suspect this may be because after the initial loading is complete, it runs a script and refreshes.
If mouse is not rolling over Internet Explorer, high probability of a human verification with images. I understand this may not be possible to avoid but is there any way I can pause the script while someone completes the verification?
Sub RoyalTrack()
Dim i As Long
Dim ie As Object
Dim t As String
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = True
.Navigate "https://www.royalmail.com/track-your-item#/tracking-results/SF511991733GB"
.Resizable = True
End With
While ie.ReadyState <> 4 Or ie.Busy: DoEvents: Wend
Dim full As Variant
Dim latest As Variant
full = ie.Document.getElementsByClassName("c-tracking-history")(0).innerText
latest = ie.Document.getElementsByClassName("tracking-history-item ng-scope")(0).innerText
MsgBox full
MsgBox latest
End Sub
Managed to figure it out. Added a 2 second wait after page loads to allow loading and an error handler to identify if the required property is available.
Sub RoyalTrack()
Dim i As Long
Dim ie As Object
Dim t As String
Dim trackingN As String
Dim count As Integer
count = 2
Do While Worksheets("Sheet1").Range("D" & count).Value <> ""
Set ie = CreateObject("InternetExplorer.Application")
trackingN = Worksheets("Sheet1").Range("D" & count).Value
With ie
.Visible = True
' Variable tracking SF-GB
.Navigate "https://www.royalmail.com/track-your-item#/tracking-results/" & trackingN
.resizable = True
End With
While ie.readyState <> 4 Or ie.Busy: DoEvents: Wend
Application.Wait (Now + TimeValue("0:00:02"))
Dim full As Variant
Dim latest As Variant
On Error Resume Next
latest = ie.document.getElementsByClassName("tracking-history-item ng-scope")(0).innerText
If Err Then
MsgBox "Prove your humanity if you can"
Err.Clear
End If
latest = ie.document.getElementsByClassName("tracking-history-item ng-scope")(0).innerText
Windows("Book1.xls").Activate
Sheets("Sheet1").Select
Range("E" & count).Value = latest
ie.Quit
Set ie = Nothing
count = count + 1
Loop
End Sub

Extracting file URL from a Hyperlinked Image

Sub DownloadFile()
Dim myURL As String
myURL = "http://data.bls.gov/timeseries/LNS14000000"
Dim WinHttpReq As Object
Set WinHttpReq = CreateObject("Microsoft.XMLHTTP")
WinHttpReq.Open "GET", myURL, False, "username", "password"
WinHttpReq.send
myURL = WinHttpReq.responseBody
If WinHttpReq.Status = 200 Then
Set oStream = CreateObject("ADODB.Stream")
oStream.Open
oStream.Type = 1
oStream.Write WinHttpReq.responseBody
oStream.SaveToFile "C:\Downloads\abc.xlsx", 2
oStream.Close
End If
End Sub
I am trying to download data using VBA and found this code running pretty well. The webpage URL from which I am trying to download data is the one I have used in the code. Please take a moment and open the webpage as the Excel file I am trying to download is linked in an image and so I am not able to find the URL to download the file from that image. Please advice. Thanks.
You might be able to hit the form target directly with a POST (action="/pdq/SurveyOutputServlet") but it is expecting a post string of the <input> elements together with their values. Most if not all of these input elements have been filled out for you simply by going to that page. All you need to do is collect and concatenate them into a post string to be shoved back at the form.
Option Explicit
'base web page
Public Const csBLSGOVpg = "http://data.bls.gov/timeseries/LNS14000000"
'form's action target
Public Const csXLSDLpg = "http://data.bls.gov/pdq/SurveyOutputServlet"
Sub mcr_Stream_Buyer_Documents()
Dim xmlDL As New MSXML2.ServerXMLHTTP60, xmlBDY As New HTMLDocument, adoFILE As Object
Dim xmlSend As String, strFN As String, f As Long, i As Long
With xmlDL
.SetTimeouts 5000, 5000, 15000, 25000
'start by going to the base web page
.Open "GET", csBLSGOVpg, False
.setRequestHeader "Content-Type", "text/javascript"
.send
If .Status <> "200" Then GoTo bm_Exit
'get the source HTML for examination; zero the post string var
xmlBDY.body.innerHTML = .responseText
xmlSend = vbNullString
'loop through the forms until you find the right one
'then loop through the input elements and construct a post string
For f = 0 To xmlBDY.getElementsByTagName("form").Length - 1
If xmlBDY.getElementsByTagName("form")(f).Name = "excel" Then
With xmlBDY.getElementsByTagName("form")(f)
For i = 0 To .getElementsByTagName("input").Length - 1
xmlSend = xmlSend & Chr(38) & _
.getElementsByTagName("input")(i).Name & Chr(61) & _
.getElementsByTagName("input")(i).Value
Next i
xmlSend = "?.x=5&.y=5" & xmlSend
End With
Exit For
End If
Next f
'Debug.Print xmlSend 'check the POST string
'send the POST string back to the form's action target
.Open "POST", csXLSDLpg, False
xmlDL.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
xmlDL.send xmlSend
If xmlDL.Status <> "200" Then GoTo bm_Exit
'pick up the response as a stream and save it as a .XLSX
strFN = Environ("USERPROFILE") & "\Documents\LNS14000000" & Format(Date, "yyyymmdd") & ".xlsx"
On Error Resume Next
Kill strFN
On Error GoTo 0
Set adoFILE = CreateObject("ADODB.Stream")
adoFILE.Type = 1
adoFILE.Open
adoFILE.Write .responseBody
adoFILE.SaveToFile strFN, 2
Set adoFILE = Nothing
End With
Set xmlBDY = Nothing
Set xmlDL = Nothing
Exit Sub
bm_Exit:
Debug.Print Err.Number & ":" & Err.Description
End Sub
This is pretty minimalist but it is all that you need. There is at least one non-standard input element that does not have a name but I elected to send its value back anyway. I did not sequentially remove things until it broke; I just built the POST string given what I retrieved and sent it back.
     
               LNS1400000020150916.xlsx
You will probably be moving this code to some sort of loop. Adjust the receiving file name accordingly. Each new page should adjust its own form input elements accordingly.
Once response is stored in an HTMLDocument object you can use a CSS selector of
#download_xlsx
The "#" means id.
You can then click on this element
htmlDocument.querySelector("#download_xlsx").Click
VBA:
Option Explicit
Public Sub DownloadFile()
Dim ie As New InternetExplorer
With ie
.Visible = True
.navigate "https://data.bls.gov/timeseries/LNS14000000"
While .Busy Or .readyState < 4: DoEvents: Wend
.document.querySelector("#download_xlsx").Click
.Quit
End With
End Sub
Other:
You could even target the form and submit:
.document.forms("excel").submit
This triggers the POST request mentioned in the other answer (which is an awesome answer btw).

Excel VBA Macro: Scraping data from site table that spans multiple pages

Thanks in advance for the help. I'm running Windows 8.1, I have the latest IE / Chrome browsers, and the latest Excel. I'm trying to write an Excel Macro that pulls data from StackOverflow (https://stackoverflow.com/tags). Specifically, I'm trying to pull the date (that the macro is run), the tag names, the # of tags, and the brief description of what the tag is. I have it working for the first page of the table, but not for the rest (there are 1132 pages at the moment). Right now, it overwrites the data everytime I run the macro, and I'm not sure how to make it look for the next empty cell before running.. Lastly, I'm trying to make it run automatically once per week.
I'd much appreciate any help here. Problems are:
Pulling data from the web table beyond the first page
Making it scrape data to the next empty row rather than overwriting
Making the Macro run automatically once per week
Code (so far) is below. Thanks!
Enum READYSTATE
READYSTATE_UNINITIALIZED = 0
READYSTATE_LOADING = 1
READYSTATE_LOADED = 2
READYSTATE_INTERACTIVE = 3
READYSTATE_COMPLETE = 4
End Enum
Sub ImportStackOverflowData()
'to refer to the running copy of Internet Explorer
Dim ie As InternetExplorer
'to refer to the HTML document returned
Dim html As HTMLDocument
'open Internet Explorer in memory, and go to website
Set ie = New InternetExplorer
ie.Visible = False
ie.navigate "http://stackoverflow.com/tags"
'Wait until IE is done loading page
Do While ie.READYSTATE <> READYSTATE_COMPLETE
Application.StatusBar = "Trying to go to StackOverflow ..."
DoEvents
Loop
'show text of HTML document returned
Set html = ie.document
'close down IE and reset status bar
Set ie = Nothing
Application.StatusBar = ""
'clear old data out and put titles in
'Cells.Clear
'put heading across the top of row 3
Range("A3").Value = "Date Pulled"
Range("B3").Value = "Keyword"
Range("C3").Value = "# Of Tags"
'Range("C3").Value = "Asked This Week"
Range("D3").Value = "Description"
Dim TagList As IHTMLElement
Dim Tags As IHTMLElementCollection
Dim Tag As IHTMLElement
Dim RowNumber As Long
Dim TagFields As IHTMLElementCollection
Dim TagField As IHTMLElement
Dim Keyword As String
Dim NumberOfTags As String
'Dim AskedThisWeek As String
Dim TagDescription As String
'Dim QuestionFieldLinks As IHTMLElementCollection
Dim TodaysDate As Date
Set TagList = html.getElementById("tags-browser")
Set Tags = html.getElementsByClassName("tag-cell")
RowNumber = 4
For Each Tag In Tags
'if this is the tag containing the details, process it
If Tag.className = "tag-cell" Then
'get a list of all of the parts of this question,
'and loop over them
Set TagFields = Tag.all
For Each TagField In TagFields
'if this is the keyword, store it
If TagField.className = "post-tag" Then
'store the text value
Keyword = TagField.innerText
Cells(RowNumber, 2).Value = TagField.innerText
End If
If TagField.className = "item-multiplier-count" Then
'store the integer for number of tags
NumberOfTags = TagField.innerText
'NumberOfTags = Replace(NumberOfTags, "x", "")
Cells(RowNumber, 3).Value = Trim(NumberOfTags)
End If
If TagField.className = "excerpt" Then
Description = TagField.innerText
Cells(RowNumber, 4).Value = TagField.innerText
End If
TodaysDate = Format(Now, "MM/dd/yy")
Cells(RowNumber, 1).Value = TodaysDate
Next TagField
'go on to next row of worksheet
RowNumber = RowNumber + 1
End If
Next
Set html = Nothing
'do some final formatting
Range("A3").CurrentRegion.WrapText = False
Range("A3").CurrentRegion.EntireColumn.AutoFit
Range("A1:C1").EntireColumn.HorizontalAlignment = xlCenter
Range("A1:D1").Merge
Range("A1").Value = "StackOverflow Tag Trends"
Range("A1").Font.Bold = True
Application.StatusBar = ""
MsgBox "Done!"
End Sub
There's no need to scrape Stack Overflow when they make the underlying data available to you through things like the Data Explorer. Using this query in the Data Explorer should get you the results you need:
select t.TagName, t.Count, p.Body
from Tags t inner join Posts p
on t.ExcerptPostId = p.Id
order by t.count desc;
The permalink to that query is here and the "Download CSV" option which appears after the query runs is probably the easiest way to get the data into Excel. If you wanted to automate that part of things, the direct link to the CSV download of results is here
You can improve this to parse out exact elements but it loops all the pages and grabs all the tag info (everything next to a tag)
Option Explicit
Public Sub ImportStackOverflowData()
Dim ie As New InternetExplorer, html As HTMLDocument
Application.ScreenUpdating = False
With ie
.Visible = True
.navigate "https://stackoverflow.com/tags"
While .Busy Or .READYSTATE < 4: DoEvents: Wend
Set html = .document
Dim numPages As Long, i As Long, info As Object, item As Object, counter As Long
numPages = html.querySelector(".page-numbers.dots ~ a").innerText
For i = 1 To 2 ' numPages ''<==1 to 2 for testing; use to numPages
DoEvents
Set info = html.getElementById("tags_list")
For Each item In info.getElementsByClassName("grid-layout--cell tag-cell")
counter = counter + 1
Cells(counter, 1) = item.innerText
Next item
html.querySelector(".page-numbers.next").Click
While .Busy Or .READYSTATE < 4: DoEvents: Wend
Set html = .document
Next i
Application.ScreenUpdating = True
.Quit '<== Remember to quit application
End With
End Sub
I'm not making use of the DOM, but I find it very easy to get around just searching between known tags. If ever the expressions you are looking for are too common just tweak the code a bit so that it looks for a string after a string).
An example:
Public Sub ZipLookUp()
Dim URL As String, xmlHTTP As Object, html As Object, htmlResponse As String
Dim SStr As String, EStr As String, EndS As Integer, StartS As Integer
Dim Zip4Digit As String
URL = "https://tools.usps.com/go/ZipLookupResultsAction!input.action?resultMode=1&companyName=&address1=1642+Harmon+Street&address2=&city=Berkeley&state=CA&urbanCode=&postalCode=&zip=94703"
Set xmlHTTP = CreateObject("MSXML2.XMLHTTP")
xmlHTTP.Open "GET", URL, False
On Error GoTo NoConnect
xmlHTTP.send
On Error GoTo 0
Set html = CreateObject("htmlfile")
htmlResponse = xmlHTTP.ResponseText
If htmlResponse = Null Then
MsgBox ("Aborted Run - HTML response was null")
Application.ScreenUpdating = True
GoTo End_Prog
End If
'Searching for a string within 2 strings
SStr = "<span class=""address1 range"">" ' first string
EStr = "</span><br />" ' second string
StartS = InStr(1, htmlResponse, SStr, vbTextCompare) + Len(SStr)
EndS = InStr(StartS, htmlResponse, EStr, vbTextCompare)
Zip4Digit = Left(Mid(htmlResponse, StartS, EndS - StartS), 4)
MsgBox Zip4Digit
GoTo End_Prog
NoConnect:
If Err = -2147467259 Or Err = -2146697211 Then MsgBox "Error - No Connection": GoTo End_Prog 'MsgBox Err & ": " & Error(Err)
End_Prog:
End Sub

VBA Error 70: Permission Denied during IE automation

I've been trying to create a quick subroutine in VBA through Excel 2010 to automate putting a list of URLs through bit.ly and copying the abbreviated links back to replace their original link. BUT I'm getting an Error 70: Permission Denied runtime error along the way. I've had a few courses and this MOSTLY works, but I'm not highly familiar with VBA and could use some help in debugging this if possible (it'd be a huge help). Here's the code:
Option Explicit
Dim IE As Object
Sub AutoAbbrev()
Set IE = CreateObject("InternetExplorer.Application")
Dim holdURL As String
Dim row_number As Integer
IE.Visible = True
For row_number = 101 To 112
holdURL = ""
If Range("b" & row_number).Value = "" Then GoTo Skip
IE.navigate "http://www.bitly.com" 'load bit.ly
Do While IE.readyState <> 4
DoEvents
Loop
IE.document.all("shorten_url").Value = Range("b" & row_number).Value
IE.document.all("shorten_btn").Click
Do While IE.document.all("shorten_url").Value = Range("b" & row_number).Value Or IE.document.all("shorten_url").Value = ""
DoEvents
Loop
holdURL = IE.document.all("shorten_url").Value
IE.document.all("shorten_url").Value = ""
Range("b" & row_number).Value = holdURL
Skip:
Next row_number
End Sub
Private Sub Command1_Click()
AutoAbbrev
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set IE = Nothing
If TypeName(IE) <> "Nothing" Then Unload IE
Set IE2 = Nothing
If TypeName(IE2) <> "Nothing" Then Unload IE2
End Sub
The error is mostly thrown on this line after the program has run through one or more iterations:
Do While IE.document.all("shorten_url").Value = Range("b" & row_number).Value Or IE.document.all("shorten_url").Value = ""
DoEvents
Loop
If any specific advice could be provided to help me over this hump, I'd greatly appreciate it. Thanks!
Automating Internet Explorer should always be a last resort, it's slow and relies on the structure of the page remaining unchanged. It's always better to opt for an API if one is available, in this case bitly provide an API for shotening links, you just need to get your Authentication Token and enter it in the below:
Public Function Shorten(url As String) As String
Const token As String = "YOUR AUTHENTICATION TOKEN"
Static oRequest As Object
If oRequest Is Nothing Then Set oRequest = CreateObject("winhttp.winhttprequest.5.1")
With oRequest
.Open "GET", "https://api-ssl.bitly.com/v3/shorten?access_token=" & token & "&longUrl=" & url & "&format=xml", False
.send
If Left(Split(.responsetext, "txt>")(1), 2) = "OK" Then Shorten = Split(Split(.responsetext, "url>")(1), "<")(0)
End With
End Function
You can then use the above as a function in your worksheet
I found this occurs because of the page not being fully loaded. IE is SLOW but sometimes you need to use it because you have dynamic content in Divs which need to be opened with an object.click event. Do Until Not appIE.Busy And appIE.ReadyState = 4: DoEvents: Loop can help but it can also hang up your browser so adding a wait period using a timer can help.