Именно этот вопрос возник у нас в процессе игры в "Города" пока мы ехали из Екатеринбурга в Тюмень, а названия городов то и дело заканчивались на "К". В тот момент город Курган был назван уже 25 раз. И нас озарило... Спарсим данные с RuWiki и посмотрим сколько городов в России начинаются и заканчиваются на букву К!

Наш подробный пайплайн исследования названий городов России:
1) Сбор данных
Мы решили спарсить данные с названиями городов и датой их первого упоминания с RuWiki.
Пример кода для парсинга данных с сайтов (Спасибо эре вайб кодинга)):
import requests
from bs4 import BeautifulSoup
import csv
url = "https://ru.ruwiki.ru/wiki/Список_городов_России"
resp = requests.get(url)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
# находим основную таблицу списка городов
table = soup.find("table")
rows = table.find_all("tr")
results = []
for row in rows[1:]: # пропускаем заголовок
cols = row.find_all(["td","th"])
if len(cols) >= 7:
# номер, герб, город, регион, округ, население, основание
city = cols[2].get_text(strip=True) #берем 2 колонку
first_mention = cols[6].get_text(strip=True)
results.append((city, first_mention))
# сохраняем в CSV
with open("cities_first_mention.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Город", "Основание/Первое упоминание"])
writer.writerows(results)
print(f"Записано {len(results)} строк.")2) Нормализация данных
Данные с RuWiki не всегда однородные: в названиях могут быть пробелы, а даты находиться в форматах «1147» и «XII век». С помощью регулярок названия городов приведены к единому виду - убрали лишние пробелы, а также преобразовали века в годы: например, XII век → 1100.
import re
def extract_year(text):
# Проверяем, что входное значение — строка, иначе возвращаем None
if not isinstance(text, str):
return None
text = text.lower() # приводим текст к нижнему регистру для упрощения поиска
# ищем обычный год в формате 1000–1999 или 500–999
year_match = re.search(r'\b(1[0-9]{3}|[5-9][0-9]{2})\b', text)
if year_match:
return int(year_match.group()) # если нашли год — возвращаем его как число
# ищем века, записанные римскими цифрами
century_match = re.search(r'([ivxlcdm]+)\s*век', text)
if century_match:
roman = century_match.group(1).upper() # получаем римское число и переводим в верхний регистр
roman_map = {
'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000
}
# Конвертация римских цифр в арабское число
value = 0
prev = 0
for c in roman[::-1]: # идём с конца строки
if roman_map[c] < prev: # если меньшая цифра перед большей — вычитаем
value -= roman_map[c]
else: # иначе прибавляем
value += roman_map[c]
prev = roman_map[c]
return (value - 1) * 100 # переводим век в начало века (например, XII в 1100)
# Если ни обычный год, ни век не найдены — возвращаем None
return None
3) Диахронический анализ (временная динамика)
Помимо того, что мы достали годы, хотелось сгруппировать даты по различным историческим периодам, чтобы в дальнейшем анализировать уже более мелкие группы.

Всё это получилось реализовать с помощью простых if, elif, else:
def period_historical(year):
if year is None:
return None
elif year < 1300: # VIII–XIII век
return "Киевская Русь / Древнерусское государство"
elif year < 1700: # XIV–XVI век
return "Московский период"
elif year < 1917: # XVII–1916
return "Период Российской империи"
elif year < 1991: # 1917–1990
return "Советский период"
else: # после 1991
return "Постсоветский период"С помощью pandas загружается CSV с названиями городов и годом их основания. Функция period_historical присваивает каждому городу исторический период: Киевская Русь, Московский период, Российская империя, Советский и Постсоветский. С помощью value_counts() и упорядочивания категорий по хронологии. И, конечно, визуализация:3 matplotlib строит столбчатую диаграмму, где видно количество городов, основанных в каждом периоде.

matplotlib, где видно количество городов, основанных в каждом периоде. 4) Алфавитный анализ
Скрипт считает, сколько городов начинается с каждой буквы алфавита, и строит столбчатую диаграмму распределения по буквам.
import matplotlib.pyplot as plt
# Получаем первую букву каждого города
df["first_letter"] = (
df["Город"] # берем название города
.str.strip() # убираем пробелы по краям
.str.upper() # приводим букву к верхнему регистру
.str[0] # берём только первую букву
)
# Считаем количество городов на каждую букву и сортируем по алфавиту
letter_counts = (
df["first_letter"]
.value_counts() # подсчёт уникальных букв
.sort_index() # сортировка по алфавиту
)
# Строим столбчатую диаграмму
plt.figure()
letter_counts.plot(kind="bar") # гистограмма
plt.xlabel("Первая буква названия города") # подпись оси X
plt.ylabel("Количество городов") # подпись оси Y
plt.title("Распределение названий городов России по начальной букве") # заголовок
plt.xticks(rotation=0) # подписи букв горизонтально
plt.tight_layout() # подгонка графика
plt.show() # отображение графика
Подобная операция была выполнена для определения количества городов, заканчивающихся на какую-либо из букв алфавита.

И снова буква К в победителях) Городов, начинающихся на К: 175 Городов, заканчивающихся на К: 404 (не Error)).
5) Интересные закономерности:
Большинство городов основано в период Российской империи;
Буква «К» доминирует в названиях городов России, причем не только в начале слов, но и в конце;
Городов, начинающихся на Й - 1!!! Городов, заканчивающихся на Й, заметно больше - 80! Так что можно выигрывать за счет этого)));
Города-дубликаты! Найдено 27 близняшек))

Самые короткие названия (2-3 буквы): Уфа, Ом, Як. Длинные названия состоят из 15–20 букв, например Северодвинск, Новокузнецк, Новосибирск.
