Страницы: 1
RSS
Запросы к серверу, обработка js ответа
 
Всем привет.
Так как нужны парсеры, собственно начал изучать эту тему.
Ну с простыми запросами проблем нет, делаешь запрос - разбираешь ответ.
Но сейчас многие сайты (если не большинство) работают с js. Мой старый разработчик писал мне парсеры к сайтам, ответ которых парсился как раз через JsonConverter. Но даже разобрав его парсеры я не смог понять как именно посылать запрос к серверу чтобы он вернул текст для разбора.
Подскажите, пожалуйста, кто разбирается в теме куда копать, что почитать? Может строчку где искать в Source соде страницы нужную?
В идеале хотелось бы не циклом запросы делать а парсить страницу https://5ka.ru/special_offers/
Для тестов создал файл, который парсит продукты магазина. Сразу скажу что сделал я его очень костыльно ибо запросы находятся в цикле котрый идет по номерам товаров каталога, так что там особо заморачиваться с разбором ответа не стал, написал так чтобы было нормально.
Буду благодарен любому совету.
Прикладываю файл и текущий код.
Код
Option Explicit
Const NF As String = "NOT FOUND"
Sub StartPars()
    Const StURL As String = "https://5ka.ru/special_offers/"
    Dim ReqRes$, tmpName$
    Dim MyRequest
    Dim x&, lr&
    Dim r As Range
        If Not IsEmpty([A2]) Then Range("A2").Resize(Cells(Rows.Count, 1).End(xlUp).Row, 3).ClearContents
        Set MyRequest = CreateObject("WinHttp.WinHttpRequest.5.1")
            For x = 616 To 617 '17000
                MyRequest.Open "GET", StURL & x & "/"
                MyRequest.send
                ReqRes = MyRequest.responsetext
                lr = Cells(Rows.Count, 1).End(xlUp).Row + 1
                tmpName = ClearResp(GetParsInfo(ReqRes, "<a href=""/special_offers/" & x & "/"">", "</a>"))
                    If Not tmpName = NF Then
                        Set r = Cells(lr, 1).Offset()
                        r(, 1) = tmpName
                        r(, 2) = GetParsInfo(ReqRes, "<span data-price=""", """ class=""cost")
                        r(, 3) = ClearResp(GetParsInfo(ReqRes, "<span class=""cost__brake"">", "</sup></span>"), "<sup>", True)
                        r(, 4) = x
                        If IsNumeric(r(, 2)) And IsNumeric(r(, 3)) Then r(, 5) = r(, 2) / r(, 3)
                    End If
                ReqRes = ""
            Next
End Sub
Function GetParsInfo(ParseResponse As String, StartStr As String, EndStr As String) As String
    Dim st&, et&
        On Error GoTo errH
        st = Application.WorksheetFunction.Search(StartStr, ParseResponse) + Len(StartStr)
        et = Application.WorksheetFunction.Search(EndStr, Mid(ParseResponse, st, Len(ParseResponse) - st))
        GetParsInfo = Mid(ParseResponse, st, et - 1)
        Exit Function
errH:
        GetParsInfo = NF
End Function
Function ClearResp(ResponseStr As String, Optional delim As String = """, Optional ReplD As Boolean = False) As String
    Dim NewStr$, tmpRepl$
    Dim x&
    Dim tmpStr As Variant
        On Error GoTo errH
        If ReplD Then tmpRepl = "." Else tmpRepl = ""
        For Each tmpStr In Split(ResponseStr, delim)
            x = x + 1
            If x > 1 Then NewStr = NewStr & tmpRepl & tmpStr Else NewStr = NewStr & tmpStr
        Next
        ClearResp = NewStr
        Exit Function
errH:
        ClearResp = ResponseStr
End Function
Изменено: andrey062006 - 29.11.2017 17:12:35
 
Есть 2 варианта, для таких сайтов:
1) использовать для загрузки страницы браузер IE (там скрипты отрабатывают, выводя данные)
2) в браузере смотреть, какие запросы отправляются скриптами
Например, для вашего сайта, скрипт выполняет запрос на адреса типа
https://5ka.ru/special_offers/?records_per_page=15&page=2
где меняется значение page=XXX
 
Цитата
Игорь написал:
1) использовать для загрузки страницы браузер IE (там скрипты отрабатывают, выводя данные)
Не вариант

Цитата
Игорь написал:
2) в браузере смотреть, какие запросы отправляются скриптами
Например, для вашего сайта, скрипт выполняет запрос на адреса типа
https://5ka.ru/special_offers/?records_per_page=15&page=2
где меняется значение page=XXX
Ну вот это я как раз и хочу понять. Не пойму только куда смотреть то. Открываю в Chrome Developer Tools но где эти запросы то смотреть?
Может подскажете?
 
Цитата
Chrome Developer Tools
все верно
вкладка NETWORK (если там есть записи - нажимаем кнопку очистки списка)
и тыкаем на странице кнопку БОЛЬШЕ АКЦИЙ

