R в качестве инструмента мониторинга цен

R в качестве инструмента мониторинга цен

378
ПОДЕЛИТЬСЯ

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

Что из этого вышло — Создатель решил попрактиковаться в решении данной задачки и промониторить 1-го из больших ритейлеров электроники в Рф, чьим постоянным клиентом создатель является. Результаты употребляются для совсем разных целей — от конфигурации локальных политик ценообразования и ведения ассортимента до составления стратегических планов развития компании. Те, кто как то связан с продажами на конкурентноспособном рынке, наверное знают, что мониторинг соперников является принципиальной задачей.

Заместо введения
Также добавлю, что не будет и описания анализа мониторинга, лишь метод сбора и некие трудности, с которыми пришлось столкнуться во время работы. Все деяния носят демонстративных нрав, и приобретенные дынные не были переданы никому. Сходу стоит огласить, что в статье не будет описаний способов социальной инженерии либо общения с фирмами, предоставляющими сервисы мониторинга. Крайнее время создатель все почаще применяет R, и сбор данных решено было сделать с его внедрением. К тому же все огромную популярность набирают открытые данные (к примеру вот, вот либо вот) и навык работы с ними прямо из используемой среды будет полезен.

Анализ веб-сайта
Есть несколько уровней товарного классификатора, и количество уровней для хоть какого продукта постоянно идиентично. Начнем с контента. До перечня продуктов можно добраться через уровень 2 либо уровень 3. 1-ое, что необходимо сделать, изучить структуру веб-сайта исследуемого соперника. Для предстоящей работы было решено применять уровень 2.
Создатель не сообразил как программно показать последующие 30 продуктов, потому решено было изучить мобильную версию веб-сайта. Последующим шагом было исследование начального кода странички со перечнем требуемых продуктов и выяснение ее структуры. В мобильной версии внизу странички есть ссылки на последующую и на последнюю странички. Плюс — мобильная версия веб-сайта еще наименее «замусоренная» излишними ссылками и тэгами. Каждый продукт находится в отдельном HTML-контейнере. Здесь нас ожидает 1-ая сложность — в коде странички содержится информация лишь о первых 30 товарах в перечне — понятно, что нас это не устраивает.

Пишем код
Вначале решено было поделить код на несколько составляющих:
Функция возвращает data.frame содержащий информацию о наличии, наименовании, артикуле, количестве отзывов и ценах продуктов. 1. Функция, которая собирает информацию с определенной странички. Воспринимает на вход URL определенной странички, на которой находится до 15 продуктов (отличие мобильной версии).
1-ая функцияgetOnePageBooklet <- function(strURLsub="", curl=getCurlHandle()){
# loading the required page
html <- getURL(strURLsub,
.encoding=’UTF-8′,
curl=curl)
# parsing html
html.raw <- htmlTreeParse(
html,
useInternalNodes=T
)
# searching for SKU nodes
html.parse.SKU <- xpathApply(html.raw,
path="//section[@class=’b-product’]",
fun=xmlValue)

# some regex 🙂
noT <- gsub(‘ ([0-9]+)\s([0-9]+) ‘,’ \1\2 ‘,unlist(html.parse.SKU))
noT <- gsub(‘;’,’,’,noT)
noT <- gsub(‘rn’,’;’,noT)
noT <- trim(noT)
noT <- gsub("(\s;)+", " ", noT)
noT <- gsub("^;\s ", "", noT)
noT <- gsub(";\s+([0-9]+)\s+;", "\1", noT)
noT <- gsub(" ; ", "", noT)
noT <- gsub("Артикул ", "", noT)
noT <- gsub("\s+Приобрести;\s*", "", noT)
noT <- gsub("\s+руб.;\s*", "", noT)
noT <- gsub(";\s+", ";", noT)

# text to list
not.df <- strsplit(noT,’;’)

# list to nice df
tryCatch(
not.df <- as.data.frame(matrix(unlist(not.df),
nrow = length(not.df),
byrow = T)), error=function(e) {print(strURLsub)} )

}

Итог функции — data.frame со всеми продуктами данного уровня товарного классификатора. Функция, которая, используя функцию 1, собирает информацию со всех страничек данного уровня товарного классификатора. 2. Основной смысл данной функции — отыскать номер крайней странички, пробежаться от первой до крайней с помощью функции 1 и объединить приобретенные результаты в один data.frame.
2-ая функцияgetOneBooklet <- function(strURLmain="", curl=getCurlHandle()){
# data frane for the result
df <- data.frame(inStock=character(), SKU=character(), Article=numeric(), Comment=numeric(), Price=numeric())

# loading main subpage
html <- getURL(strURLmain,
.encoding=’UTF-8′,
curl=curl)
# parsing main subpage
html.raw <- htmlTreeParse(
html,
useInternalNodes=T
)
# finding last subpage
html.parse.pages <- xpathApply(html.raw,
path="//a[@class=’page g-nouline’]",
fun=xmlValue)
if(length(html.parse.pages)==0){
urlMax <- 1
}else{
urlMax <- as.numeric(unlist(html.parse.pages)[length(unlist(html.parse.pages))])
}

# loop for all sybpages
tryCatch(
for(iPage in 1:urlMax){
strToB <- paste0(strURLmain, ‘?pageNum=’,iPage)
df.inter <- getOnePageBooklet(strToB, curl)
df <- rbind(df, df.inter)
}, error=function(e) {print(iPage)})
# write.table(df, paste0(‘D:\’, as.numeric(Sys.time()) ,’.csv’), sep=";")
df
}

