Вопрос: Что не так с моим парсером на python?

Лучший ответ:
Я уже здесь неоднократно советовал, возьмите себе за правило, перед любым парсингом, загрузите страницу с помощью скрипта себе на диск. Далее откройте страницу в текстовом редакторе, и поищите - есть ли нужный элемент с нужным классом(или id) в html. Если есть, значит можно работать requests'том. В противном случае - Selenium (есть еще XHR...).
Вот сам код:
import requests

headers = {'user-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0',
			'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
}

url = 'ссылка'
filename = 'index.html'

response = requests.get(url,headers=headers)
if response.status_code == 200:
    with open(filename,'w') as file:
        file.write(response.text)
else:
	print(response)
Вписываете ссылку и запускаете скрипт. Если все ОК - на диске появится файл index.html(на этом файле можно дальше тренироваться с парсингом). В противном случае - в консоль вылетит HTTP код ошибки. Если ошибка, подставляете заголовки, cookies ... и заново.

Конкретно в данном случае, элемента div с классом play-detail в html нет. Он появиться после обработки js скриптов js движком. Но выход есть. Все данные есть. Но они в формате json в теге script с id (если не ошибаюсь) - json-extras.

Худший ответ:
В блоке <script id="json-extras" type="application/json"> вся нужная тебе информацию представлена в формате JSON:
json elem{
   "scoresBest":[
      {
         "id":3211927044,
         "best_id":3211927044,
         "user_id":16873295,
         "accuracy":0.8836257309941521,
         "mods":[
            
         ],
         "score":1981044,
         "max_combo":377,
         "perfect":false,
         "statistics":{
            "count_50":1,
            "count_100":41,
            "count_300":238,
            "count_geki":29,
            "count_katu":21,
            "count_miss":5
         },
         "pp":69.7365,
         "rank":"B",
         "created_at":"2020-08-25T08:26:10+00:00",
         "mode":"osu",
         "mode_int":0,
         "replay":false,
         "beatmap":{
            "difficulty_rating":4.84,
            "id":1385399,
            "mode":"osu",
            "version":"Fanteer's Insane",
            "accuracy":7.5,
            "ar":9,
            "beatmapset_id":653534,
            "bpm":110,
            "convert":false,
            "count_circles":123,
            "count_sliders":162,
            "count_spinners":0,
            "cs":3.5,
            "deleted_at":null,
            "drain":6,
            "hit_length":95,
            "is_scoreable":true,
            "last_updated":"2018-01-08T11:05:04+00:00",
            "mode_int":0,
            "passcount":772659,
            "playcount":3155157,
            "ranked":1,
            "status":"ranked",
            "total_length":96,
            "url":"https:\/\/osu.ppy.sh\/beatmaps\/1385399"
         },
   ]
}
Можешь хоть регуляркой вытягивать этот JSON, преобразовывать его в словарь Python и доставать нужные тебе данные.
Так получается, потому что все элементы на странице рендерятся JS'ом, а requests не умеет интерпретировать JS-код, по-этому исходный код отличается от конечного, который отображается в твоём браузере. Проверить то, какой изначально страница представляется до отработки JS можешь так, как подсказал Gennady S