первая же строка на вкладке Network - запрос на этот адрес
 
Ух ты, спасибо огромное. Нашел всё, заработало. Ответ получен.
Посредством http://json.parser.online.fr/ разобран правильно! С JsonConverter пока не очень подружил. Но как разберусь и подружу их - обязательно отпишусь!)
Огромное спасибо за подсказку!
 
Еще раз всем привет!)
Ну в общем и целом я разобрался с запросами и парсингом. Запросы - это легко, а вот сам парсинг - много возни.
С JsonConverter я его не смог подружить (почему то он берет только первые 4 значения, а вложенные уровни не парсит). Долго возился, пробовал, настраивал, но он так и не смог разобрать весь ответ, поэтому пришлось писать собственные функции обработки, кроме "DateFromUnix" (её взял из интернета).
Зато теперь знаю куда смотреть и где искать строчку, куда отправлять запрос)
Прикладываю код и файл ниже.
STk - это назначение переменных для разбора в функциях. Может, конечно, у меня руки кривые, но почему то не смог без Char подсунуть ему текстовую строку с символами...
Хотелось бы услышать критику, замечания и может кто то подскажет что можно сделать по другому)
Может можно как то более изящно сделать?)
Код
Option Explicit
Const NF As String = "NOT FOUND"
Option Base 1
Dim MyRequest As MSXML2.XMLHTTP60
Dim r1$, r2$, r3$, r4$, r5$, r6$, r7$, r8$
Dim NextURL
Dim ItemsCount&
Sub STk()
    r1 = Chr(34) '"
    r2 = Chr(58) ':
    r3 = Chr(123) '{
    r4 = Chr(125) '}
    r5 = Chr(91) '[
    r6 = Chr(93) ']
    r7 = Chr(33) 'space
    r8 = Chr(44) ',
End Sub
Sub StartPars()
    Const MassLC As Integer = 30
    Const StURL As String = "https://5ka.ru/api/special_offers/?records_per_page=15&page=1"
    Dim MyRequest
    Dim mass()
    Dim x&, y&, z&, mC&, rC&
    Dim tmpCol As Collection
    Dim tmpCol2 As Collection
    Dim VAL As Variant
    Dim FirstLoop As Boolean
    Dim isDate As Boolean
        Set MyRequest = New MSXML2.XMLHTTP60
        STk
        NextURL = StURL
        If Not IsEmpty([A1]) Then Range("A1").Resize(Cells(Rows.Count, 1).End(xlUp).Row, Cells(1, Columns.Count).End(xlToLeft).Column).ClearContents
        rC = 1
        FirstLoop = True
        isDate = False
        With MyRequest
            Do
                .Open "GET", NextURL
                .SetRequestHeader "Host", "5ka.ru"
                .SetRequestHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"
                .SetRequestHeader "X-Requested-With", "XMLHttpRequest"
                .send
                    Set tmpCol = GetParsCol(.ResponseText)
                    If FirstLoop Then ReDim mass(ItemsCount + 10, MassLC): FirstLoop = False
                        For x = 1 To tmpCol.Count
                            rC = rC + 1
                            Set tmpCol2 = ReplVal(tmpCol.Item(x))
                                For y = 1 To tmpCol2.Count
                                    mC = 0
                                        For Each VAL In Split(tmpCol2.Item(y), ":")
                                            mC = mC + 1
                                                Select Case mC
                                                    Case 1
                                                        For z = 1 To MassLC
                                                            isDate = Not IsError(Application.Search("date", VAL))
                                                            If mass(1, z) = VAL Then Exit For
                                                            If IsEmpty(mass(1, z)) Then mass(1, z) = VAL: Exit For
                                                        Next
                                                    Case 2: If isDate Then mass(rC, z) = DateFromUnix(VAL) Else mass(rC, z) = VAL
                                                End Select
                                        Next
                                Next
                        Next
            Loop While NextURL <> "null"
            Range("A1").Resize(UBound(mass), MassLC) = mass
        End With
End Sub
Function ReplVal(vStr As String) As Collection
    Dim VAL
    Dim kk As New Collection
    Dim kk2 As New Collection
    Dim rVal$
    Dim KeyW
    Dim mC%
    Dim x&, y&, z&
    Dim ANC As Boolean
        kk2.Add Chr(34) '"
        kk2.Add Chr(123) '{
        kk2.Add Chr(125) '}
        kk2.Add Chr(91) '[
        kk2.Add Chr(93) ']
    On Error Resume Next
            For Each VAL In Split(vStr, r8 & r1)
                rVal = VAL
                For x = 1 To kk2.Count: rVal = Replace(rVal, kk2.Item(x), ""): Next
                kk.Add rVal
            Next
            Set kk2 = Nothing
                kk2.Add "image_small"
                kk2.Add "image_big"
                kk2.Add "date_start"
                kk2.Add "date_end"
                kk2.Add "special_price"
                kk2.Add "regular_price"
                kk2.Add "discount_percent"
                kk2.Add "id"
                kk2.Add "name"
            z = kk.Count
                For x = 1 To z
                    ANC = False
                    mC = 0
                        For Each VAL In Split(kk.Item(x), r2)
                            mC = mC + 1
                                For y = 1 To kk2.Count
                                    If mC = 1 Then
                                        If VAL = kk2.Item(y) Then ANC = True: Exit For
                                    End If
                                Next
                            If mC = 1 And Not ANC Then kk.Remove (x): x = x - 1: z = z - 1: Exit For
                            If mC = 1 And ANC Then Exit For
                        Next
                Next
            Set ReplVal = kk
