Веб-исходник Excel VBA - как извлечь несколько полей на один лист - PullRequest
0 голосов
/ 08 сентября 2018

Добрый день, ребята. В продолжение предыдущего запроса, который был в значительной степени решен QHarr, я хотел выполнить решенный запрос для нескольких полей из исходного кода, а не только для одного.

URL-адрес, который я использую: 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

В любом случае, каждое поле в идеале должно быть в строке справа от тикера, используемого для акции.

Снимок экрана:

image

Любая помощь будет принята с благодарностью.
Спасибо.

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

Это какой-то ловкий код !! Мне это очень нравится!! Кроме того, вы можете рассмотреть возможность использования 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) 
0 голосов
/ 08 сентября 2018

ТЛ; др;

Код ниже работает для данных тестов. С более длинными списками смотрите раздел ToDo.

API:

Вы хотите заглянуть в API, чтобы предоставить эту информацию, если это возможно. Я считаю, что Alpha Vantage теперь предоставляет информацию, которую использовал Yahoo Finance API *. Здесь есть хорошее руководство по JS . Документация Alpha Vantage здесь . В самом низу этого ответа я кратко рассмотрю функции временных рядов, доступные через API.

Функция веб-сервиса:

С помощью ключа API вы также можете использовать функцию веб-сервиса в Excel для извлечения и анализа данных. Пример здесь . Не тестировалось.

XMLHTTPЗапрос и класс:

Тем не менее, я покажу вам, как использовать класс и цикл по URL. Вы можете улучшить это. Я использую класс «голые кости» под названием clsHTTP для хранения объекта запроса XMLHTTP. Я даю ему 2 метода. Один, GetHTMLDoc, для возврата ответа на запрос в HTML-документе, а другой, GetInfo, для возврата массива интересующих элементов со страницы.

Использование класса таким способом означает, что мы экономим на накладных расходах на многократное создание и уничтожение объекта xmlhttp и предоставляем хороший описательный набор открытых методов для обработки требуемых задач.

Предполагается, что ваши данные такие же, как показано, а строка заголовка - строка 2.

ToDo:

Сразу видно, что 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

Результаты:

results


Примечания к методу GetInfo и селекторам CSS:

Метод класса GetInfo извлекает информацию из каждой веб-страницы, используя селектор комбинации css для определения стиля страницы.

Информация, которую мы ищем на каждой странице, размещается в двух смежных таблицах, например:

image

Вместо того, чтобы возиться с несколькими таблицами, я просто нацеливаюсь на все ячейки таблицы в элементах тела таблицы с помощью комбинации селекторов tbody td.

Селекторная комбинация CSS применяется с помощью querySelectorAll метода HTMLDocument, возвращающего статический nodeList.

Возвращенные элементы nodeList имеют заголовки с четными индексами и необходимые данные с нечетными индексами. Мне нужны только первые две таблицы информации, поэтому я прекращаю цикл над возвращенным nodeList, когда я дал вдвое больше длины интересующих заголовков. Я использую цикл шага 2 из индекса 1 для извлечения только интересующих данных, за исключением заголовков.

Пример того, как выглядит nodeList:

image


Ссылки (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 дат. Каждая дата имеет следующую информацию:

image

Немного покопавшись в документации, вы узнаете, возможно ли связывание тикеров, я не смог быстро это увидеть и можно ли получить больше ваших начальных предметов, представляющих интерес, через другую строку запроса.

Более подробная информация, например, об использовании функции TIME_SERIES_DAILY_ADJUSTED в URL-вызове

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

Здесь вы получите следующее:

image

Вы можете проанализировать ответ JSON, используя JSON-анализатор, такой как JSONConverter.bas , а также есть варианты для загрузки в csv.

* Стоит немного изучить, какие API обеспечивают наибольшее покрытие ваших товаров.Похоже, что Alpha Vantage не покрывает столько, сколько извлекает мой код выше.

...