Fried Chicken Wars — Отображение территории Popeyes и KFC с помощью войлока

Недавно я пошел пообедать с другом, который только что вернулся домой в Орландо. Рассказывая о том, как развивается город, я упомянул ему, что повсюду, куда бы я ни пошел, открываются новые рестораны Popeyes. С его точки зрения, он видел, как повсюду открывались новые рестораны KFC. Я знал, что мы оба не можем быть правы. В итоге мы спорили, какая сеть Fried Chicken имеет большую долю рынка в Соединенных Штатах.
В результате этих жарких дебатов я решил ответить на вопрос, собрав данные и создав визуализацию карты. Ниже я проведу вас через мой процесс.
- Сбор данных
Первой проблемой, с которой я столкнулся, был поиск надежного источника данных о местоположении магазинов Popeyes и KFC. Я нашел веб-сайт scrapehero.com, который предлагал мне данные по цене 95 долларов за набор данных одной сети. Однако я не собирался тратить около 200 долларов на то, чтобы похвастаться перед своим другом. После изучения веб-сайта Popeyes мне в голову пришла идея. Интересно, смогу ли я перепроектировать API (он же Web Scraping).
Первым делом я открыл инструменты разработчика, щелкнув правой кнопкой мыши и проверив. Я перешел на вкладку «Сеть» и просмотрел все входящие запросы. После некоторого расследования я обнаружил, что запрос используется для получения и заполнения местоположения магазина в пользовательском интерфейсе.

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