End Function
Function DateFromUnix(s As Variant) As Date
    Dim d As Double
        If (Len(s) = 13) Then
            d = CDbl(Left(s, 10)) 'javaScript Time
            If Int(Mid(s, 11, 3) >= 500) Then d = d + 1 'may need to round for milliseconds
        ElseIf (Len(s) = 10) Then
            d = CDbl(s) 'unix Time
        Else
            DateFromUnix = CVErr(xlErrValue) 'wtf time
            Exit Function
        End If
        DateFromUnix = DateAdd("s", d, DateSerial(1970, 1, 1))
End Function
Function GetParsCol(ParseResponse As String) As Collection
    Dim d$
    Dim st&, et&, cc&, mC&
    Dim tmpCol As New Collection
    Dim VAL, VAL2
    Dim FirstLoop As Boolean
    Dim isFnd As Boolean
        d = r3 & r1 & "shopitem_categories" & r1 & r2 & r5 '& r6'& r8'{"shopitem_categories":[],
        FirstLoop = True
        isFnd = False
    On Error GoTo errH
            Do
                st = Application.WorksheetFunction.Search(d, ParseResponse, 1)
                    If FirstLoop Then
                        FirstLoop = False
                            For Each VAL In Split(Left(ParseResponse, st), ",")
                                mC = 0
                                    For Each VAL2 In Split(Left(VAL, st), r1 & r2)
                                        mC = mC + 1
                                            Select Case VAL2
                                                Case r3 & r1 & "count": cc = 1
                                                Case r1 & "next": cc = 2
                                            End Select
                                        If mC = 2 Then
                                            Select Case cc
                                                Case 1: ItemsCount = CDbl(Replace(VAL2, r1, ""))
                                                Case 2: NextURL = Replace(VAL2, r1, ""): isFnd = True: Exit For
                                            End Select
                                        End If
                                    Next
                                If isFnd Then Exit For
                            Next
                    End If
                ParseResponse = Right(ParseResponse, Len(ParseResponse) - st - Len(d) + 1)
                et = Application.WorksheetFunction.Search(d, ParseResponse, 1)
                tmpCol.Add Left(ParseResponse, et - 2)
            Loop While st > 0
        Exit Function
errH:
        Set GetParsCol = tmpCol
End Function
Изменено: andrey062006 - 30.11.2017 18:54:51
 
А что надо сделать с кодом? и зачем? (если код работает)

PS: в моём парсере есть функционал преобразования JSON в XML
а из XML достать нужный параметр уже легко
(итого, любое значение из JSON извлекается в 2 действия)
Раз нужны разные парсеры, - имеет смысл посмотреть готовые решения, нежели писать подобный код для каждого сайта
 
Цитата
Игорь написал:
А что надо сделать с кодом?
Да ничего особо. Хотелось бы чтобы знающий человек посмотрел своим взглядом, указал на ошибки или что нужно/можно поправить.
Да, код работает.
Хотя вообще то я бы хотел понять почему JsonConverter не "схавал" весь ответ...
Цитата
Игорь написал:
имеет смысл посмотреть готовые решения, нежели писать подобный код для каждого сайта
Я бы не сказал что за 25-30 тысяч рублей имеет смысл покупать Ваш парсер, когда можно самому написать или заказать разработчику, который сделает в 15 раз дешевле!
Когда мне нужны парсеры я их заказываю и оплачиваю, сейчас я пытаюсь разобраться в теме сам.
Изменено: andrey062006 - 30.11.2017 19:35:44
 
за 25-30 тр - да, не нужно.
а вот купить один раз за 2500, и потом самостоятельно быстро настраивать, - не самый плохой вариант
 
Игорь, Вы Ваш парсер, который по Вашим словам всеядный, чего в принципе быть не может, продавали всегда за 25-30к. Когда я к Вам обращался, года 2 назад примерно, Вы мне именно эту цену и называли! А я потом нашел разработчика, который как раз и сделал мне парсеры за 3000... Не знаю уж когда у Вас там так резко цена спустилась и почему, но факт остается фактом.
К тому же мне нужен открытый код для интеграции в мои программы, а Вы, простите, ТОЛЬКО С ЗАКРЫТЫМ КОДОМ продаете, так как это Ваша интеллектуальная собственность!
А сейчас вообще уже вообще без надобности так как сам изучаю этот вопрос...
Страницы: 1
Читают тему
Наверх