1

各位下午好。在 QHarr 非常解决的先前查询的后续操作中,我想针对源代码中的多个字段而不是一个字段运行已解决的查询。

我使用的网址是:https ://finance.yahoo.com/quote/AAPL/?p=AAPL

取价格的VBA代码'Previous Close'是:

Option Explicit

    Sub PreviousClose()
        Dim html As HTMLDocument, http As Object, ticker As Range
        Set html = New HTMLDocument
        Set http = CreateObject("WINHTTP.WinHTTPRequest.5.1")

    Dim lastRow As Long, myrng As Range
    With ThisWorkbook.Worksheets("Tickers")

        lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
        Set myrng = .Range("A2:A" & lastRow)

        For Each ticker In myrng
            If Not IsEmpty(ticker) Then
                With http
                    .Open "GET", "https://finance.yahoo.com/quote/" & ticker.Value & "?p=" & ticker.Value, False
                    .send
                    html.body.innerHTML = .responseText
                End With
                On Error Resume Next
                ticker.Offset(, 1) = html.querySelector("[data-test=PREV_CLOSE-value]").innertext

                On Error GoTo 0
            End If
        Next

    End With
End Sub

无论如何,理想情况下,每个字段都应位于股票代码右侧的一行中。

工作表截图:

图片

任何帮助将不胜感激。
谢谢。

4

2 回答 2

4

tl;博士;

下面的代码适用于给定的测试用例。对于更长的列表,请参阅该ToDo部分。

接口:

如果可能,您希望查看 API 以提供此信息。我相信Alpha Vantage现在提供了 Yahoo Finance API 用于* 的信息。这里有一个不错的 JS 教程。Alpha Vantage 文档在这里。在这个答案的最底部,我快速浏览了通过 API 可用的时间序列函数。

网络服务功能:

使用 API 密钥,您还可以潜在地使用 Excel 中的 Web 服务功能来检索和解析数据。这里的例子。未测试。

XMLHTTPRequest 和类:

但是,我将向您展示一种使用类和 URL 循环的方法。你可以改进这一点。我使用一个简单的类clsHTTP来保存 XMLHTTP 请求对象。我给它2个方法。一个,GetHTMLDoc,以 html 文档的形式返回请求响应,另一个,GetInfo,返回页面中感兴趣的项目的数组。

以这种方式使用类意味着我们节省了重复创建和销毁 xmlhttp 对象的开销,并提供了一组很好的描述性公开方法来处理所需的任务。

假设您的数据如图所示,标题行为第 2 行。

去做:

立即显而易见的开发,IMO,是您将想要添加一些错误处理。例如,您可能想要开发类来处理服务器错误。


VBA:

因此,在您的项目中添加一个名为的类模块clsHTTP并放入以下内容:

clsHTTP

Option Explicit

Private http As Object
Private Sub Class_Initialize()
    Set http = CreateObject("MSXML2.XMLHTTP")
End Sub

Public Function GetHTMLDoc(ByVal URL As String) As HTMLDocument
    Dim html As HTMLDocument
    Set html = New HTMLDocument
    With http
        .Open "GET", URL, False
        .send
        html.body.innerHTML = StrConv(.responseBody, vbUnicode)
        Set GetHTMLDoc = html
    End With
End Function
Public Function GetInfo(ByVal html As HTMLDocument, ByVal endPoint As Long) As Variant
    Dim nodeList As Object, i As Long, result(), counter As Long
    Set nodeList = html.querySelectorAll("tbody td")
    ReDim result(0 To endPoint - 1)
    For i = 1 To 2 * endPoint Step 2
        result(counter) = nodeList.item(i).innerText
        counter = counter + 1
    Next    
    GetInfo = result
End Function

在标准模块中(模块 1)

