Вытащить определенные ячейки таблицы из Morningstar, а затем перейти к следующей странице Morningstar - PullRequest
1 голос
/ 01 июля 2019

В настоящее время я пытаюсь собрать определенные фрагменты данных из таблицы на Morningstar, затем перевести их на следующий тикер и повторять до тех пор, пока тикеров больше не будет.

В настоящее время он вытянет всю строку «ранг в категории» в таблице «Итоговый доход». Я просто пытаюсь потянуть 3 месяца, 6 месяцев, с начала года, 1 год, 3 года и 5 лет. Когда он завершит тянуть их, он перейдет к следующему тикеру, как определено в «Ячейках (p, 14)» в строке навигации.

есть. Он обнаруживает, что «LINKX» находится в ячейке 1, 14, поэтому он переходит к http://performance.morningstar.com/fund/performance-return.action?t=LINKX&region=usa&culture=en_US и извлекает все строки «Rank in Category» из таблицы «Trailing Total Returns». Я хочу, чтобы указанные объекты помещались в указанные ячейки, а затем переходили к следующему тикеру.

Я просмотрел многие из этих тем, используя Excel VBA. Я пытаюсь получить информацию о ключах со страницы определенного тикера, затем перейти к следующему тикеру и повторить.

Declare PtrSafe Function apiShowWindow Lib "user32" Alias "ShowWindow" _
        (ByVal hwnd As LongPtr, ByVal nCmdShow As LongPtr) As LongPtr
    Global Const SW_MAXIMIZE = 3
    Global Const SW_SHOWNORMAL = 1
    Global Const SW_SHOWMINIMIZED = 2

Sub LinkedInWebScrapeScript()

    Dim objIE As InternetExplorer

    Dim html As HTMLDocument

    Set objIE = GetObject("new:{D5E8041D-920F-45e9-B8FB-B1DEB82C6E5E}")
    objIE.Visible = 1
Dim p As Integer
p = 3

    objIE.navigate ("http://performance.morningstar.com/fund/performance-return.action?t=" & Cells(p, 14) & "&region=usa&culture=en_US")
    Application.Wait Now + #12:00:02 AM#

    While objIE.Busy
        DoEvents
    Wend
    apiShowWindow objIE.hwnd, SW_MAXIMIZE

    For i = 1 To 2
        objIE.document.parentWindow.scrollBy 0, 100000 & i
        Application.Wait Now + #12:00:01 AM#
    Next i

Dim TDelements As IHTMLElementCollection
Dim htmldoc As MSHTML.IHTMLDocument 'Document object
Dim eleColtr As MSHTML.IHTMLElementCollection 'Element collection for tr tags
Dim eleColtd As MSHTML.IHTMLElementCollection 'Element collection for td tags
Dim eleColtd1 As MSHTML.IHTMLElementCollection
Dim eleRow As MSHTML.IHTMLElement 'Row elements
Dim eleCol As MSHTML.IHTMLElement 'Column elements
Set htmldoc = objIE.document 'Document webpage
Set eleColtr = htmldoc.getElementsByTagName("tr") 'Find all tr tags
Set TDelements = htmldoc.getElementsByTagName("table")
'This section populates Excel
i = 0 'start with first value in tr collection


Set eleColtd = htmldoc.getElementsByClassName("r_table3 width955px print97")(0).getElementsByClassName("last")(0).getElementsByClassName("row_data divide") 'get all the td elements in that specific tr

    For Each eleCol In eleColtd 'for each element in the td collection
        Sheets("Sheet2").Range("A1").Offset(i, j).Value = eleCol.innerText 'paste the inner text of the td element, and offset at the same time
        j = j + 1 'move to next element in td collection
    Next eleCol 'rinse and repeat
i = i + 1

p = p + 1

objIE.navigate ("http://performance.morningstar.com/fund/performance-return.action?t=" & Cells(p, 14) & "&region=usa&culture=en_US")

