【问题标题】:Scraping specific data inside a table II在表 II 中抓取特定数据
【发布时间】:2023-03-12 03:19:01
【问题描述】:

我讨厌我不得不再次问这个问题,但是我一直在从更新中抓取数据的网站,不仅仅是在美学上,底层代码也发生了变化。在更新之前,程序会查找“关键数据”表并使用计数器查找特定数据。问题是我不再进入值,当我尝试使用更接近值的类名时,它根本找不到它并退出程序。

我已经剪掉了下面的一些代码来分享,不胜感激。和上次一样,我感觉很接近,但我还不够。

Set ieObj = New InternetExplorer
ieObj.Visible = True
ieObj.navigate "https://web.tmxmoney.com/quote.php?qm_symbol=" & Cells(c, 2) & ":US"

Do While ieObj.readyState <> 4
    Application.StatusBar = "Getting to 'Key Data' Table"
    DoEvents
Loop

d = 0
For Each htmlELE In ieObj.document.getElementsByClassName("sc-kfYqjs jpcbJh")
    
    'scrape EPS amount
    If d = 9 Then
        EPS = htmlELE.innerText
        Range("H2").value = EPS
    End If

    'scrape dividend
    If d = 14 Then
        div = htmlELE.innerText
        Range("I2").value = div
    End If

d = d + 1
Next