Option Explicit
Public Sub GetYahooInfo()
    Dim tickers(), ticker As Long, lastRow As Long, headers()
    Dim wsSource As Worksheet, http As clsHTTP, html As HTMLDocument

    Application.ScreenUpdating = False

    Set wsSource = ThisWorkbook.Worksheets("Sheet1") '<== Change as appropriate to sheet containing the tickers
    Set http = New clsHTTP

    headers = Array("Ticker", "Previous Close", "Open", "Bid", "Ask", "Day's Range", "52 Week Range", "Volume", "Avg. Volume", "Market Cap", "Beta", "PE Ratio (TTM)", "EPS (TTM)", _
                    "Earnings Date", "Forward Dividend & Yield", "Ex-Dividend Date", "1y Target Est")

    With wsSource
        lastRow = GetLastRow(wsSource, 1)
        Select Case lastRow
        Case Is < 3
            Exit Sub
        Case 3
            ReDim tickers(1, 1): tickers(1, 1) = .Range("A3").Value
        Case Is > 3
            tickers = .Range("A3:A" & lastRow).Value
        End Select

        ReDim results(0 To UBound(tickers, 1) - 1)
        Dim i As Long, endPoint As Long
        endPoint = UBound(headers)

        For ticker = LBound(tickers, 1) To UBound(tickers, 1)
            If Not IsEmpty(tickers(ticker, 1)) Then
                Set html = http.GetHTMLDoc("https://finance.yahoo.com/quote/" & tickers(ticker, 1) & "/?p=" & tickers(ticker, 1))
                results(ticker - 1) = http.GetInfo(html, endPoint)
                Set html = Nothing
            Else
                results(ticker) = vbNullString
            End If
        Next

        .Cells(2, 1).Resize(1, UBound(headers) + 1) = headers
        For i = LBound(results) To UBound(results)
            .Cells(3 + i, 2).Resize(1, endPoint-1) = results(i)
        Next
    End With   
    Application.ScreenUpdating = True
End Sub

Public Function GetLastRow(ByVal ws As Worksheet, Optional ByVal columnNumber As Long = 1) As Long
    With ws
        GetLastRow = .Cells(.Rows.Count, columnNumber).End(xlUp).Row
    End With
End Function

结果:

结果


关于GetInfo方法和 CSS 选择器的注意事项:

类方法GetInfo使用 css 组合选择器从每个网页中提取信息以定位页面样式。

我们在每一页上的信息都在两个相邻的表格中,例如:

我没有搞乱多个表格,而是简单地针对表格主体元素中的所有表格单元格,使用tbody td.

CSS 选择器组合通过 的querySelectorAll方法应用HTMLDocument,返回一个静态的nodeList

返回的nodeList项目在偶数索引处具有标题,在奇数索引处具有所需数据。nodeList我只想要前两个信息表,所以当我给出两倍于感兴趣的标头长度时,我终止了返回的循环。我使用索引 1 中的第 2 步循环仅检索感兴趣的数据,减去标题。

返回的示例nodeList


参考资料(VBE > 工具 > 参考资料):

  1. Microsoft HTML 对象库

Alpha Vantage API:

快速查看time series API调用表明可以使用字符串

https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=AA&outputsize=full&apikey=yourAPIKey

这会产生一个 JSON 响应,在Time Series (Daily)整个返回字典的子字典中,返回了 199 个日期。每个日期都有以下信息:

稍微深入研究一下文档将揭示是否可以捆绑股票代码,我无法很快看到这一点,以及是否可以通过不同的查询字符串获得更多您感兴趣的初始项目。

还有更多信息,例如,使用TIME_SERIES_DAILY_ADJUSTEDURL 调用中的函数

https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=AA&outputsize=full&apikey=yourAPIkey

在这里,您将获得以下信息:

您可以使用JSONConverter.bas等JSON 解析器来解析 JSON 响应,并且还有用于 csv 下载的选项。

*值得研究哪些 API 可以最大程度地覆盖您的项目。Alpha Vantage 似乎没有我上面的代码检索到的那么多。

于 2018-09-08T11:52:50.427 回答
0

那是一些漂亮的代码!我很喜欢!!顺便说一句,您可能需要考虑使用 R 来做这种事情。看看你可以用几行简单的代码做什么!

library(finreportr)

# print available functions in finreportr
ls('package:finreportr')

my.ticker <- 'SBUX'

# set final year
my.year <- 2017

# get income for FB
my.income <- GetIncome(my.ticker, my.year)

# print result
print(head(my.income))


# get unique fields
unique.fields <- unique(my.income$Metric)

# cut size of string
unique.fields <- substr(unique.fields,1, 60)

# print result
print(unique.fields)


# set col and date
my.col <- 'Earnings Per Share, Basic'

# print earnings per share
print(my.income[my.income$Metric == my.col, ])


library(tidyquant)

# set stock and dates
my.ticker <- 'AAPL'
first.date <- '2017-01-01'
last.date <-  Sys.Date()

# get data with tq_get
my.df <- tq_get(my.ticker,
                get = "stock.prices", 
                from = first.date, 
                to = last.date)

print(tail(my.df))


# get key financial rations of AAPL
df.key.ratios <- tq_get("AAPL",get = "key.ratios")

# print it
print(df.key.ratios) 
于 2018-09-16T15:11:40.850 回答