Set eleColtd = htmldoc.getElementsByClassName("r_table3 width955px print97")(0).getElementsByClassName("last")(0).getElementsByClassName("row_data divide") 'get all the td elements in that specific tr

    For Each eleCol In eleColtd 'for each element in the td collection
        Sheets("Sheet2").Range("A1").Offset(i, j).Value = eleCol.innerText 'paste the inner text of the td element, and offset at the same time
        z = z + 1
        j = j + 1 'move to next element in td collection
    Next eleCol 'rinse and repeat


End Sub

Он вытянет всю строку «ранг в категории» в таблице Trailing Total Returns. Я просто пытаюсь потянуть 3 месяца, 6 месяцев, с начала года, 1 год, 3 года и 5 лет. Когда он завершит тянуть их, он перейдет к следующему тикеру, как определено в «Ячейках (p, 14)» в строке навигации.

1 Ответ

2 голосов
/ 01 июля 2019

Ниже показан цикл и порядок выбора соответствующей таблицы, затем ячеек таблицы с помощью css селекторов .Тикеры считываются в массив из столбца N, начиная со строки 1. Предполагается, что в диапазоне нет пустых ячеек (хотя можно добавить тест, чтобы быть уверенным).

В массиве есть цикл,который содержит каждый тикер, а заполнитель TICKER в URL-адресе заменяется текущим значением тикера.

На вкладке ежемесячного отображения есть строка, которую нужно щелкнуть.

Соответствующая строка определяется с помощью

Set rankings = .querySelectorAll("#tab-month-end-content .last td")

#tab-month-end-content - селектор идентификатора, который получает правую вкладку, затем .last - селектор класса для имени класса последнего tbody (который last), затем td используется для указания дочерних td ячеек в этом теле.


Селекторы CSS:

Современные браузеры оптимизированы для CSS.Селекторы CSS - это быстрый способ сопоставления элементов в HTML-документе.Селекторы Css применяются с помощью методов querySelector или querySelectorAll;в этом случае HTMLDocument (т.е. документ).querySelector возвращает один узел: первое совпадение для селектора css;querySelectorAll возвращает нодлист из всех совпадающих элементов - затем вы индексируете этот нодлист для получения определенных элементов, например, вторая ячейка td имеет индекс 1.

Глядя на указанный нами шаблон:

#tab-month-end-content .last td

Первая часть - это селектор идентификатора , #, который выбирает элемент по идентификатору

#tab-month-end-content

При применении к странице это возвращает два совпадения, и мы хотим, чтобы вторая

Нажмите на изображение, чтобы увеличить его

enter image description here

Следующая часть

.last 

- селектор класса , . для имени класса last.При этом выбирается дочерний элемент тега tbody, показанный на рисунке выше.Так как только у второго элемента, совпадающего с идентификатором, есть этот дочерний элемент, мы теперь работаем с правым родительским элементом, чтобы продолжить и выбрать элементы типа td, используя селектор типа

td

Пробел, , между каждой частью, описанной выше, известны как потомки-комбинаторы , и они указывают, что элементы, соответствующие второму селектору, выбираются, если они имеют элемент-предок, соответствующий первому селектору, т.е.Слева - родительский элемент элементов, соответствующих селектору, найденных смежным селектором CSS справа.

Мы можем увидеть это на следующем изображении:

Нажмите на изображениеувеличить

enter image description here


VBA:

