Javascript Can';t在HTMLDocument中设置对象变量,用于从网站中抓取数据

Javascript Can';t在HTMLDocument中设置对象变量,用于从网站中抓取数据,javascript,html,vba,object,web-scraping,Javascript,Html,Vba,Object,Web Scraping,我以前看到过这个问题,但我的情况有些不同,所以我希望我能从社区得到一些帮助,也许还有一个新的视角。我有一个用vba编写的宏,它应该可以从这家公司的在线数据库中提取返回数据,比如MSCI世界指数、标准普尔500指数等的返回数据。我的代码可以在其他页面上使用,但我认为这是不同的。我和网站管理员谈过,他告诉我,代码不是为了刮取而设计的,但这不受他们使用策略的限制。如果我真的可以通过抓取来获取数据的话,这对我来说将是一个巨大的时间节约,所以我正在努力找出一种方法来做到这一点。我也在java脚本下对此进行

我以前看到过这个问题,但我的情况有些不同,所以我希望我能从社区得到一些帮助,也许还有一个新的视角。我有一个用vba编写的宏,它应该可以从这家公司的在线数据库中提取返回数据,比如MSCI世界指数、标准普尔500指数等的返回数据。我的代码可以在其他页面上使用,但我认为这是不同的。我和网站管理员谈过,他告诉我,代码不是为了刮取而设计的,但这不受他们使用策略的限制。如果我真的可以通过抓取来获取数据的话,这对我来说将是一个巨大的时间节约,所以我正在努力找出一种方法来做到这一点。我也在java脚本下对此进行了标记,因为我认为代码非常相似,我希望接受尽可能多的解决方案来解决这个问题

情况是这样的:我有以下代码,在实际抓取数据时抛出“Object variable not set”(对象变量未设置)错误(以“set els=htmlDoc…”开头的行)。我尝试了许多getElement的组合函数思维这可能是问题所在,但我已经画了一张空白。有人知道在这种环境下设置对象变量的其他方法吗?或者只是任何其他创造性的方法来提取数据

我无法给出登录信息,但我认为只要导航到“caRetPage”站点,您就可以看到我正在尝试获取/解析的html代码

Sub caScrape()

Dim ie As Object        'ie: internet explorer
Dim htmlDoc As MSHTML.HTMLDocument
Dim els As Object   'to store html objects
Dim rtn As String   'to store values to be scraped from page
Dim loginButton As Object
caLoginPage = "https://members.cambridgeassociates.com/Login/Forms/login-form.asp"
caRetPage = "https://members.cambridgeassociates.com/markets/marketindexsnapshot/DailyMarketReturnsUS.asp"
caUser = "xxxxx"
caPass = "xxxxx"
Set ie = CreateObject("internetexplorer.application")
ie.Visible = True
ie.navigate caLoginPage
While ie.Busy
    DoEvents
Wend
Do Until ie.readyState = 4
    DoEvents
Loop
Set htmlDoc = ie.document
'Log in to site
Set loginButton = htmlDoc.getElementsByTagName("button").Item(0)
With htmlDoc
    .all("Username").Value = caUser
    .all("Password").Value = caPass
    loginButton.Click
End With
While ie.Busy
    DoEvents
Wend
Set acceptButton = htmlDoc.getElementsByName("Submit").Item(0)
acceptButton.Click
While ie.Busy
    DoEvents
Wend


'Here is the page with the return data on it                                                                                   
ie.navigate caRetPage
While ie.Busy
    DoEvents
Wend
Do Until ie.readyState = 4
    DoEvents
Loop
Set htmlDoc = ie.document

'This next line is where the error gets thrown
Set els = htmlDoc.getElementById("tblData")(0).getElementByTagName("tr")(5).getElementByTagName("td")(1)
    'Also tried the following and plenty of variations of getElement command
'Set els = htmlDoc.getElementsByTagName("body")(0).getElementsByTagName("table")(2).getElementsByTagName("tbody")(0).getElementByTagName("tr")(5).getElementByTagName("td")(1)

rtn = els.innerText
Debug.Print(rtn)


End Sub

任何帮助都将不胜感激。

除了Tim的eagle eye之外,这里还有一些文档。错误(正如Tim指出的)在于getElementById不返回HTML元素列表,而是返回单个元素

这里有一个文档链接


我相信我有一个使用上面代码中提供的URL的工作示例。从我可以看出,网站上有一些框架,因此您需要稍微不同地处理这些框架

