【问题标题】:Getting Web Element Using VBA使用 VBA 获取 Web 元素
【发布时间】:2022-01-01 18:50:54
【问题描述】:

我似乎无法使用此函数返回 Web 元素。任何帮助将不胜感激。

Function scrape2(id As String)

Set objhttp1 = New WinHttp.WinHttpRequest
Dim doc1 As MSHTML.HTMLDocument
objhttp1.Open "GET", "https://www.axie.tech/axie-pricing/" & id, True
objhttp1.send

Set doc1 = New MSHTML.HTMLDocument
doc1.body.innerHTML = objhttp1.responseText

Set Result = doc1.FindElementByClass("axie-pricing_decodedGenesCapsule__m9R-C")

scrape2 = doc1.Text
    
End Function

函数输入:373595

预期结果:

D
R1
R2
Telescope
Telescope
Sleepless
Sakura
Pink Cheek
Curly
Anemone
Sponge
Blue Moon
Piranha
Piranha
Lam
Lagging
Scaly Spear
Teal Shell
Koi
Nimo
Snake Jar

【问题讨论】:

  • scrape2 = result.Text ?你得到了什么结果?
  • 您不会直接从该 URL 获取信息,它们都是通过脚本和 XHR 生成的。从网络检查并复制 XHR 请求以获取其 JSON 数据。
  • 尝试通过将 open 调用中的最后一个参数更改为 False 来使调用同步。 objhttp1.Open "GET", "https://www.axie.tech/axie-pricing/" & id, False

标签: vba web-scraping


【解决方案1】:

doc1 是一个 MSHTML.HTMLDocument 实例,它没有 FindElementByClass 方法。也不是 DOM 中任何节点的 .text 属性。您引用的方法是 Selenium Type 类中的一种,其中您有一个 WebDriver 的实例。

还有其他各种问题。您在页面上看到的内容是动态呈现的,您想要的数据来自使用返回 JSON 的 graphql 查询的 XHR POST 请求。您需要制定这些请求并在结果上使用 JSON 解析器,如下面的示例所示。

另外,使用Option Explicit,声明所有变量,使用有意义的名称,并通过在.Open 行中传递False 使请求同步。

parts 是您需要在For Each 之上的字典集合,然后是For Each Key,其中Key 是变体,在每个字典的.Keys 之上。
选择您想要的项目。键 abilities 返回一个集合,所以如果使用需要不同的处理。

待办事项:

  1. R1R2 您需要使用父 ID 并从请求中提取这些 ID 信息。下面的代码用于子 ID,并返回您想要的结果列 D
  2. data 请求更多您似乎需要的信息,以便您可以删除其中的许多参数。

JSON 库:

我使用JsonConverter.bas。从 here 下载原始代码并添加到名为 JsonConverter 的标准模块中。从复制的代码中删除顶部的 Attribute .... 行,如果已指定 Option Explicit

然后你需要去:

VBE > Tools > References> 添加引用:

Microsoft Scripting Runtime
Microsoft HTML Object Library
Microsoft WinHTTP Services, version 5.1 Library (or your version). 

在 VBA for JSON 中,[] 表示集合,{} 表示字典。


VBA:

Option Explicit

Public Sub PrintGetAxieDetail()

    Dim response As Object
    
    Set response = GetAxieGeneDetail("5016162")
    
    Debug.Print JsonConverter.ConvertToJson(response)
    
End Sub

Public Function GetAxieGeneDetail(ByVal id As String) As Object
    
    Dim http As WinHttp.WinHttpRequest
    Dim doc1 As MSHTML.HTMLDocument
    Dim data As String
    
    Set http = New WinHttp.WinHttpRequest
    Set doc1 = New MSHTML.HTMLDocument
    
    With http
    
        .Open "POST", "https://axieinfinity.com/graphql-server-v2/graphql", False
        
        .setRequestHeader "content-type", "application/json"
        .setRequestHeader "user-agent", "Mozilla/5.0"
        .setRequestHeader "referer", "https://www.axie.tech/"
        .setRequestHeader "accept-language", "en-GB,en-US;q=0.9,en;q=0.8"

        data = "{""operationName"":""GetAxieDetail"",""variables"":{""axieId"":""" & id & """},""query"":""query GetAxieDetail($axieId: ID!)" & _
                "{\n  axie(axieId: $axieId) {\n    ...AxieDetail\n    __typename\n  }\n}\n\nfragment AxieDetail on Axie {\n  id\n  image" & _
                "\n  class\n  chain\n  name\n  genes\n  owner\n  birthDate\n  bodyShape\n  class\n  sireId\n  sireClass\n  matronId\n  matronClass" & _
                "\n  stage\n  title\n  breedCount\n  level\n  figure {\n    atlas\n    model\n    image\n    __typename\n  }" & _
                "\n  parts {\n    ...AxiePart\n    __typename\n  }\n  stats {\n    ...AxieStats\n    __typename\n  }" & _
                "\n  auction {\n    ...AxieAuction\n    __typename\n  }\n  ownerProfile {\n    name\n    __typename\n  }" & _
                "\n  battleInfo {\n    ...AxieBattleInfo\n    __typename\n  }" & _
                "\n  children {\n    id\n    name\n    class\n    image\n    title\n    stage\n    __typename\n  }\n  __typename" & _
                "\n}\n\nfragment AxieBattleInfo on AxieBattleInfo {\n  banned\n  banUntil\n  level\n  __typename" & _
                "\n}\n\nfragment AxiePart on AxiePart {\n  id\n  name\n  class\n  type\n  specialGenes\n  stage\n  abilities" & _
                "{\n    ...AxieCardAbility\n    __typename\n  }\n  __typename\n}\n\nfragment AxieCardAbility on AxieCardAbility" & _
                "{\n  id\n  name\n  attack\n  defense\n  energy\n  description\n  backgroundUrl\n  effectIconUrl\n  __typename\n}" & _
                "\n\nfragment AxieStats on AxieStats {\n  hp\n  speed\n  skill\n  morale\n  __typename\n}\n\nfragment AxieAuction on Auction" & _
                "{\n  startingPrice\n  endingPrice\n  startingTimestamp\n  endingTimestamp\n  duration\n  timeLeft\n  currentPrice" & _
                "\n  currentPriceUSD\n  suggestedPrice\n  seller\n  listingIndex\n  state\n  __typename\n}\n""}"

        .send data
        
        Dim axieDetail As Object, geneResults As Object
        
        Set axieDetail = JsonConverter.ParseJson(.responseText)
        Set geneResults = axieDetail("data")("axie")("parts") 'this returns a collection to For Each over
        Set GetAxieGeneDetail = geneResults
        
    End With
    
End Function

JSON 示例:

【讨论】:

  • 您介意在屏幕截图中分享一下 JSON 解析器吗?
  • @RaymondWu 这不是一个很棒的 json 查看器 IMO,但有时会处理许多其他查看器拒绝的字符串格式。显示的是jsonviewer.stack.hu,但有关选择请参阅:geekflare.com/json-online-tools 并选择适合您需要的。我通常使用 jsoneditoronline.org ,并在屏幕截图中使用一个用于困难的情况,但在 geekflare 的列表中看到了一些我喜欢的外观。
  • 谢谢!我很欣赏这个建议,我很久以前就一直在使用谷歌搜索,视觉上它非常简单,所以我一定会尝试一下!
  • 是的,至少我认为您需要一个允许导入/导出、验证/错误标记、文本搜索和树视图的工具。 @RaymondWu
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-21
  • 2022-01-20
  • 2012-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多