Скребок выдает ошибки вместо выхода из браузера, когда все сделано

Я написал скребок для разбора информации о фильмах с торрент-сайта. Я использовал IE и queryselector.

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

Вот полный код:

Sub Torrent_Data()
    Dim IE As New InternetExplorer, html As HTMLDocument
    Dim post As Object

    With IE
        .Visible = False
        .navigate "https://yts.am/browse-movies"
        Do While .readyState <> READYSTATE_COMPLETE: Loop
        Set html = .Document
    End With

    For Each post In html.querySelectorAll(".browse-movie-bottom")
        Row = Row + 1: Cells(Row, 1) = post.queryselector(".browse-movie-title").innerText
        Cells(Row, 2) = post.queryselector(".browse-movie-year").innerText
    Next post
    IE.Quit
End Sub

Я загрузил два изображения, чтобы показать ошибки.

First error

Second error

Обе ошибки появляются одновременно.

Я использую Internet Explorer 11.

Если я попытаюсь, как показано ниже, это принесет результаты успешно, без проблем.

Sub Torrent_Data()
    Dim IE As New InternetExplorer, html As HTMLDocument
    Dim post As Object

    With IE
        .Visible = False
        .navigate "https://yts.am/browse-movies"
        Do While .readyState <> READYSTATE_COMPLETE: Loop
        Set html = .Document
    End With

    For Each post In html.getElementsByClassName("browse-movie-bottom")
        Row = Row + 1: Cells(Row, 1) = post.queryselector(".browse-movie-title").innerText
        Cells(Row, 2) = post.queryselector(".browse-movie-year").innerText
    Next post
    IE.Quit
End Sub

Ссылки добавлены в библиотеку:

  1. Microsoft Internet Controls
  2. Microsoft HTML Object Library

Есть ли какие-либо ссылки для добавления в библиотеку, чтобы избавиться от ошибок?

Ответ 1

Хорошо, поэтому на этой веб-странице есть что-то серьезно недружелюбное. Это продолжало рушиться для меня. Поэтому я прибегал к запуску javascript-программы в скриптовом движке/скриптовом управлении, и он работает.

Надеюсь, ты сможешь следовать за ним. Логика находится в javascript, добавленном в ScriptEngine. Я получаю два списка узлов, один список фильмов и один список лет; затем я перехожу через каждый массив в синхронизации и добавляю их в качестве пары ключевых значений в словарь сценариев Microsoft.

Option Explicit

'*Tools->References
'*    Microsoft Scripting Runtime
'*    Microsoft Scripting Control
'*    Microsoft Internet Controls
'*    Microsoft HTML Object Library

Sub Torrent_Data()
    Dim row As Long
    Dim IE As New InternetExplorer, html As HTMLDocument
    Dim post As Object

    With IE
        .Visible = True
        .navigate "https://yts.am/browse-movies"
        Do While .readyState <> READYSTATE_COMPLETE:
            DoEvents
        Loop
        Set html = .document
    End With

    Dim dicFilms As Scripting.Dictionary
    Set dicFilms = New Scripting.Dictionary

    Call GetScriptEngine.Run("getMovies", html, dicFilms)

    Dim vFilms As Variant
    vFilms = dicFilms.Keys

    Dim vYears As Variant
    vYears = dicFilms.Items

    Dim lRowLoop As Long
    For lRowLoop = 0 To dicFilms.Count - 1

        Cells(lRowLoop + 1, 1) = vFilms(lRowLoop)
        Cells(lRowLoop + 1, 2) = vYears(lRowLoop)

    Next lRowLoop

    Stop

    IE.Quit
End Sub