此外,等待页面加载需要一种不同的方法。为此,我重用了一些代码,得到了另一个StackOverflow答案。基本上,它会等待直到加载时在页面上找不到更多标记为止

代码如下:

#If VBA7 Then
    Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
#Else
    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

Sub getData()
    Dim element     As Object
    Dim MyURL       As String
    MyURL = "https://members.cambridgeassociates.com/markets/marketindexsnapshot/DailyMarketReturnsUS.asp"

    'Late binding open IE
    Dim MyBrowser   As Object: Set MyBrowser = CreateObject("InternetExplorer.Application")
    MyBrowser.Visible = True
    MyBrowser.navigate MyURL

    waitforload MyBrowser

    Set element = MyBrowser.document.getelementsByTagName("Frameset")(0).Children(1).contentdocument
    Set element = element.getelementByID("tblData")
    Set element = element.getelementsByTagName("tr")(5)
    Set element = element.getelementsByTagName("td")(1)
    Debug.Print element.innertext
End Sub

Private Sub waitforload(ByRef ie As Object)
    Dim i        As Byte
    Dim tagnames As Long

    While ie.Busy
        Sleep 250
        DoEvents
    Wend

    While ie.ReadyState <> 4
        Sleep 250
        DoEvents
    Wend

    Do
        tagnames = ie.document.getelementsByTagName("*").Length
        For i = 1 To 5
            Sleep 75
            If tagnames = ie.document.getelementsByTagName("*").Length Then Exit Sub
        Next
    Loop
End Sub
#如果是VBA7,则
公共声明PtrSafe子睡眠库“kernel32”(ByVal dwr作为LongPtr)
#否则
公共声明子睡眠库“kernel32”(ByVal的长度为毫秒)
#如果结束
子getData()
作为对象的暗元素
将MyURL设置为字符串
MyURL=”https://members.cambridgeassociates.com/markets/marketindexsnapshot/DailyMarketReturnsUS.asp"
“晚装订打开了。”
将MyBrowser设置为对象:设置MyBrowser=CreateObject(“InternetExplorer.Application”)
MyBrowser.Visible=True
MyBrowser.navigate MyURL
WaitForLoadMyBrowser
Set element=MyBrowser.document.getelementsByTagName(“框架集”)(0).子项(1).内容文档
Set element=element.getelementByID(“tblData”)
Set element=element.getelementsByTagName(“tr”)(5)
Set element=element.getelementsByTagName(“td”)(1)
Debug.Print element.innertext
端接头
私有子waitforload(ByRef ie作为对象)
作为字节的Dim i
将标记名变长
趁我忙
睡眠250
多芬特
温德
而ie.ReadyState 4
睡眠250
多芬特
温德
做
标记名=ie.document.getelementsByTagName(“*”).Length
对于i=1到5
睡眠75
如果标记名=ie.document.getelementsByTagName(“*”).Length,则退出子类
下一个
环
端接头

这应该会返回:
0.10

当我在一个长表达式中得到一个错误时,我会将它分解成更小的部分。你会发现表达式的哪一段是这样断开的。
getElementById(“tblData”)
将始终返回单个元素(假设进行了匹配),而不是集合/列表,因此你不需要
(0)
接着说。@SMeaden谢谢你的出现……上次你帮了忙。我不认为这是一个长表达式的问题。这是一个网站不允许我设置变量的问题。你听说过吗?这段代码(或非常类似的版本)在其他页面上运行得很好;这可能是防火墙问题吗?如果是的话,有没有规避的想法?谢谢@TimWilliams;我也尝试了代码的两行,并通过标记名缩小搜索范围。仍然存在错误。另外,如果我尝试设置els=htmlDoc.getElementByID(“tblData”)如果将该行的其余部分留空,则会抛出错误。有什么想法吗?
getElementByTagName
不是有效的方法:它是getElementsByTagName(尽管在注释掉的行中它是正确的)。如果没有HTML源代码或可访问的URL>Ok,就不可能真正提供帮助……我知道getElementByID命令只返回一个元素,但我也尝试了其他方法(请参阅“ID”命令下面两行的注释代码)我仍然无法使用此方法设置变量。对替代代码中可能出现的错误有什么想法吗?我仍然在考虑防火墙。你知道这方面的任何事情吗?如果没有用户名和密码,我们很难调试。你尝试过querySelector吗?看到了吗?你能以某种方式显示HTML页面源代码吗?哇!!太棒了!万分感谢!很抱歉我病了好几天没有反应。太棒了,谢谢瑞安!你怎么知道的?