Option Explicit
Public Sub GetData()
    Dim ie As Object, tickers(), ws As Worksheet, lastRow As Long
    Dim results(), headers(), r As Long, i As Long, url As String

    headers = Array("ticker", "3m", "6m", "ytd", "1y", "3y", "6y")
    Set ws = ThisWorkbook.Worksheets("Sheet1")
    tickers = Application.Transpose(ws.Range("N1:N" & GetLastRow(ws, 14)).Value)
    ReDim results(1 To UBound(tickers), 1 To UBound(headers) + 1)
    Set ie = GetObject("new:{D5E8041D-920F-45e9-B8FB-B1DEB82C6E5E}")
    With ie
        .Visible = True
        For i = LBound(tickers) To UBound(tickers)
            r = r + 1
            url = Replace$("http://performance.morningstar.com/fund/performance-return.action?t=TICKER&region=usa&culture=en_US", "TICKER", tickers(i))
            .Navigate2 url

            While .Busy Or .readyState < 4: DoEvents: Wend

            .document.querySelector("[tabname='#tabmonth']").Click

            Dim rankings As Object
            Do
            Loop While .document.querySelectorAll("#tab-month-end-content .last td").Length = 0 'could add timed loop here

            With .document
                Set rankings = .querySelectorAll("#tab-month-end-content .last td")
                On Error Resume Next
                results(r, 1) = tickers(i)
                results(r, 2) = rankings.item(1).innerText
                results(r, 3) = rankings.item(2).innerText
                results(r, 4) = rankings.item(3).innerText
                results(r, 5) = rankings.item(4).innerText
                results(r, 6) = rankings.item(5).innerText
                results(r, 7) = rankings.item(6).innerText
                On Error GoTo 0
            End With
            Set rankings = Nothing
        Next
        ws.Cells(1, 15).Resize(UBound(results, 1), UBound(results, 2)) = results
        .Quit
    End With
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

Как уже упоминалось @SIM, вы можете использовать xmlhttp и избегать браузера, хотя и не уверены в настройках безопасности, нужен ли белый список сайтов.Вам необходимо выяснить, является ли заполнитель действительным в URL здесь: XNAS:TICKER.Префикс XNAS может варьироваться в зависимости от ваших тикеров, и в этом случае вам потребуется соответствующая строка, включая префикс в столбце N, а затем заменить расширенный заполнитель на этот, например ..... =PLACEHOLDER&region .......

Option Explicit
Public Sub GetData()
    Dim tickers(), ws As Worksheet, lastRow As Long
    Dim results(), headers(), r As Long, i As Long, url As String, html As HTMLDocument
    Set html = New HTMLDocument 'vbe > tools > references > Microsoft HTML Object Library

    headers = Array("ticker", "3m", "6m", "ytd", "1y", "3y", "6y")
    Set ws = ThisWorkbook.Worksheets("Sheet1")
    tickers = Application.Transpose(ws.Range("N1:N" & GetLastRow(ws, 14)).Value)
    ReDim results(1 To UBound(tickers), 1 To UBound(headers) + 1)

    With CreateObject("MSXML2.XMLHTTP")

        For i = LBound(tickers) To UBound(tickers)
            r = r + 1
            url = Replace$("http://performance.morningstar.com/perform/Performance/fund/trailing-total-returns.action?&t=XNAS:TICKER&region=usa&culture=en-US&cur=&ops=clear&s=0P0000J533&ndec=2&ep=true&align=m&annlz=true&comparisonRemove=false&loccat=&taxadj=&benchmarkSecId=&benchmarktype=", "TICKER", tickers(i))
           .Open "GET", url, False
           .setRequestHeader "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"
           .setRequestHeader "DNT", "1"
           .send
           html.body.innerHTML = .responseText

            Dim rankings As Object
            With html
                Set rankings = .querySelectorAll(".last td")

                On Error Resume Next
                results(r, 1) = tickers(i)
                results(r, 2) = rankings.item(1).innerText
                results(r, 3) = rankings.item(2).innerText
                results(r, 4) = rankings.item(3).innerText
                results(r, 5) = rankings.item(4).innerText
                results(r, 6) = rankings.item(5).innerText
                results(r, 7) = rankings.item(6).innerText
                On Error GoTo 0
            End With
            Set rankings = Nothing
        Next
        ws.Cells(1, 15).Resize(UBound(results, 1), UBound(results, 2)) = results
    End With
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
...