Private Function GetScriptEngine() As ScriptControl
    '* see code from this SO Q & A
    ' https://stackoverflow.com/questions/37711073/in-excel-vba-on-windows-how-to-get-stringified-json-respresentation-instead-of
    Static soScriptEngine As ScriptControl
    If soScriptEngine Is Nothing Then
        Set soScriptEngine = New ScriptControl
        soScriptEngine.Language = "JScript"

        soScriptEngine.AddCode "function getMovies(htmlDocument, microsoftDict) { " & _
                                    "var titles = htmlDocument.querySelectorAll('a.browse-movie-title'), i;" & _
                                    "var years = htmlDocument.querySelectorAll('div.browse-movie-year'), j;" & _
                                    "if ( years.length === years.length) {" & _
                                    "for (i=0; i< years.length; ++i) {" & _
                                    "   var film = titles[i].innerText;" & _
                                    "   var year = years[i].innerText;" & _
                                    "   microsoftDict.Add(film, year);" & _
                                    "}}}"

    End If
    Set GetScriptEngine = soScriptEngine
End Function

Ответ 2

На веб-сайте есть API. Проверьте, например, результат из URL https://yts.am/api/v2/list_movies.json?page=1&limit=50, который фактически представляет 50 фильмов с первой страницы последней категории фильмов в формате JSON.

Взгляните на приведенный ниже пример. Импортируйте модуль JSON.bas в проект VBA для обработки JSON.

Option Explicit

Sub Test()

    Dim sJSONString As String
    Dim vJSON
    Dim sState As String
    Dim lPage As Long
    Dim aRes()
    Dim i As Long
    Dim aData()
    Dim aHeader()

    With Sheets(1)
        .Cells.Delete
        .Cells.WrapText = False
    End With
    lPage = 1
    aRes = Array()
    Do
        With CreateObject("MSXML2.XMLHTTP")
            .Open "GET", "https://yts.am/api/v2/list_movies.json?page=" & lPage & "&limit=50", False
            .send
            sJSONString = .responseText
        End With
        JSON.Parse sJSONString, vJSON, sState
        If Not vJSON("data").Exists("movies") Then Exit Do
        vJSON = vJSON("data")("movies")
        ReDim Preserve aRes(UBound(aRes) + UBound(vJSON) + 1)
        For i = 0 To UBound(vJSON)
            Set aRes(UBound(aRes) - UBound(vJSON) + i) = vJSON(i)
        Next
        lPage = lPage + 1
        Debug.Print "Parsed " & (UBound(aRes) + 1)
        DoEvents
    Loop
    JSON.ToArray aRes, aData, aHeader
    With Sheets(1)
        OutputArray .Cells(1, 1), aHeader
        Output2DArray .Cells(2, 1), aData
        .Columns.AutoFit
    End With
    MsgBox "Completed"

End Sub

Sub OutputArray(oDstRng As Range, aCells As Variant)

    With oDstRng
        .Parent.Select
        With .Resize(1, UBound(aCells) - LBound(aCells) + 1)
            .NumberFormat = "@"
            .Value = aCells
        End With
    End With

End Sub

Sub Output2DArray(oDstRng As Range, aCells As Variant)

    With oDstRng
        .Parent.Select
        With .Resize( _
                UBound(aCells, 1) - LBound(aCells, 1) + 1, _
                UBound(aCells, 2) - LBound(aCells, 2) + 1)
            .NumberFormat = "@"
            .Value = aCells
        End With
    End With

End Sub

Выход для меня следующим образом, на данный момент есть 7182 фильмов всего:

output

BTW, аналогичный подход применяется в следующих ответах: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 и 15.

Ответ 3

Ну, похоже, я нашел решение для работы с .queryselectorAll(). После много экспериментов я заметил, что у него есть только некоторые проблемы с for loop, поэтому я тактично избегал for loop и вместо этого использовал with block для выполнения той же работы. Вот как мы можем достичь этого:

Sub Torrent_Data()

    With CreateObject("InternetExplorer.Application")
        .Visible = False
        .navigate "https://yts.am/browse-movies"
        While .Busy = True Or .readyState < 4: DoEvents: Wend

        With .document.querySelectorAll(".browse-movie-bottom")
            For I = 0 To .Length - 1
                Cells(I + 1, 1) = .Item(I).querySelector(".browse-movie-title").innerText
                Cells(I + 1, 2) = .Item(I).querySelector(".browse-movie-year").innerText
            Next I
        End With
    End With

End Sub

Btw, вышеуказанный script может быть выполнен без ссылки на что-либо в библиотеке.