使用VBA将特定Web数据导入Excel

时间:2016-03-05 19:32:32

标签: excel vba excel-vba web-scraping

我非常喜欢VBA编码场景(Web脚本更像我的事情)但是我有一个基于Excel的程序,我需要创建它将数据从基于Intranet web的应用程序导入到电子表格中。以下是我要设置的要点...... 在电子表格中,用户将输入以下信息:用户名,密码,客户帐号列表和日期范围。然后,用户将单击将发生以下情况的“命令按钮”:

  1. 打开基于网络的程序,登录(根据登录/密码输入电子表格)并导航到帐户搜索屏幕。

  2. 在搜索字段中输入第一个客户帐号,然后单击“搜索”按钮以导航到特定的客户帐户。

  3. 导航至“搜索活动”屏幕,输入日期范围,然后点击“搜索活动按钮。

  4. 从活动表的特定列中提取数据并将数据导入电子表格。

  5. 如果有多个数据页面会有一个“下一个结果”按钮,则应该有一个循环来单击下一个结果按钮(如果存在)并从每个页面拉出相同的数据列直到该按钮不再存在(没有更多数据)。

  6. 一旦没有更多的数据页面(或者如果只有一个页面),宏将循环返回并导航到帐户搜索屏幕,并对输入的帐户列表中的每个帐户执行相同的操作电子表格,直到没有其他帐户。

  7. 完成后(所有数据都成功导入电子表格),它应关闭IE窗口。

  8. 这有点复杂,我发现excel / vba绝对不是执行这些功能的最佳解决方案,但不幸的是,这是我必须在这个实例中使用的。我已经能够拼凑出几乎所有上述内容的VBA,我遇到的问题是循环遍历活动页面并且拉动数据不起作用(得到的各种错误只会让我更加困惑),有时它会从第一张表中提取数据,单击“下一步结果”按钮,转到下一页并抛出错误,甚至可以通过两到三页并抛出错误。它没有多大意义,但最常见的错误是“权限被拒绝”。此代码目前只从一个帐户中提取数据,我希望一旦我让它为一个帐户工作,就可以很容易地创建一个完整代码的循环,让它在帐号列表中下载并执行相同的操作每个都完成。我已经坚持了几个星期,我已经准备好抛弃整个事情并从头开始,任何帮助都将非常感激!

    以下是我到目前为止的代码......

    Private Sub CommandButton1_Click()
    
        ' open IE, navigate to the desired page and loop until fully loaded
        Set IE = New InternetExplorerMedium
        my_url = "https://customerinfo/pages/login.jsp"
        my_url2 = "https://customerinfo/pages/searchCustomer.jsp"
        my_url3 = "https://customerinfo/pages/searchAccountActivity.jsp"
    
        With IE
            .Visible = True
            .navigate my_url
            Do Until Not .Busy And .readyState = 4
                DoEvents
            Loop
        End With
    
        ' Input the userid and password
        IE.document.getElementById("userId").Value = [B2]
        IE.document.getElementById("password").Value = [B3]
    
        ' Click the "Login" button
        IE.document.getElementById("action").Click
        Do Until Not IE.Busy And IE.readyState = 4
            DoEvents
        Loop
    
        ' Navigate to Search screen
        With IE
            .navigate my_url2
            Do Until Not .Busy And .readyState = 4
                DoEvents
            Loop
        End With
    
        ' Input the account number & click search
        IE.document.getElementById("accountNumber").Value = [B5]
        IE.document.getElementById("action").Click
        Do Until Not IE.Busy And IE.readyState = 4
            DoEvents
        Loop
    
        With IE
            .navigate my_url3
            Do Until Not .Busy And .readyState = 4
                DoEvents
            Loop
        End With
    
        'Input search criteria
        IE.document.getElementById("store").Value = [C7]
        IE.document.getElementById("dateFromMonth").Value = [C10]
        IE.document.getElementById("dateFromDay").Value = [B11]
        IE.document.getElementById("dateFromYear").Value = [B12]
        IE.document.getElementById("timeFromHour").Value = [B20]
        IE.document.getElementById("timeFromMinute").Value = [B21]
        IE.document.getElementById("dateToMonth").Value = [C15]
        IE.document.getElementById("dateToDay").Value = [B16]
        IE.document.getElementById("dateToYear").Value = [B17]
        IE.document.getElementById("timeToHour").Value = [B24]
        IE.document.getElementById("timeToMinute").Value = [B25]
        IE.document.getElementById("action").Click
        Do Until Not IE.Busy And IE.readyState = 4
            DoEvents
        Loop
    
        'Pulls data from activity search
        Dim TDelements As IHTMLElementCollection
        Dim TDelement As HTMLTableCell
        Dim r As Long, i As Long
        Dim e As Object
    
        Application.Wait Now + TimeValue("00:00:05")
        Set TDelements = IE.document.getElementsByTagName("tr")
        r = 0
        For i = 1 To 1
            Application.Wait Now + TimeValue("00:00:03")
            For Each TDelement In TDelements
                If TDelement.className = "searchActivityResultsOldContent" Then
                    Sheet1.Range("E1").Offset(r, 0).Value = TDelement.ChildNodes(8).innerText
                    r = r + 1
                ElseIf TDelement.className = "searchActivityResultsNewContent" Then
                    Sheet1.Range("E1").Offset(r, 0).Value = TDelement.ChildNodes(8).innerText
                    r = r + 1
                End If
            Next
            Application.Wait Now + TimeValue("00:00:02")
            Set elems = IE.document.getElementsByTagName("input")
            For Each e In elems
                If e.Value = "Next Results" Then
                    e.Click
                    i = 0
                    Exit For
                End If
            Next e
        Next i
    
        Do Until Not IE.Busy And IE.readyState = 4
          DoEvents
        Loop
        IE.Quit
    
    End Sub
    

1 个答案:

答案 0 :(得分:0)

那么,点击“下一步......”元素后会发生什么?让我描述一下我遇到的问题。假设代码流如下:

  1. 创建IE实例,然后导航到某个URL,例如: G。第一个搜索结果页面。
  2. 检查页面是否已加载并准备就绪。等等。
  3. 创建由DispHTMLElementCollection检索的目标元素的.document.getElementsByTagName()集合等。
  4. 遍历集合的元素,做一些事情。
  5. 点击“下一步...”元素。问题是,在某些情况下,由于某些JS或XHR处理,下一页在点击后不会立即开始下载。
  6. 如果下一页已加载并准备好,请进行常规检查。这种检查只允许进一步执行代码而没有任何延迟,因为在点击之后没有立即开始下一页的下载,并且当前现有页面被确定为下载并准备好下一页,这是错误的。简单的几秒延迟不能提供准备页面的可靠方法。
  7. 再次,错误地从现有页面而不是下一页创建元素的DispHTMLElementCollection集合。
  8. 循环创建集合的元素。循环正在进行时,下一页开始下载。该集合仍包含对对象的引用,但实际上已卸载具有该对象的页面。因此,无论是尝试访问卸载页面的元素还是由于文档对象没有响应,操作都会给出“权限被拒绝”错误。
  9. 我的线索是避免点击“下一步...”,尝试从“下一步...”锚点.href元素的<a>属性中读取下一页网址,然后调用{ {1}}到该网址,然后检查网页准备情况。

    查看example implementing that approach

    IMO最有效的方法是使用XHR,例如thisthisthis