Страницы: 1
RSS
Запросы к серверу, обработка js ответа
 
Всем привет.
Так как нужны парсеры, собственно начал изучать эту тему.
Ну с простыми запросами проблем нет, делаешь запрос - разбираешь ответ.
Но сейчас многие сайты (если не большинство) работают с js. Мой старый разработчик писал мне парсеры к сайтам, ответ которых парсился как раз через JsonConverter. Но даже разобрав его парсеры я не смог понять как именно посылать запрос к серверу чтобы он вернул текст для разбора.
Подскажите, пожалуйста, кто разбирается в теме куда копать, что почитать? Может строчку где искать в Source соде страницы нужную?
В идеале хотелось бы не циклом запросы делать а парсить страницу https://5ka.ru/special_offers/
Для тестов создал файл, который парсит продукты магазина. Сразу скажу что сделал я его очень костыльно ибо запросы находятся в цикле котрый идет по номерам товаров каталога, так что там особо заморачиваться с разбором ответа не стал, написал так чтобы было нормально.
Буду благодарен любому совету.
Прикладываю файл и текущий код.
Код
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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 подсунуть ему текстовую строку с символами...
Хотелось бы услышать критику, замечания и может кто то подскажет что можно сделать по другому)
Может можно как то более изящно сделать?)
Код
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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
    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
Читают тему
Наверх
Loading...