【问题标题】:How to optimize the wait method using VBA and Chromedriver如何使用 VBA 和 Chromedriver 优化等待方法
【发布时间】:2018-11-29 17:26:18
【问题描述】:

在本主页“http://www.kpia.or.kr/index.php/year_sugub

如果你检查 html,从 li1 到 li6 有 6 个 id。第一次使用chromedriver后,我注意到的第一件事就是wait方法无效。所以我在这个主页上搜索了各种优化点击互联网后等待的方法。 例如,我应用了以下三种编码。

ex1) Application.Wait Now + TimeSerial (0, 0, 5)

ex2) .FindElementById("li2", timeout: = 10000) .Click

ex3) '做 'DoEvents '错误继续下一步 '设置 ele = .FindElementById ("li2") '在错误转到 0 'If Timer - t = 10 Then Exit Do'

但是,如果不使用 Application.Wait Now + TimeSerial (0, 0, 5),我们最终无法找到优化等待方法的方法。此方法在点击li2后未完全加载,但偶尔会执行额外的任务。

于是,我想到了一种形式化的编码逻辑,以后可以偶尔用它来做类似的编码,我想出了以下逻辑。例如,在 li2 中,Ethylene 的值始终与结果值是固定值,因此如果单击 li2 然后查找“SM”值,数据将加载到工作表中。接下来,li3中的“LDPE”是加载完成后将数据粘贴到工作表中的方式。所以我在用这个想法进行编码,而我在处理 VBA 时无法解决错误。

Dim d As WebDriver, ws As Worksheet, clipboard As Object
Set d = New ChromeDriver
Set ws = ThisWorkbook.Worksheets("Sheet3")
Set clipboard = GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
Const URL = "http://www.kpia.or.kr/index.php/year_sugub"
Dim html As HTMLDocument

Set html = New HTMLDocument

With d
    .AddArgument "--headless"
    .Start "Chrome"
    .get URL, Raise:=False
rep:
    .FindElementById("li2", timeout:=10000).Click

    Dim Posts As WebElements
    Dim elem As WebElements
    Dim a1 As Integer

    For Each Posts In .FindElementsByClass("bbs")
        For Each elem In Posts.FindElementsByCss("td")
            If Not elem.Text = "SM" Is Nothing Then

html.body.innerHTML = d.PageSource

Dim tarTable As HTMLTable
Dim hTable As HTMLTable

For Each tarTable In html.getElementsByTagName("table")
    If InStr(tarTable.className, "bbs") <> 0 Then
    Set hTable = tarTable
    End If
Next

    clipboard.SetText .FindElementById("table_body").Attribute("outerText")
    clipboard.PutInClipboard

    else
    goto rep
    end if
    .Quit

End With

如果找到与 SM 值匹配的值,则假定加载完成并继续将相关数据传输到剪贴板。如果没有找到 SM 值,使用 GOTO 使用 .FindElementById ("li2" timeout: = 10000)。我想我可以通过创建一个从 .Click 重新启动的循环来修复它。

我是一个初学者,正在阅读中节省时间和努力学习,所以如果您能给我更多帮助,我将不胜感激。

【问题讨论】:

  • 这一行 If Not elem.Text = "SM" Is Nothing Then 应该失败,因为只有对象可以是 Nothing 并且 Not elem.Text = "SM" 是布尔值。这段代码能运行吗?
  • 铁人。我知道了。谢谢你的评论。我会试试的

标签: vba selenium web-scraping selenium-chromedriver wait


【解决方案1】:

我将完全避免使用浏览器并发出 XMLHTTP POST 请求并解析 XML 响应以写入工作表。在覆盖每个选项卡的 gubun 代码(即 gubun=1 到 6)上循环执行此操作。

Option Explicit