【问题讨论】:

    标签: html excel vba web-scraping


    【解决方案1】:

    这是一个动态页面。内容将通过向下滚动加载。所以你必须等待加载页面的“第一部分”。然后向下滚动到所需的表格 1500。向下滚动后等待加载表格。

    您不需要循环来抓取想要的值。您可以使用querySelector() 通过属性名称和属性值获取特定元素。

    在此处查看有关querySelector()的信息
    这里的例子你也可以使用querySelector():querySelectorAll()

    此代码对我有用。如果不是你玩的休息时间和滚动量:

    Sub test()
    
    Dim ieObj As Object
    Dim nodeEps As Object
    Dim nodeDividend As Object
    Dim eps As String
    Dim dividend As String
    
      Set ieObj = CreateObject("InternetExplorer.Application")
      ieObj.Visible = True
      'ieObj.navigate "https://web.tmxmoney.com/quote.php?qm_symbol=" & Cells(c, 2) & ":US"
      ieObj.navigate "https://web.tmxmoney.com/quote.php?qm_symbol=GE:US"
      
      Do While ieObj.readyState <> 4
        Application.StatusBar = "Getting to 'Key Data' Table"
        DoEvents
      Loop
      Application.Wait (Now + TimeSerial(0, 0, 3))
      ieObj.document.parentWindow.Scroll 0, 1500
      Application.Wait (Now + TimeSerial(0, 0, 2))
      
      'scrape EPS amount
      Set nodeEps = ieObj.document.querySelector("div[data-testid='eps-value']")
      eps = Trim(nodeEps.innerText)
      'Range("H2").Value = eps
      
      'scrape dividend
      Set nodeDividend = ieObj.document.querySelector("div[data-testid='dividendAmount-value']")
      dividend = Trim(nodeDividend.innerText)
      'Range("I2").Value = dividend
      
      'Clean up
      ieObj.Quit
      Set ieObj = Nothing
      Set nodeEps = Nothing
      Set nodeDividend = Nothing
      Application.StatusBar = False
      
      MsgBox eps & Chr(13) & dividend
    End Sub
    

    【讨论】:

    • 感谢您的宝贵时间。我在 eps 刮片上收到“运行时错误'424':需要对象”通知,行“Set nodeEps = ie.obj.document ...”我认为这与上面定义的对象有点奇怪.我检查了 Microsoft HTML 对象库,但我还缺少什么吗?
    • 成功!你是正确的调整休息时间和滚动。感谢您让我看到“queryselector”——生活的改变者。
    • @Matt 我很高兴您现在再次获得所需的数据。在我的代码中,不需要 Excel 中的库引用。我使用后期绑定,因为代码无需进一步准备就可以在每台计算机上运行。早期绑定需要引用。 Infos:riptutorial.com/excel-vba/example/10855/…你看过SIM的解决方案了吗?它的优点是您可以节省 IE 的整个处理过程,并且答案来得更快。但是如果你以前从未使用过 xhr 和 JSON,你必须花更多的时间来理解它。
    【解决方案2】:

    如果您将发布请求连同适当的参数一起发送到正确的 url,您可以使用 xmlhttp 请求获取 eps 的值。运行脚本时,您应该得到包含所需结果的 json 响应。我使用正则表达式来解析您感兴趣的特定部分。

    有效载荷比平时大。但是,您可以相应地更改变量中的代码名称。

    你可以这样做:

    Sub GetContent()
        Const link = "https://app-money.tmx.com/graphql"
        Dim elem As Object, payload As Variant, S$, tickerName$
        
        tickerName = "AAPL:US"      'use ticker name here
    
        payload = "{""operationName"":""getQuoteBySymbol"",""variables"":{""symbol"":""" & tickerName & """,""locale"":""en""},""query"":""query getQuoteBySymbol($symbol: String, $locale: String) {\n  getQuoteBySymbol(symbol: $symbol, locale: $locale) {\n    symbol\n    name\n    price\n    priceChange\n    percentChange\n    exchangeName\n    exShortName\n    exchangeCode\n    marketPlace\n    sector\n    industry\n    volume\n    openPrice\n    dayHigh\n    dayLow\n    MarketCap\n" & _
                "MarketCapAllClasses\n    peRatio\n    prevClose\n    dividendFrequency\n    dividendYield\n    dividendAmount\n    dividendCurrency\n    beta\n    eps\n    exDividendDate\n    shortDescription\n    longDescription\n    website\n    email\n    phoneNumber\n    fullAddress\n    employees\n    shareOutStanding\n    totalDebtToEquity\n    totalSharesOutStanding\n    sharesESCROW\n    vwap\n    dividendPayDate\n    weeks52high\n    weeks52low\n    alpha\n    averageVolume10D\n    averageVolume30D\n    averageVolume50D\n    priceToBook\n    priceToCashFlow\n    returnOnEquity\n" & _
                "returnOnAssets\n    day21MovingAvg\n    day50MovingAvg\n    day200MovingAvg\n    dividend3Years\n    dividend5Years\n    datatype\n    __typename\n  }\n}\n""}"
    
        With CreateObject("MSXML2.XMLHTTP")
            .Open "POST", link, False
            .setRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
            .setRequestHeader "Content-Type", "application/json"
            .send payload
            S = .responseText
        End With
        
        With CreateObject("VBScript.RegExp")
            .Global = True
            .MultiLine = True
    
            .Pattern = """eps"":(.*?),"
            Set elem = .Execute(S)
            MsgBox elem(0).SubMatches(0)
        End With
    End Sub
    

    【讨论】:

    • 这是迄今为止我在 SO 上读到的最棒的答案之一。对我来说,这是一种新方法,比使用 IE 有很大的优势。感谢您通过 xhr 和 JSON 深入了解解决方案方法。我了解服务器的答案,但我不知道您从哪里知道,如何为有效负载构建 JSON?你能给我一个提示或研究方法吗?到目前为止,我还将在此问题上寻找您对 SO 的回复。
    • 好的,我将在此处粘贴一些有助于理解整个过程的链接。谢谢。
    • 好的,我在网络分析中找到了 JSON。可能每次都需要检查哪一个是正确的有效载荷。链接非常受欢迎。谢谢。
    • 检查network activity和创建json params
    • 对不起,由于我对 VBA 的了解有限,这个例子让我无法理解,但是当我有更多的时间时,我将不得不尝试它。感谢提供。
    猜你喜欢
    • 2020-04-25
    • 2017-01-26
    • 1970-01-01
    • 2019-01-03
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    相关资源
    最近更新 更多