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

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

Поскольку мне нравятся карты и… движущиеся вещи, я решил как-то использовать данные с веб-сайта, на который я недавно наткнулся и который полюбил (вероятно, после многих часов игры в SimCity в подростковом возрасте) - Веб-сайт Uber Movement.

Он позволяет визуализировать анонимные данные о среднем времени в пути от определенной точки (или зоны) до любой другой точки в том же городе.

«Отлично!» - подумал я. «Позвольте мне просто загрузить время в пути и нанести на график некоторые цифры, чтобы я мог перейти к исследовательскому анализу данных (EDA)».

Оказывается, данные, которые мне нужны, было не так-то просто извлечь.

Эта проблема

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

Веб-сайт Uber Movement позволяет загружать данные из любой зоны в любую другую зону города. Однако есть одна загвоздка. Какой бы диапазон дат вы ни хотели узнать, время в пути не состоит из ежедневных данных.

То есть, если вы выберете загрузку значений с января 2020 года по март 2020 года, вы не получите 90 значений, что примерно соответствует количеству дней в этом диапазоне. Вместо этого он выводит файл csv с одним единственным значением для трехмесячного среднего времени в пути для каждой пары зон.

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

TL; DR: Uber Movement не предоставляет данные временных рядов, а монохронные / перекрестные данные.

Разумеется, были и другие ограничивающие факторы, такие как тот факт, что я получал только то, что я называю «радиальными» данными, когда я не получал бы время в пути между всеми возможными парами зон, а только от самой центральной части. укажите на другие точки. С точки зрения статистики, я не был уверен, что это даст мне достаточно точную оценку среднего времени в пути для данного города.

Вот (очень уродливая) картина того, как выглядят два геопространственных типа данных, которые вы можете скачать:

Однако надежда еще оставалась.

Моя наивная попытка решить проблему

Максимально возможный диапазон дат, который можно выбрать, составлял 3 месяца. С другой стороны, минимум - один день, а это как раз то, что мне нужно.

Поэтому я подумал: «Почему бы не щелкнуть каждый день и не загрузить соответствующий набор данных?».

Ну… Я попробовал.

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

На загрузку данных за один месяц у меня ушло около 6 минут 30 секунд. Среди доступных городов наибольшее количество дней данных составляет 3,25 года (с января 2016 года по март 2020 года) или 39 месяцев. Мне потребовалось бы 253 минуты или более 4 часов непрерывных щелчков мышью, чтобы загрузить все наборы данных для одного города. В общей сложности я хотел извлечь данные из 31 города, поэтому умножение 4 часов на 31 город дает примерно 5 дней ручной загрузки.

И это без еды и сна.

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

Решение

По сути, я использовал два пакета Python для автоматизации загрузки ежедневных наборов данных: Selenium для автоматизации действий (например, щелчок и ожидание ответов страницы) и datetime, чтобы использовать точный тип данных для чтения даты и времени, а не просто использовать строки.

Большой кусок кода состоит из простых назначений селекторов XPath и CSS для переменных и действий щелчка, выполняемых над этими переменными. Это было само по себе учебным упражнением для меня, поскольку я никогда не пользовался инструментами разработчика Google Chrome.

Круто, я только что научился автоматизировать клики. Затем у меня возникла идея пролистывать каждый день в календаре. Очень просто.

Однако теперь я столкнулся с большим препятствием.

Чтобы перебрать даты, мне пришлось вернуться со страницы, на которой была кнопка загрузки, в окно с календарем (чтобы я мог щелкнуть следующую дату). Для этого мне пришлось обновить страницу. И когда вы обновляете страницу, также обновляется DOM (объектная модель документа).

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

Например, Selenium выдал бы мне следующее сообщение об ошибке:

selenium.common.exceptions.StaleElementReferenceException: Message: <element_name>'stale element reference: element is not attached to the page document

Это был кошмар и проблема, с которой я не мог справиться, сколько бы сообщений на StackOverflow я ни читал. За этим следует отчаяние.

Поэтому я обращаюсь к своему бывшему коллеге, который, как я знал, имел опыт работы с CSS и Java, чтобы узнать, может ли он мне помочь, и, хотя мои надежды были невысокими, он придумал простую, но элегантную идею: «Почему вместо того, чтобы нажимать на даты и иметь чтобы обновить страницу, разве вы не обновляете сам URL, чтобы он содержал желаемую дату? »

Это изменило правила игры для моего проекта. (Валеу Эдуардо!).

Если вы внимательно посмотрите на URL-адрес Uber Movement для данного города, вы заметите, что он содержит всю информацию, необходимую для перехода на нужную страницу.

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

Функция создания URL выглядит следующим образом:

# Create URLs for the desired date range
def getURL():
    """"
    Function that creates one URL per date between the specified    date range
    """
    date = datetime(2016,2,2)
    while date <= datetime(2020,3,31):
        yield ('https://movement.uber.com/explore/' + city + '/travel-times/query?si' + origin_code + '&ti=&ag=' + zone_type + '&dt[tpb]=ALL_DAY&dt[wd;]=1,2,3,4,5,6,7&dt[dr][sd]=' +
               date.strftime('%Y-%m-%d') + '&dt[dr][ed]=' + date.strftime('%Y-%m-%d') + '&cd=&sa;=&sdn=' + coordinates + '&lang=en-US')
        date += timedelta(days=1)

Наконец, нам просто нужно создать механизм итерации, который выполняет следующий URL-адрес в функции getURL.

# Perform iteration through URLs downloading the datasets for each URL
iterated_URLs = []
i = 0
print('Number of generated URLs: ' + str(len(list(getURL()))))
for url in getURL():
    i += 1
    driver.execute_script("window.open('"+url+"', '_self')")
    iterated_URLs.append(url)

Вот бот в действии:

Вот и все!

Следующим шагом будет объединение каждого файла csv в один и выполнение очистки и форматирования для запуска EDA; что легко сделать с помощью Pandas.

Благодаря этому каждый может максимально использовать потрясающие данные, которые Uber предоставил нам бесплатно. В то время как анализ временных рядов был невозможен из-за характера структуры, в которой данные представлены на их веб-сайте, теперь мы можем расширить объем исследований, которые проводятся по времени в пути Uber и геопространственным данным.

Конечно, эти два фрагмента кода - это еще не все, поэтому вы можете проверить этот и другие мои проекты на GitHub.