The below function opens Oanda.com and takes the currency conversion rate between USD and a input (another currency). The equation will be filled down with another macro to span 48 rows. The macro as it stands, will open 48 Internet Explorer Windows and close them after extracting the data point (1 at a time as the equation updates down the column). This process is tedious and the below method seems to be more efficient on every front but I cannot figure out how to implement:
Is there a way to amend this to first check for existing Internet Explorer window, If one is open, simply use that window to go to the domain (which is variable here) and extract data. If a window is not open, then open one. I do not know procedures to search for programs running in the background.
The main goal is to speed up the equation when it is executed by fill down. Any suggestions welcome.
Clarity Edit: I created the UDF to help me build a table. Currency tickers (USD, EUR, GBP, etc) will span Column A down to row 48. Column B needs to show the corresponding conversion rate matched against 1 USD (down to row 48). The UDF below does as intended but i'm seeking an alternative, more efficient, way to do this.
Option Explicit
Public Function ConvertUSD(ConvertWhat As String) As Double
'References
' Microsoft XML, vs.0
' Microsoft Internet Controls
' Microsoft HTML Object Library.
Dim IE As New InternetExplorer
'IE.Visible = True
IE.Navigate "https://www.oanda.com/currency/converter?quote_currency=USD&base_currency=" & ConvertWhat
Do
DoEvents
Loop Until IE.ReadyState = ReadyState_Complete
Dim Doc As HTMLDocument
Set Doc = IE.Document
Dim Ans As String
Ans = Trim(Doc.getElementsByTagName("tbody")(2).innerText)
Dim AnsExtract As Variant
AnsExtract = Split(Ans, " ")
ConvertUSD = AnsExtract(4)
IE.Quit
End Function
Try this:
Sub MainSub()
Dim IE As InternetExplorer
Set IE = New InternetExplorer
'
Dim x As Long
Dim Currencies As Variant
Currencies = Array("GBP", "EUR", "JPY", "HKD")
'
For x = LBound(Currencies) To UBound(Currencies)
Debug.Print "1 USD = " & ConvertUSD(Currencies(x), IE) & " " & Currencies(x)
Next x
IE.Quit ' Quit here instead
Set IE = Nothing
End Sub
Public Function ConvertUSD(ByVal ConvertWhat As String, IE As InternetExplorer) As Double
'References
' Microsoft XML, vs.0
' Microsoft Internet Controls
' Microsoft HTML Object Library.
' Dim IE As New InternetExplorer ' Commented out here
IE.Navigate "https://www.oanda.com/currency/converter?quote_currency=USD&base_currency=" & ConvertWhat
Do
DoEvents
Loop Until IE.ReadyState = ReadyState_Complete
Dim Doc As HTMLDocument
Set Doc = IE.Document
Dim Ans As String
Ans = Trim(Doc.getElementsByTagName("tbody")(2).innerText)
Dim AnsExtract As Variant
AnsExtract = Split(Ans, " ")
ConvertUSD = AnsExtract(4)
' IE.Quit ' Don't quit here
End Function
Your problem is that you keep opening a new IE every time the function is called. However if you open one before calling the function, you'll be able to re-use it as needed - and only quit after you've finished.
Related
Hello Guys i am having a problem that i made a code to open internet explorer...open a url placed in Column A of excel sheet... scroll down to specific section of browser window and take screenshot. I dont know why but my code is not looping while ie.busy or ie.readystate <> 4 I am getting runtime error 462 The remote server machine does not exist. and other issue is with ieb.Document.parentWindow.scroll 0&, 200& statement as its not scrolling page. I have checked many internet resources but all in vain.
Sub Test()
Dim str As String, Win As Object
For rowno = 2 To 3
str = Sheet1.Cells(rowno, 1).Text
Dim ieb As Object
Dim IEdoc As HTMLDocument
Dim Item As Variant
Set ieb = CreateObject("InternetExplorer.application")
ieb.Visible = True
ieb.FullScreen = True
ieb.Navigate str
Do While ieb.Busy
Do events
Loop
ieb.Document.parentWindow.scroll 0&, 200&
Call timee
Set ieb = Nothing
Next rowno
End Sub
I want to eventually create a function where I can specify a web page element and URL and populate all instances of that element down a column. But am currently only experiencing limited success with this function:
Sub GrabAnchorTags() '(URL As String) As Variant'
Dim objIE As InternetExplorer
Dim elem As Object
Set objIE = New InternetExplorer
objIE.Visible = False
objIE.navigate "http://example.com/"
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
Dim aRange As Range
Debug.Print objIE.document.getElementsByTagName("a").Length
For Each elem In objIE.document.getElementsByTagName("a")
Debug.Print elem
ActiveCell.Offset(x, y).Value = elem
ActiveCell.Offset(x, y + 1).Value = elem.textContent
x = x + 1
Next
objIE.Quit
Set objIE = Nothing
End Sub
I would like to be able to turn this successfully from a macro to a function.
Currently, it uses a for loop to populate the cells and I wonder if it's possible to accomplish the same thing using evaluate or something similar because the for loop is inefficient.
This function would need to live in a cell, reference a URL in another cell, and populate the cells bellow it with all elements of a type found on the page. I am currently working on the anchor tag.
Many other solutions I referenced used macros:
Scraping data from website using excel vba
Getting links url from a webpage excel vba
VBA – Web scraping with getElementsByTagName()
Generally speaking, whenever you have many cells to write to, you should enter the data into an internal array, and then write the entire array to the worksheet in one hit. However you seem to not want a macro/sub in your case.
If you wish it to take the worksheet formula approach for usability reasons, then the best way is to use a very powerful, but underused technique in Excel development.
A NAMED RANGE
Named ranges are Excels closest thing to getting an in-memory block of data, and then other simpler formulas can use the named range to get info from the Named Range.
A Named Range doesn't have to actually be a simple block of cells on a sheet. You can write your VBA formula as a Public formula, and then reference it in the Named Range.
Function getElems(url As String, tagName As String) As String()
Dim browser As New MSXML2.XMLHTTP60
Dim doc As MSHTML.HTMLDocument
With browser
.Open "GET", url, False
.send
If .readyState = 4 And .Status = 200 Then
Set doc = New MSHTML.HTMLDocument
doc.body.innerHTML = .responseText
Else
MsgBox "Error" & vbNewLine & "Ready state: " & .readyState & _
vbNewLine & "HTTP request status: " & .Status
End If
End With
Dim tag As MSHTML.IHTMLElement
Dim tags As MSHTML.IHTMLElementCollection
Set tags = doc.getElementsByTagName(tagName)
Dim arr() As String
Dim arrCounter As Long: arrCounter = 1
ReDim arr(1 To tags.Length, 1 To 2)
For Each tag In tags
arr(arrCounter, 1) = tag.innerText
'Change the below if block to suit
If tagName = "a" Then
arr(arrCounter, 2) = tag.href
Else
arr(arrCounter, 2) = tag.innerText
End If
arrCounter = arrCounter + 1
Next tag
Set doc = Nothing
Set browser = Nothing
getElems = arr
End Function
Now set a Named Range in Excel such as:
elementData
=getElems(Sheet1!$A$1, Sheet1!$B$1)
In A1, put the URL, and in B1 put the tag Name such as "a"
Then in your cells you can say
=INDEX(elementData, ROW(1:1), 1) and in adjacent cell put =INDEX(elementData, ROW(1:1), 2) (or use ROWS formula technique)
and drag down.
I am trying to copy a table from this website: http://www.nzfma.org/data/search.aspx#
I need to select the date as yesterday's date, then copy and paste the table to a file.
My code is below:
Sub Test1()
'open IE, navigate to the website of interest and loop until fully loaded
Dim NZFMA As Worksheet
Dim TodayN As Range
Dim elemCollection As Object
Set NZFMA = Sheets("NZFMA")
Set TodayN = NZFMA.Range("B2")
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = True
.navigate "http://www.nzfma.org/data/search.aspx"
Do Until Not ie.Busy And ie.ReadyState = 4
DoEvents
Loop
'Select the dates from the drop-down box
ie.Document.getElementbyid("ctl00_cphBody_rdpDate_dateInput").Value = Format(TodayN, "yyyy-mm-dd")
'Click the submit button
ie.Document.getElementbyid("cphBody_btnSearch").Value = "Search"
'Copy the results
Set elemCollection = ie.Document.getElementbyid("cphBody_upResults")
While ie.ReadyState = 4
DoEvents
Wend
End With
End Sub
For some reason, my macro stops after the first getElementbyID line. Can someone advise as to which part of the code is wrong?
Alright, haven't tested this but something like it should work to change the first dropdown box
Dim elemCollection As HTMLFormElement
set elemCollection = ie.Document.getElementbyid("cphBody_lstMarket")
dim teststring as string
teststring = whatever
Select Case TestString
Case "BKBM"
elemCollection.selectedIndex = 1
Case "BBRS030"
elemCollection.selectedIndex = 2
etc etc
To change the date, you will need something like
ie.Document.getElementbyid("ctl00_cphBody_rdpDate_dateInput")(0).value = "05/07/2016"
Anyway, have a go with that, and please tell me the results
I am trying to get a row of data from this table on this website: http://www.nasdaq.com/symbol/neog/financials?query=balance-sheet
Now I can manage to get the "total liabilities" row using the
doc.getelementsbyclassname("net")(3).innertext
but I cannot figure out how to get any other rows of data such as common stock.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Row = Range("bscode").Row And _
Target.Column = Range("bscode").Column Then
Dim IE As New InternetExplorer
IE.Visible = True
IE.navigate "http://www.nasdaq.com/symbol/" & Range("bscode").Value & "/financials?query=balance-sheet&data=quarterly"
Do
DoEvents
Loop Until IE.readyState = READYSTATE_COMPLETE
Dim Doc As HTMLDocument
Set Doc = IE.document
Dim sD As String
sD = Doc.getElementsByTagName("tr")(8).innerText
MsgBox sD
Dim aD As Variant
aD = Split(sD, "$")
Range("bs").Value = aD(1)
Range("ba").Value = aD(2)
Range("bb").Value = aD(3)
Range("bc").Value = aD(4)
End If
End Sub
If it helps, I have the HTML source and the tr highlighted that I want to grab.
screenshot of HTML code
The issue is the method of finding the table row data. Could someone please explain to me how to get other rows of data? It would be much appreciated !
I was able to do some trial and error and to get the correct reference this way:
Dim eTR As Object, cTR As Object, I as Integer 'I used object, because I did late binding
Set cTR = Doc.getElementsByTagName("tr")
i = 0
For Each eTR In cTR
If Left(eTR.innerText, 3) = "Com" Then
Debug.Print "(" & i; "): " & eTR.innerText
End If
i = i + 1
Next
The immediate window then displayed
(308): Common Stocks ... (a bunch of space) ...
$5,941$5,877$5,773$3,779
I then tested this statement:
sd = Doc.getElementsByTagName("tr")(308).innerText
Debug.Print sd
And got the same result.
I want to copy into Excel 3 tracking information tables that website generates when I track a parcel. I want to do it through Excel VBA. I can write a loop and generate this webpage for various tracking numbers. But I am having a hard time copying tables - the top table, travel history and shipments track table. Any solution? In my vba code last 3 lines below are giving an error :( - run time error '438' Object doesn't support this property or error.
Sub final()
Application.ScreenUpdating = False
Set ie = CreateObject("InternetExplorer.Application")
my_url = "https://www.fedex.com/fedextrack/index.html?tracknumbers=713418602663&cntry_code=us"
With ie
.Visible = True
.navigate my_url
Do Until Not ie.Busy And ie.readyState = 4
DoEvents
Loop
End With
ie.document.getElementById("detailsBody").Value
ie.document.getElementById("trackLayout").Value
ie.document.getElementById("detail").Value
End Sub
.Value is not a method available in that context. also, you will want to assign the return value of the method call to a variable. Also, you should declare your variables :)
I made some modifications and include one possible way of getting data from one of the tables. YOu may need to reformat the output using TextToColumns or similar, since it prints each row in a single cell.
I also notice that when I execute this, the tables have sometimes not finished loading and the result will be an error unless you put in a suitable Wait or use some other method to determine when the data has fully loaded on the webpage. I use a simple Application.Wait
Option Explicit
Sub final()
Dim ie As Object
Dim my_url As String
Dim travelHistory As Object
Dim history As Variant
Dim h As Variant
Dim i As Long
Application.ScreenUpdating = False
Set ie = CreateObject("InternetExplorer.Application")
my_url = "https://www.fedex.com/fedextrack/index.html?tracknumbers=713418602663&cntry_code=us"
With ie
.Visible = True
.navigate my_url
'## I modified this logice a little bit:
Do While .Busy And .readyState <> 4
DoEvents
Loop
End With
'## Here is a simple method wait for IE to finish, you may need a more robust solution
' For assistance with that, please ask a NEW question.
Application.Wait Now() + TimeValue("0:00:10")
'## Get one of the tables
Set travelHistory = ie.Document.GetElementByID("travel-history")
'## Split teh table to an array
history = Split(travelHistory.innerText, vbLf)
'## Iterate the array and write each row to the worksheet
For Each h In history
Range("A1").Offset(i).Value = h
i = i + 1
Next
ie.Quit
Set ie = Nothing
End Sub