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

Dec 01 2022
Недавно я пошел пообедать с другом, который только что вернулся домой в Орландо. Рассказывая о том, как развивается город, я упомянул ему, что повсюду, куда бы я ни пошел, открываются новые рестораны Popeyes.

Недавно я пошел пообедать с другом, который только что вернулся домой в Орландо. Рассказывая о том, как развивается город, я упомянул ему, что повсюду, куда бы я ни пошел, открываются новые рестораны Popeyes. С его точки зрения, он видел, как повсюду открывались новые рестораны KFC. Я знал, что мы оба не можем быть правы. В итоге мы спорили, какая сеть Fried Chicken имеет большую долю рынка в Соединенных Штатах.

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

  1. Сбор данных

Первой проблемой, с которой я столкнулся, был поиск надежного источника данных о местоположении магазинов Popeyes и KFC. Я нашел веб-сайт scrapehero.com, который предлагал мне данные по цене 95 долларов за набор данных одной сети. Однако я не собирался тратить около 200 долларов на то, чтобы похвастаться перед своим другом. После изучения веб-сайта Popeyes мне в голову пришла идея. Интересно, смогу ли я перепроектировать API (он же Web Scraping).

Первым делом я открыл инструменты разработчика, щелкнув правой кнопкой мыши и проверив. Я перешел на вкладку «Сеть» и просмотрел все входящие запросы. После некоторого расследования я обнаружил, что запрос используется для получения и заполнения местоположения магазина в пользовательском интерфейсе.

Найдите правильный запрос API, извлекающий данные

Вы можете скопировать запрос 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, чтобы удалить метки, а также уменьшить размер маркера. Это было важно, чтобы очистить карту, чтобы сделать ее более презентабельной.

Стилизация карты в Felt

Результаты были довольно шокирующими, даже несмотря на то, что я украдкой заглянул, когда предварительно просматривал данные. В KFC на 1000+ ресторанов больше, чем в Popeyes, и визуально это просто ошеломляет. KFC поглощает Popeyes до такой степени, что кажется, что вы даже не видите никаких маркеров для Popeyes.

Почувствовал, что инструмент картографирования, который я использовал, также имеет возможность легко включать и выключать слои.

Заключительные примечания

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

Карта KFC vs Popeyes, созданная с помощью Felt

PS Popeyes превосходит KFC

Посмотрите карту на Felt:

У вас есть вопросы или отзывы? Я хотел бы услышать это!

электронная почта: [email protected]

твиттер:https://twitter.com/duckduckquy

Чувствовала:https://felt.com/

Гитхаб:https://github.com/duckduckquy/popeyesScraper

Источник данных широты и долготы:https://gist.github.com/meiqimichelle/7727723