Web Scraping от TagName - PullRequest
       27

Web Scraping от TagName

1 голос
/ 06 апреля 2019

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

Class="container size" - 5*5,5*10 kind of value
Class="description" - Standard in this case also need to copy like Drive-up Access
Class="offer1" & "offer2" - Call for Availability
Class="price"

Я пытался создать код, но не могу судить, какое именно имя тега нужно выбрать, ниже приведен мой код, пожалуйста, помогите мне скопировать эти данные.

Dim ie As New InternetExplorer, ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
With ie
    .Visible = True
    .Navigate2 "" & Sheets("Home").Range("C3").Text

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

    Sheets("Unit Data").Select


    Dim listings As Object, listing As Object, headers(), results()
    Dim r As Long, list As Object, item As Object
    headers = Array("size", "features")
    Set list = .document.getElementsByClassName("units-table main")
    '.unit_size medium, .features, .promo_offers, .board_rate_wrapper p, .board_rate
    Dim rowCount As Long
    rowCount = .document.querySelectorAll(".units-table main li").Length


    ReDim results(1 To rowCount, 1 To UBound(headers) + 1)
    For Each listing In list
        For Each item In listing.getElementsByTagName("li")
            r = r + 1
            On Error Resume Next
            results(r, 1) = item.getElementsByClassName("container size")(0).innerText
            results(r, 2) = item.getElementsByClassName("description")(0).innerText

            On Error GoTo 0


        Next
    Next
    ws.Cells(1, 1).Resize(1, UBound(headers) + 1) = headers
    ws.Cells(2, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
    .Quit
End With

1 Ответ

2 голосов
/ 06 апреля 2019

XHR:

Вся информация доступна через XMLHTTP (XHR) запрос - намного быстрее, чем открытие браузера.

Сначала я получаю количество строк с помощью селектора css .main li[class]. "." - это селектор класса , li - селектор типа , а [class] - селектор атрибута . Пробел, " ", между ними - комбинатор-потомок . Это указывает, что я хочу получить все li элементы тега / типа с атрибутом класса, имеющим родителя, чье имя класса main.

Это соответствует следующему:

image

Как видите, это дает мне счетчик строк; количество родительских элементов li для получения информации для результирующего набора.

Эта коллекция li элементов возвращается в виде nodeList querySelectorAll. Я не могу зациклить этот список, применяя getElementsByClassName / querySelector к отдельным узлам, поскольку элементы li не предоставляют методов, которые я могу использовать.

Теперь, поскольку я не использую браузер, я вынужден полагаться на методы, доступные для HTMLDocument объекта. В отличие от браузера, у меня нет доступа к ограниченным селекторам псевдокласса , которые они поддерживают, при автоматизации через VBA, что позволило бы мне использовать синтаксис селектора, такой как :nth-of-type для доступа к отдельным строкам. Это раздражающее ограничение веб-скребка с VBA.

Итак, что мы можем сделать? Ну, в этом случае я могу записать innerHTML каждого узла в другую переменную HTMLDocument, html2, чтобы я мог получить доступ к методам querySelector/querySelectorAll этого объекта. HTML тогда будет ограничен только текущими li.

Если мы посмотрим на рассматриваемый HTML-код:

image

Мы можем видеть, что элементы li - это общие братья и сестры. Они сидят рядом друг с другом на одном уровне. Зацикливая свой nodeList listings, я передаю innerHTML из текущего узла в html2; моя вторая HTMLDocument переменная.

Стоит отметить, что я, вероятно, опустился в каждый список, используя children, например:

listings.item(i).Children(2)......

Тогда я мог бы разделиться на новые строки и т. Д., Чтобы получить доступ ко всей информации. Я думаю, что данный метод быстрее и надежнее.

VBA:

Option Explicit  
Public Sub GetInfo()
    Dim ws As Worksheet, html As HTMLDocument, s As String
    Const URL As String = "https://www.neighborhoodselfstorage.net/self-storage-delmar-md-f1426"

    Set ws = ThisWorkbook.Worksheets("Sheet1")
    Set html = New HTMLDocument
    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", URL, False
        .setRequestHeader "User-Agent", "Mozilla/5.0"
        .send
        s = .responseText
        html.body.innerHTML = s

        Dim headers(), results(), listings As Object, amenities As String

        headers = Array("Size", "Description", "Amenities", "Offer1", "Offer2", "RateType", "Price")
        Set listings = html.querySelectorAll(".main li[class]")

        Dim rowCount As Long, numColumns As Long, r As Long, c As Long
        Dim icons As Object, icon As Long, amenitiesInfo(), i As Long, item As Long

        rowCount = listings.Length
        numColumns = UBound(headers) + 1

        ReDim results(1 To rowCount, 1 To numColumns)
        Dim html2 As HTMLDocument
        Set html2 = New HTMLDocument
        For item = 0 To listings.Length - 1
            r = r + 1
            html2.body.innerHTML = listings.item(item).innerHTML
            'size,description, amenities,specials offer1 offer2, rate type, price

            results(r, 1) = Trim$(html2.querySelector(".size").innerText)
            results(r, 2) = Trim$(html2.querySelector(".description").innerText)
            Set icons = html2.querySelectorAll("i[title]")

            ReDim amenitiesInfo(0 To icons.Length - 1)

            For icon = 0 To icons.Length - 1
                amenitiesInfo(icon) = icons.item(icon).getAttribute("title")
            Next

            amenities = Join$(amenitiesInfo, ", ")

            results(r, 3) = amenities
            results(r, 4) = html2.querySelector(".offer1").innerText
            results(r, 5) = html2.querySelector(".offer2").innerText
            results(r, 6) = html2.querySelector(".rate-label").innerText
            results(r, 7) = html2.querySelector(".price").innerText
        Next

        ws.Cells(1, 1).Resize(1, UBound(headers) + 1) = headers
        ws.Cells(2, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
    End With
End Sub

Internet Explorer:

Предположим, не перенаправлено с данного URL. Здесь я использую: селектор псевдокласса nth-of-type для нацеливания на каждую строку списка. Эти строки являются элементами li (list), содержащими информацию для каждого списка блоков. Я создаю строку селектора CSS, которая определяет строку, а затем элемент в строке, которую я ищу. Я передаю эту строку в querySelector или querySelectorAll, которая возвращает совпадающий элемент / с.

Option Explicit

Public Sub UseIE()
    Dim ie As New InternetExplorerm, ws As Worksheet
    Const Url As String = "https://www.neighborhoodselfstorage.net/self-storage-delmar-md-f142"

    Set ws = ThisWorkbook.Worksheets("Sheet1")

    With ie
        .Visible = True
        .Navigate2 Url

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

        Dim headers(), results(), listings As Object, listing As Object, amenities As String

        headers = Array("Size", "Description", "Amenities", "Offer1", "Offer2", "RateType", "Price")

        Set listings = .document.querySelectorAll(".main li[class]")

        Dim rowCount As Long, numColumns As Long, r As Long, c As Long
        Dim icons As Object, icon As Long, amenitiesInfo(), i As Long

        rowCount = listings.Length
        numColumns = UBound(headers) + 1
        ReDim results(1 To rowCount, 1 To numColumns)
        For Each listing In listings
            r = r + 1
            'size,description, amenities,specials offer1 offer2, rate type, price
            With .document

                results(r, 1) = Trim$(.querySelector(".main li:nth-of-type(" & r & ") .size").innerText)
                results(r, 2) = Trim$(.querySelector(".main li:nth-of-type(" & r & ") .description").innerText)

                Set icons = .querySelectorAll("." & Join$(Split(listing.className, Chr$(32)), ".") & ":nth-of-type(" & r & ") i[title]")

                ReDim amenitiesInfo(0 To icons.Length - 1)

                For icon = 0 To icons.Length - 1
                    amenitiesInfo(icon) = icons.item(icon).getAttribute("title")
                Next

                amenities = Join$(amenitiesInfo, ",")
                results(r, 3) = amenities
                results(r, 4) = .querySelector(".main li:nth-of-type(" & r & ") .offer1").innerText
                results(r, 5) = .querySelector(".main li:nth-of-type(" & r & ") .offer2").innerText
                results(r, 6) = .querySelector(".main li:nth-of-type(" & r & ") .rate-label").innerText
                results(r, 7) = .querySelector(".main li:nth-of-type(" & r & ") .price").innerText
            End With
        Next
        .Quit
        ws.Cells(1, 1).Resize(1, UBound(headers) + 1) = headers
        ws.Cells(2, 1).Resize(UBound(results, 1), UBound(results, 2)) = results
    End With
End Sub


Ссылки (VBE> Инструменты> Ссылки):

  1. Библиотека объектов Microsoft HTML
  2. Microsoft Internet Controls

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...