Функция, которая, используя функцию 2, собирает информацию со всех имеющихся уровней товарного классификатора. Не считая того к приобретенным данным добавляются наименования уровней классификатора. 3. На всякий вариант, когда полностью собрана информация по одной категории — итог сохраняется на диске.
3-я функцияgetOneCity <- function(urlMain = "http://m.tramlu.ru", curl = getCurlHandle()){
df.prices <- data.frame(inStock = character(),
SKU = character(),
Article = numeric(),
Comment = numeric(),
Price = numeric(),
level1 = character(),
level2 = character())
level1 <- getAllLinks(urlMain, curl)
numLevel1 <- length(level1[,2])

for (iLevel1 in 1:numLevel1){
strURLsubmain <- paste0(urlMain, level1[iLevel1, 2])
level2 <- getAllLinks(strURLsubmain, curl)
numLevel2 <- length(level2[,2])

for (iLevel2 in 1:numLevel2){
strURLsku <- paste0(urlMain, level2[iLevel2,2])
df.temp <- getOneBooklet(strURLsku, curl)
df.temp$level1 <- level1[iLevel1,1]
df.temp$level2 <- level2[iLevel2,1]

df.prices <- rbind(df.prices, df.temp)
}
write.table(df.prices, paste0(‘D:\’, iLevel1 ,’.csv’), sep=";", quote = FALSE)
}
df.prices
}

В предстоящем, ужаснувшись молота бана, пришлось добавить еще функцию — использующую прокси. В последствии выяснилось, что банить никто даже не собирался, весь классификатор, с учетом тестирования скриптов, был собран без заморочек, потому все ограничилось созданием data.frame с информацией по 100 прокси, которые так и не использовались.
Получение перечня proxygetProxyAddress <- function(){
htmlProxies <- getURL(‘http://www.google-proxy.net/’,
.encoding=’UTF-8′)
#htmlProxies <- gsub(‘</td></tr>’,’ n ‘, htmlProxies)
htmlProxies <- gsub(‘n’,», htmlProxies)
htmlProxies <- gsub(‘(</td><td>)|(</td></tr>)’,’ ; ‘, htmlProxies)
# parsing main subpage
htmlProxies.raw <- htmlTreeParse(
htmlProxies,
useInternalNodes=T
)

# finding last subpage
html.parse.proxies <- xpathApply(htmlProxies.raw,
path="//tbody",
fun=xmlValue)
html.parse.proxies<- gsub(‘( )+’,», html.parse.proxies)
final <- unlist(strsplit(as.character(html.parse.proxies),’;’))
final <- as.data.frame(matrix(final[1:800],
nrow = length(final)/8,
ncol = 8,
byrow=T))
#final <- gsub(‘( )+’,», final)
names(final) <- c(‘IP’,’Port’,’Code’,’Country’,’Proxy type’,’Google’,’Https’,’Last checked’)
sapply(final, as.character)
}

Применять прокси можно вот так
Как применять проксиopts <- list(
proxy = "1.1.1.1",
proxyport = "8080"
)
getURL("http://habrahabr.ru", .opts = opts)

Употребляются функции getURL из пакета RCurl, htmlTreeParse и xpathApply из пакета XML и несколько постоянных выражений. Структуры всех составляющих кода похожи.
При заходе на веб-сайт исследуемой компании возникает окно, которое дает выбрать город-месторасположение. Дальше нужно просто загрузить в R страничку, подобающую интересующему городку. Для того, чтоб R мог сохранять куки, нужно задать характеристики используемого соединения. По умолчанию при загрузке данных выдавалась информация по ценам доставки по почте с неопределенным наличием продукта. Данная информация потом сохраняется в куки браузера и употребляется для показа цен и продуктов в избранном городке. Крайней сложностью стало указание городка, цены в котором мы желаем выяснить.
Характеристики соединенияagent ="Mozilla/5.0"
curl = getCurlHandle()
curlSetOpt(cookiejar="cookies.txt",
useragent = agent,
followlocation = TRUE,
curl=curl)

Заместо заключения
Все, задав требуемые характеристики, запускаем функцию 3, ждем около получаса и получаем перечень со всеми продуктами и категориями исследуемого веб-сайта. Итог представляется в виде data.frame и пары сохраненных файлов на диске. Вышло наиболее 60 000 цен для городка Москва.
Скрипт полностью находится на GitHub.

habrahabr.ru Спасибо за уделенное внимание.

НЕТ КОММЕНТАРИЕВ

ОСТАВЬТЕ ОТВЕТ

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.