После этого шага я написал примерный сценарий, который отправлял запросы и сохранял их в CSV на моем компьютере. Ключевые вещи, которые я заметил в запросе, который использовался для получения всех местоположений магазинов, заключаются в том, что в полезной нагрузке у вас было несколько переменных, с которыми вы могли поиграть, включая: userLat, userLng, searchRadius и first. Настроив эти переменные, я смог собрать данные для всех 2851 магазинов. Пользователь Github meiqimichelle проделал тяжелую работу, предоставив координаты широты и долготы каждого штата, который я просматривал. Я бы использовал аналогичный подход при сборе данных о магазине KFC.
const axios = require('axios');
const ObjectsToCsv = require('objects-to-csv');
const states = require('./constants');
function getPopeyesLocations(){
const promises = []
for( let i = 0; i < states.length; i++){
const state = states[i]
const { latitude : lat, longitude : lon } = state
var data = JSON.stringify([
{
"operationName": "GetRestaurants",
"variables": {
"input": {
"filter": "NEARBY",
"coordinates": {
"userLat": lat,
"userLng": lon,
"searchRadius": 800000
},
"first": 10000,
"status": "OPEN"
}
},
"query": "query GetRestaurants($input: RestaurantsInput) {\n restaurants(input: $input) {\n pageInfo {\n hasNextPage\n endCursor\n __typename\n }\n totalCount\n nodes {\n ...RestaurantNodeFragment\n __typename\n }\n __typename\n }\n}\n\nfragment RestaurantNodeFragment on RestaurantNode {\n _id\n storeId\n isAvailable\n posVendor\n chaseMerchantId\n curbsideHours {\n ...OperatingHoursFragment\n __typename\n }\n deliveryHours {\n ...OperatingHoursFragment\n __typename\n }\n diningRoomHours {\n ...OperatingHoursFragment\n __typename\n }\n distanceInMiles\n drinkStationType\n driveThruHours {\n ...OperatingHoursFragment\n __typename\n }\n driveThruLaneType\n email\n environment\n franchiseGroupId\n franchiseGroupName\n frontCounterClosed\n hasBreakfast\n hasBurgersForBreakfast\n hasCatering\n hasCurbside\n hasDelivery\n hasDineIn\n hasDriveThru\n hasTableService\n hasMobileOrdering\n hasLateNightMenu\n hasParking\n hasPlayground\n hasTakeOut\n hasWifi\n hasLoyalty\n id\n isDarkKitchen\n isFavorite\n isHalal\n isRecent\n latitude\n longitude\n mobileOrderingStatus\n name\n number\n parkingType\n phoneNumber\n physicalAddress {\n address1\n address2\n city\n country\n postalCode\n stateProvince\n stateProvinceShort\n __typename\n }\n playgroundType\n pos {\n vendor\n __typename\n }\n posRestaurantId\n restaurantImage {\n asset {\n _id\n metadata {\n lqip\n palette {\n dominant {\n background\n foreground\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n crop {\n top\n bottom\n left\n right\n __typename\n }\n hotspot {\n height\n width\n x\n y\n __typename\n }\n __typename\n }\n restaurantPosData {\n _id\n __typename\n }\n status\n vatNumber\n __typename\n}\n\nfragment OperatingHoursFragment on OperatingHours {\n friClose\n friOpen\n monClose\n monOpen\n satClose\n satOpen\n sunClose\n sunOpen\n thrClose\n thrOpen\n tueClose\n tueOpen\n wedClose\n wedOpen\n __typename\n}\n"
}
]);
var config = {
method: 'post',
url: 'https://use1-prod-plk.rbictg.com/graphql',
headers: {
'authority': 'use1-prod-plk.rbictg.com',
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'apollographql-client-name': 'wl-web',
'apollographql-client-version': '4ef9144',
'content-type': 'application/json',
'origin': 'https://www.popeyes.com',
'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
'x-forter-token': '4798c310640a4c129119b405583aca62_1669841988907__UDF43_13ck_tt',
'x-session-id': '4C6B1EAF-0D2A-4295-84FB-81CDCDFC25D2',
'x-ui-language': 'en',
'x-ui-platform': 'web',
'x-ui-region': 'US',
'x-user-datetime': '2022-11-30T16:00:05-05:00'
},
data : data
};
promises.push(axios(config))
}
Promise.allSettled(promises).then((res) => {
const popeyes = []
for( let i = 0; i < res.length; i++){
const nodes = res[i].value.data[0].data?.restaurants?.nodes
const restaurants = nodes.map(node => {
return {
id : node?.id, name : 'Popeyes ' + node?.name, lat : node?.latitude, lon : node?.longitude
}
})
restaurants.forEach(restaurant => {
const isInArray = popeyes.find(fRestaurant => fRestaurant.id === restaurant.id )
if( !isInArray){
popeyes.push(restaurant)
}
})
}
const csv = new ObjectsToCsv(popeyes);
csv.toDisk('./Popeyes_Store_Locations.csv');
})
}
getPopeyesLocations();
Popeyes Store Locations CSV
Теперь самое интересное — отобразить все собранные данные. Я использовал функцию загрузки в Felt, чтобы создать два слоя. Один для Popeyes, а другой для KFC.
Имея на карте почти 6700+ точек, я использовал панель стилей, встроенную в Felt, чтобы удалить метки, а также уменьшить размер маркера. Это было важно, чтобы очистить карту, чтобы сделать ее более презентабельной.

Результаты были довольно шокирующими, даже несмотря на то, что я украдкой заглянул, когда предварительно просматривал данные. В KFC на 1000+ ресторанов больше, чем в Popeyes, и визуально это просто ошеломляет. KFC поглощает Popeyes до такой степени, что кажется, что вы даже не видите никаких маркеров для Popeyes.
Почувствовал, что инструмент картографирования, который я использовал, также имеет возможность легко включать и выключать слои.
Заключительные примечания
Данные не лгут (в большинстве случаев). KFC доминирует над Popeyes в США. Однако этот мысленный эксперимент заставил меня подумать, что было бы интересно когда-нибудь потенциально использовать этот инструмент для картирования территории продаж, а также для анализа того, следует ли вам открывать ресторан/бизнес в определенной области.

PS Popeyes превосходит KFC
Посмотрите карту на Felt:
У вас есть вопросы или отзывы? Я хотел бы услышать это!
электронная почта: [email protected]
твиттер:https://twitter.com/duckduckquy
Чувствовала:https://felt.com/
Гитхаб:https://github.com/duckduckquy/popeyesScraper
Источник данных широты и долготы:https://gist.github.com/meiqimichelle/7727723