Public Sub GetTable()
    Dim sResponse As String, body As String, columnToWriteOut As Long, gubunNumber As Long
    Dim xmlDoc As Object

    Set xmlDoc = CreateObject("MSXML2.DOMDocument") 'New MSXML2.DOMDocument60
    columnToWriteOut = 1

    With CreateObject("MSXML2.XMLHTTP")

        For gubunNumber = 1 To 6

            body = "gubun=" & CStr(gubunNumber)
            .Open "POST", "http://www.kpia.or.kr/index.php/year_sugub/get_year_sugub", False
            .setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
            .setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
            .setRequestHeader "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"
            .setRequestHeader "Content-Length", Len(body)
            .send body
            sResponse = .responseText

            With xmlDoc
                .validateOnParse = True
                .setProperty "SelectionLanguage", "XPath"
                .async = False
                If Not .LoadXML(sResponse) Then
                    Err.Raise .parseError.ErrorCode, , .parseError.reason
                End If
            End With

            Dim startYear As Long, endYear As Long, numColumns As Long, numRows As Long, data()
            Dim node As Object, nextNode As Object, headers(), i As Long

            startYear = xmlDoc.SelectSingleNode("//rec/sy").Text
            endYear = xmlDoc.SelectSingleNode("//rec/ey").Text
            numRows = xmlDoc.SelectNodes("//product").Length

            ReDim headers(1 To endYear - startYear + 3)
            numColumns = UBound(headers)
            ReDim data(1 To numRows, 1 To numColumns)
            headers(1) = "Product": headers(2) = "Category"

            For i = 1 To endYear - startYear + 1
                headers(i + 2) = startYear + i - 1
            Next

            Dim r As Long, c As Long, rowCounter As Long

            rowCounter = 0
            For Each node In xmlDoc.SelectNodes("//rec")  ' '//rec/*[not(self::sy) and not(self::ey) and not(self::product)]  ?
                c = 1: rowCounter = rowCounter + 1
                For Each nextNode In node.ChildNodes
                    Select Case c
                    Case 3
                        data(rowCounter, 1) = nextNode.Text
                    Case Is > 3
                        data(rowCounter, c - 1) = nextNode.Text
                    End Select

                    Select Case rowCounter Mod 4
                    Case 1
                        data(rowCounter, 2) = "Production (shipment)"
                    Case 2
                        data(rowCounter, 2) = "Export"
                    Case 3
                        data(rowCounter, 2) = "income"
                    Case 0
                        data(rowCounter, 2) = "Domestic demand "
                    End Select
                    c = c + 1
                Next
            Next

            With ThisWorkbook.Worksheets("Sheet1")
                .Cells(1, columnToWriteOut).Resize(1, UBound(headers)) = headers
                .Cells(2, columnToWriteOut).Resize(UBound(data, 1), UBound(data, 2)) = data
            End With
            columnToWriteOut = columnToWriteOut + UBound(headers) + 2
        Next
    End With
End Sub

或者,您可以循环等待每个 Ajax 调用完成:

Option Explicit

Public Sub GetInfo()
    Dim d As WebDriver, ws As Worksheet, clipboard As Object, writeOutColumn As Long
    writeOutColumn = 1
    Const URL = "http://www.kpia.or.kr/index.php/year_sugub"

    Set d = New ChromeDriver
    Set ws = ThisWorkbook.Worksheets("Sheet3")
    Set clipboard = GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")

    With d
        .Start "Chrome"
        .get URL

        Dim links As Object, i As Long
        Set links = .FindElementsByCss("[href*=action_tab]")

        For i = 1 To links.Count
            If i > 1 Then
                links(i).Click
                Do
                Loop While Not .ExecuteScript("return jQuery.active == 0")
            End If
            Dim table As Object
            Set table = .FindElementByTag("table")
            clipboard.SetText table.Attribute("outerHTML")
            clipboard.PutInClipboard

            ws.Cells(1, writeOutColumn).PasteSpecial
            writeOutColumn = writeOutColumn + table.FindElementByTag("tr").FindElementsByTag("td").Count + 2
            Set table = Nothing
        Next
        .Quit
    End With
End Sub

【讨论】:

  • QHarr。谢谢 !!确认您的回答后,我有两个问题。首先,“Content-Length”,Len(body)..这句话中,我不明白为什么要插入len(body)。其次,我不明白为什么 .ExecuteScript ("return jQuery.active == 0") 是这样写的。这是因为kpia.or.kr/index.php使用了php吗?或者如果我看html,我不能直接看到xml数据地址,而是找到了 $ .ajaxSetup ({dataType: "text"});我想知道是否可以使用 jQuery.active == 0 加载 ajax 格式的数据。
  • 您编写的代码非常出色,足以给人留下深刻印象。我刚刚确认了你写的内容,已经留言了。
  • 当使用开发工具网络选项卡在更新该页面时查看网络流量时,提供该信息的请求包括 len。我只是模仿那个。您也可以尝试不使用它。 XML 数据地址也在那里被捕获。页面在切换选项卡时使用 AJAX 更新内容。这个 .ExecuteScript ("return jQuery.active == 0") 监视 AJAX 更新何时完成。 Put Not 意味着循环直到完成。这是我能找到的优化等待的最可靠方法,因为这意味着内容已加载。
  • 我明白了。正如您所提到的,我已经确认它运行良好,尽管删除了内容长度。顺便说一句,“If-Modified-Since”、“Sat, 1 Jan 2000 00:00:00 GMT”是什么意思?这是即使在网络选项卡上也无法验证的东西,所以我想知道插入这个内容意味着什么。
  • 您也可以删除。我用来缓解被缓存的响应。真的只是非常频繁更新的网页内容的问题,例如实时股票价格。我出于习惯将其包括在内。
猜你喜欢
  • 1970-01-01
  • 2018-02-04
  • 2021-04-10
  • 2018-07-16
  • 1970-01-01
  • 2020-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多