Всем привет. Так как нужны парсеры, собственно начал изучать эту тему. Ну с простыми запросами проблем нет, делаешь запрос - разбираешь ответ. Но сейчас многие сайты (если не большинство) работают с 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
Есть 2 варианта, для таких сайтов: 1) использовать для загрузки страницы браузер IE (там скрипты отрабатывают, выводя данные) 2) в браузере смотреть, какие запросы отправляются скриптами Например, для вашего сайта, скрипт выполняет запрос на адреса типа https://5ka.ru/special_offers/?records_per_page=15&page=2 где меняется значение page=XXX
Ну вот это я как раз и хочу понять. Не пойму только куда смотреть то. Открываю в Chrome Developer Tools но где эти запросы то смотреть? Может подскажете?
Ух ты, спасибо огромное. Нашел всё, заработало. Ответ получен. Посредством 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
А что надо сделать с кодом? и зачем? (если код работает)
PS: в моём парсере есть функционал преобразования JSON в XML а из XML достать нужный параметр уже легко (итого, любое значение из JSON извлекается в 2 действия) Раз нужны разные парсеры, - имеет смысл посмотреть готовые решения, нежели писать подобный код для каждого сайта
Да ничего особо. Хотелось бы чтобы знающий человек посмотрел своим взглядом, указал на ошибки или что нужно/можно поправить. Да, код работает. Хотя вообще то я бы хотел понять почему JsonConverter не "схавал" весь ответ...
Цитата
Игорь написал: имеет смысл посмотреть готовые решения, нежели писать подобный код для каждого сайта
Я бы не сказал что за 25-30 тысяч рублей имеет смысл покупать Ваш парсер, когда можно самому написать или заказать разработчику, который сделает в 15 раз дешевле! Когда мне нужны парсеры я их заказываю и оплачиваю, сейчас я пытаюсь разобраться в теме сам.
Игорь, Вы Ваш парсер, который по Вашим словам всеядный, чего в принципе быть не может, продавали всегда за 25-30к. Когда я к Вам обращался, года 2 назад примерно, Вы мне именно эту цену и называли! А я потом нашел разработчика, который как раз и сделал мне парсеры за 3000... Не знаю уж когда у Вас там так резко цена спустилась и почему, но факт остается фактом. К тому же мне нужен открытый код для интеграции в мои программы, а Вы, простите, ТОЛЬКО С ЗАКРЫТЫМ КОДОМ продаете, так как это Ваша интеллектуальная собственность! А сейчас вообще уже вообще без надобности так как сам изучаю этот вопрос...