3

I am trying to scrape a website and there are many elements I am dealing with. I need to wait for elements to be loaded.

This is my try till now but I am waiting a long time and sometimes I get errors.

.FindElementById("ContentPlaceHolder1_Button1").Click
.Wait 2000
GoBack1:
Set elePrint = .FindElementById("IconImg_CrystalReportViewer1_toptoolbar_print", timeout:=20000, Raise:=False)
If elePrint Is Nothing Then
    Application.Wait Now() + TimeValue("00:00:01"): GoTo GoBack1
Else
    elePrint.Click
End If
GoBack2:
Set eleExport = .FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn", timeout:=20000, Raise:=False)
If eleExport Is Nothing Then
    Application.Wait Now() + TimeValue("00:00:01"): GoTo GoBack2
Else
    eleExport.Click
End If

Is there a better way for doing this?

This is the html part

<tbody><tr valign="middle"><td height="21" width="5" style="background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px 0px;"></td><td id="theBttnCenterImgbobjid_1545656314367_dialog_submitBtn" align="center" class="wizbutton" style="padding-left:3px;padding-right:3px;background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px -42px;"><nobr><a id="theBttnbobjid_1545656314367_dialog_submitBtn" href="javascript:void(0)" class="wizbutton" role="button">Export</a></nobr></td><td height="21" width="5" style="background-image:url('aspnet_client/system_web/4_0_30319/crystalreportviewers13/js/crviewer/../dhtmllib/images/skin_standard/button.gif');background-position:0px -21px;"></td></tr></tbody>
Community
  • 1
  • 1
YasserKhalil
  • 9,138
  • 7
  • 36
  • 95

4 Answers4

4

You can try looping your elements until they become available.

On Error Resume Next
Do While .FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn") Is Nothing
    DoEvents
Loop
On Error Goto 0

I would also consider adding a timer to your loop if for some reason the the webpage hangs - which if this is the case you could reload the webpage or perform some other error handling operation.

K.Dᴀᴠɪs
  • 9,945
  • 11
  • 33
  • 43
2

You can shorten this to

Do 
Loop While .FindElementsByCss("#theBttnbobjid_1545642213647_dialog_submitBtn").Count = 0 

Though you should set a timeout

Const MAX_WAIT_SEC As Long = 10
Dim t 
t = Timer
Do 
    If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop While .FindElementsByCss("#theBttnbobjid_1545642213647_dialog_submitBtn").Count = 0 

There won't be an error and there is no need to yield control. If the item is not found the .Count will be zero.

If the id is dynamic and you have a constant substring (which only occurs for one id attribute on the page (to be safe - may not be essential) you can use ^, *, $ operators in css). For example, if the start string is constant across pages change the css selector to

[id^='theBttnbobjid']

If occurs more than once, and the index is constant across pages then use the index to interact later e.g.

.FindElementsByCss("[id^='theBttnbobjid']")(2)
QHarr
  • 83,427
  • 12
  • 54
  • 101
  • Thanks a lot for help. I have noticed that this `#theBttnbobjid_1545642213647_dialog_submitBtn` is changeable. How can I deal with it in this case ..? I am dealing with many pages and each time has different selector !! that's too weird – YasserKhalil Dec 24 '18 at 13:01
  • Yes that is true ..I have inspected another page and this was the result `theBttnbobjid_1545656314367_dialog_submitBtn` – YasserKhalil Dec 24 '18 at 13:04
  • I think so.. can it be implemented by partial text? But when searching for `dialog_submitBtn` I found three instances but the beginning is different in the three so can we rely on the start part too `theBttnbobjid`? – YasserKhalil Dec 24 '18 at 13:05
  • I tried such a selector `theBttnbobjid_*_dialog_submitBtn` but doesn't select anything.. I see your edit but how to include the start part too? – YasserKhalil Dec 24 '18 at 13:16
  • Ok It is there but this is for start only – YasserKhalil Dec 24 '18 at 13:17
  • 1
    I am sorry. I thought I have posted the html part but it seems I have posted in in another thread at this [LINK](https://stackoverflow.com/questions/53911876/download-file-to-specific-directory-using-selenium-excel-vba?noredirect=1#comment94669621_53911876) ... I have included the html part too here – YasserKhalil Dec 24 '18 at 13:20
  • I found two instances with that beginning .. so if possible I need a solution for start and end parts together ..so as to get just one instance – YasserKhalil Dec 24 '18 at 13:26
  • The second one .. is the target – YasserKhalil Dec 24 '18 at 13:33
  • 1
    Is it always the second one? If so you can use the method above but when you come to interact with the target use index .FindElementsByCss("[id^='theBttnbobjid']")(2) . I think indices are 1 based in selenium otherwise use 1. – QHarr Dec 24 '18 at 13:35
  • 1
    Thank you very much for awesome help. You are a legend – YasserKhalil Dec 24 '18 at 13:41
1

Shorter alternative:

Do While Not .FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn").IsDisplayed 
    DoEvents
Loop
Robert Fridzema
  • 517
  • 3
  • 18
1

This is what I do in my codes:

.FindElementById("theBttnbobjid_1545642213647_dialog_submitBtn").WaitDisplayed