Как скачать статистику игроков world of tanks / Павел...
TRANSCRIPT
Как скачать статистику игроковWorld of Tanks за одну ночь
Павел Пересторонин
История• Игроки
• Танки
• Бои
• Победы
• Поражения
• Статистика
• Процент побед на каждом танке
2
49.9%
Не все танки одинаково полезныPz.Kpfw. 38 (t) n.A. 57%
БТ-2 55%
Type 3 Chi-Nu 47%
Type 1 Chi-He 46%
Type 4 Chi-To 33%
4
Нужно больше данных
https://api.worldoftanks.ru/wot/account/tanks/…, {
"statistics": {
"wins": 21 ,
"battles": 51
},
"mark_of_mastery": 1,
"tank_id": 1025
}, …
01.
02.
03.
04.
05.
06.
07.
08.
6
Нужно с чего-то начатьfor account_id in range(1, 50000000):
requests.get(
"https://api.worldoftanks.ru/wot/account/tanks/",
params={
"account_id": account_id,
"application_id": "…",
},
)
01.
02.
03.
04.
05.
06.
07.
08.
7
3+ месяцев
Keep-Alive$ python -m timeit -s "import requests" -- \
"requests.get('http://google.com')"
10 loops, best of 3: 543 msec per loop
$ python -m timeit -s \
"import requests; s = requests.Session() " -- \
"s.get('http://google.com')"
10 loops, best of 3: 313 msec per loop
01.
02.
03.
01.
02.
03.
04.
9
2+ суток
concurrent.futures.ThreadPoolExecutor• Сложно отлаживать и останавливать
• Фиксированное число потоков
• …которые все время чего-то ждут
11
Event Loopimport asyncio, aiohttp
@asyncio.coroutine
def func():
response = yield from aiohttp.request(
"GET", "http://ya.ru")
text = yield from response.text()
print(text)
asyncio.get_event_loop().run_until_complete(func())
01.
02.
03.
04.
05.
06.
07.
08.
13
response = yield from aiohttp.request (
"GET",
"http://api.worldoftanks.ru/wot/account/tanks/",
params=params,
)
if response.status == http.client.OK:
json = yield from response.json()
if json["status"] == "ok":
return json["data"]
logging.warning("API error: %s", json["error"]["message"])
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
14
Keep-Aliveconnector = aiohttp.TCPConnector()
# …
response = yield from aiohttp.request(
'get', 'http://python.org', connector=connector )
01.
02.
03.
04.
15
wait_for и sleepasyncio.get_event_loop().run_until_complete(
asyncio.wait_for (
aiohttp.request("GET", "http://ya.ru"),
0.01,
))
01.
02.
03.
04.
05.
16
Еще ничего не ускорилиНо у нас уже есть конкурентный однопоточный код, который
поддерживает одновременное ожидание нескольких запросов.
17
Нужно больше запросовpending = set()
for account_ids in chop(range(start_id, end_id + 1), 100):
pending.add(asyncio.async(api.account_tanks(account_ids)))
if len(pending) < max_pending_count :
continue
done, pending = yield from asyncio.wait (
pending, return_when= asyncio.FIRST_COMPLETED)
# TODO: done.result()
01.
02.
03.
04.
05.
06.
07.
08.
18
Нужно больше запросовТеперь на каждой итерации можно подстраивать число запросов в
очереди.
Например, подсчитывая число ошибок REQUEST_LIMIT_EXCEEDED .
19
Зачем это всё?• Хорошо отлаживается
• Не нужно думать про синхронизацию
• Работает быстрее
20
Занимательная статистика• 14 часов
• Последний ID на RU-регионе: 40 751 344
• 19 905 151 игроков
• 632 538 123 танков
• 31.8 танков в среднем на аккаунт
• Дамп уместился в 2752.5MiB .
• 145B на аккаунт. 4.6B на танк.
21