Данная статья представлена исключительно в ознакомительных целях и не несет призыва к действию. Вся информация направлена на то, чтобы уберечь читателей от противозаконных действий.
Категорически запрещено использование разработки для совершения любых противоправных действий.
Вступление
Привет, сегодня мы спарсим информацию с стима, информации нужно парсить много, и код возможно вышел сложный на вид, но ничего, я подробно опишу где и как вы сможете достать определенную информацию, думаю распарсить текст вам не составит труда, но так же я поделюсь и своим кодом на языке Python.
В коде мы будем очень много использовать BeautifulSoup/lxml – данные модули служат для удобного парсинга html разметки, все тоже самое можно сделать с помощью регулярных выражений, их мы тоже будем использовать.
*Возможно есть способ сделать это лучше, я лишь делюсь своим опытом, надеюсь помогу сэкономить время.*
*Для примера используется Python, я лишь показываю алгоритм действий, и не призываю использовать мой код.*
Для работы
Для аутентификация нам понадобится кук “steamLoginSecure”, его мы можем достать с логов, ну или же через расширение браузера, например Cookie Editor.
Для работы нам понадобятся библиотеки:
requests, bs4, lxml, re, json
Приступим
Для начала я предоставлю информацию о url, ещё раз хочу напомнить, эта информация предоставлена для разработчиков, дабы не бегать по всему стиму, вы можете сами выбрать ту информацию которая нужна вам, и вашей программе.
(HTML) – означает что берем информацию из html тела, (JSON) – из jsonGET(HTML): https://store.steampowered.com/ – Тут мы сможем получить, идентификатор аккаунта, баланс, а так же ссылку на аккаунт(она пригодится для дальнейшего парсинга, обозначим ее как account_url).
GET: account_url(HTML) – Получим уровень стим аккаунта.
GET: account_url/games/?tab=all(HTML) – На этой странице мы можем получить информацию о играх на аккаунте, в стиме у каждой игры есть свой айди, так мы сможем проверять, имеет ли данный аккаунт определенные игры.
GET: https://store.steampowered.com/account/licenses/(HTML) – На данной странице мы можем просмотреть историю покупок, я использую данную страницу чтобы проверить имеет ли аккаунт прайм, если там есть строчка “Global Offensive Free Edition” значит прайма нет.
GET: account_url/inventory/(HTML) – Тут мы можем узнать в каких играх у нас есть инвентарь, схема такая же, у каждой игры есть свой айди(он нам пригодится), так же тут есть поле под названием “g_steamID”, данное значение понадобится для парсинга самого инвентаря.
GET: https://steamcommunity.com/inventory/g_steamID(значение добытое выше)/айди_игры//2l=russian&count=число_предметов(JSON) – Данный запрос идет к апи, и отдается json с предметами, число предметов мы так же получаем из запроса выше.
ID730 – Кс
ID570 – Вроде дота, любое айди можно узнать перейдя по ссылке на игру https://store.steampowered.com/app/570/Dota_2/, как мы видим, айди 570
Мой код
Честно, даже не знаю что пояснить тут, по факту скажу, что функция принимает 3 аргумента, куки в виде словаря, а так же 2 кортежа, вscraping_game_inventory мы передаем айди игр инвентарь которых мы хотим получить. has_game_account передаем айди игр которые мы хотим проверить на наличие. Запросы идут в порядке, как они представлены выше, тут особо нечего прокомментировать, делаем запрос -> вырезаем данные, можете брать мой пример, а можете написать свой ориентируясь на мой код, он вам поможет найти местонахождение конкретной информации.
import json
import re
import time
import requests
import lxml
from bs4 import BeautifulSoup
def checker_steam(cookies: dict, scraping_game_inventory: tuple = (), has_game_account: tuple = ()):
session = requests.Session()
for key, value in cookies.items():
session.cookies.set(key, value)
game_in_account = {}
inventory_in_account = {}
response = session.get('https://store.steampowered.com/', timeout=10)
soup = BeautifulSoup(response.text, 'lxml')
account_id = soup.find('span', id='account_pulldown').text.strip()
wallet_balance = soup.find('a', id='header_wallet_balance').text.strip()
account_url = soup.find('a', class_='menuitem supernav username persona_name_text_content').get('href').replace(
'/home', '')
response = session.get(account_url)
soup = BeautifulSoup(response.text, 'lxml')
account_lvl = soup.find('span', class_='friendPlayerLevelNum').text.strip()
if has_game_account:
response = session.get(account_url + '/games/?tab=all')
game_in_account_re = re.findall(r"var rgGames = \[(.*?)\]", response.text)[0]
for game_id in has_game_account:
account_have_game = re.search(r'\"appid\":' + str(game_id) + r',\"name\":(.*?),', game_in_account_re)
context = {'name': 'None', 'have': False}
if account_have_game:
if game_id == 730:
response = session.get('https://store.steampowered.com/account/licenses/', timeout=10)
has_prime = True if not re.search('Global Offensive Free Edition ', response.text) else False
context = {'name': account_have_game.groups()[0].strip('"'), 'have': True}
game_in_account[game_id] = context
if scraping_game_inventory:
response = session.get(account_url + '/inventory/')
soup = BeautifulSoup(response.text, 'lxml')
account_inventory_id = re.search(r'g_steamID = \"(.*?)\"', response.text).groups()[0]
game_in_account_soup = soup.find('div', class_='games_list_tabs').find_all('a')
for game in game_in_account_soup:
game_id = int(game.get('href').strip('#'))
if game_id in scraping_game_inventory:
items_count = game.find('span', class_='games_list_tab_number').text.strip('()')
context = []
if int(items_count) > 0:
response = session.get(
f'https://steamcommunity.com/inventory/{account_inventory_id}/{game_id}/2?l=russian&count={items_count}',
timeout=10)
for item in response.json()['descriptions']:
context.append(item['market_name'])
inventory_in_account[game_id] = context
response = session.get('https://store.steampowered.com/account/', timeout=10)
has_guard = True if re.search(r'https://store.akamai.steamstatic.com/public/images/account/sg_good\.png',
response.text) else False
soup = BeautifulSoup(response.text, 'lxml')
for i in soup.find_all('span', class_='account_data_field'):
if '@' in i.text:
email = i.text
context = {
'account_id': account_id,
'email': email,
'wallet_balance': wallet_balance,
'account_link': account_url,
'account_lvl': account_lvl,
'has_guard': has_guard,
'has_prime': has_prime,
'game_check_id': has_game_account,
'game_in_account': game_in_account,
'game_check_inventory_id': scraping_game_inventory,
'return_inventory_in_account': inventory_in_account,
}
return context
if __name__ == '__main__':
start = time.time()
session = requests.Session()
response = checker_steam(
{
"steamLoginSecure": "",
},
has_game_account=(291550, 304050, 2143, 730), scraping_game_inventory=(433, 570, 730, 120))
fin = time.time()
with open(f'xedRecordJson.json', 'w', encoding='utf-8') as outfile:
json.dump(response, outfile, indent=4, ensure_ascii=False)
Результат на выходе:
{
"account_id": "",
"email": "",
"wallet_balance": "$0.02 USD",
"account_link": "",
"account_lvl": "11",
"is_guard": true,
"is_prime": true,
"game_check_id": [
291550,
304050,
2143,
730
],
"game_in_account": {
"291550": {
"name": "None",
"have": false
},
"304050": {
"name": "None",
"have": false
},
"2143": {
"name": "None",
"have": false
},
"730": {
"name": "Counter-Strike: Global Offensive",
"have": true
}
},
"game_check_inventory_id": [
433,
570,
730,
120
],
"return_inventory_in_account": {
"570": [
"Боевой пропуск 2022: мешочек конфет",
"The International 2022: набор карточек",
"Ruling Staff of the Priest Kings",
"Hides of Hostility - Arms",
"Hides of Hostility - Off-Hand",
"Hides of Hostility - Shoulder",
"Hides of Hostility - Head",
"Hides of Hostility - Weapon",
"Насмешка: Blades of Gory",
"Silkwing Spotters",
"Pouches",
"Боевой пропуск 2022: комплект музыки",
"Боевой пропуск 2022: загрузочный экран I",
"Наклейка команды: PSG.LGD | TI 2022",
"Lineage Arm Guard of the Ram's Head",
"Lineage Belt of the Ram's Head",
"Flutterstep",
"Заряды паровозика",
"Lineage Mask of the Ram's Head",
"Заряды пузырьков",
"Заряды шариков",
"Lineage Pauldron of the Ram's Head",
"Боевой пропуск 2022: Immortal Treasure II",
"Flockheart's Gamble",
"Voice of Flockheart's Gamble",
"Featherfall Spaulders",
"Lineage Ram's Head Blade",
"Staff of the Malevolent",
"Осенний ландшафт",
"Caustic Tarsus",
"Caustic Spurs",
"Caustic Rack",
"Caustic Guard",
"Caustic Glare",
"Загрузочный экран: Stargazer's Curiosity",
"Staff of the Stargazer's Curiosity ",
"Mantle of the Stargazer's Curiosity ",
"Cloak of the Stargazer's Curiosity ",
"Hood of the Stargazer's Curiosity",
"Rifle of the Great Safari",
"Dota Plus: кричалка «Уверенность в победе»",
"Dota Plus: месяц подписки (продлевается автоматически)",
"Sigil of the Tahlin Watch",
"Convergence of Distant Fates Arms",
"Convergence of Distant Fates Shoulder",
"Convergence of Distant Fates Back",
"Convergence of Distant Fates Head",
"Amberlight Shawl",
"Summer Lineage Shoulders of the Ageless Witness",
"Summer Lineage Legs of the Ageless Witness",
"Summer Lineage Head of the Ageless Witness",
"Summer Lineage Arms of the Ageless Witness",
"Feast of the Damned Back",
"Feast of the Damned Head",
"Feast of the Damned Arms",
"Feast of the Damned Belt",
"Dark Debutante Ultimate",
"Dark Debutante Skirt",
"Dark Debutante Armor",
"Dark Debutante Head",
"Carapace of the Burning Sand",
"Crown of the Burning Sand",
"Shadows of the Fall Head",
"Shadows of the Fall Legs",
"Shadows of the Fall Shoulder",
"Blazing Tiger Arms",
"Blazing Tiger Belt",
"Blazing Tiger Head",
"Blazing Tiger Off-Hand",
"Blazing Tiger Shoulder",
"Frostwatch Warmonger Legs",
"Frostwatch Warmonger Head",
"Inscribed True Crow's Bite",
"True Crow's Ward",
"True Crow's Talon",
"True Crow's Vision",
"True Crow's Pelt",
"True Crow's Wings",
"Загрузочный экран: True Crow",
"Bracers of the Endless Plane",
"Pauldron of the Endless Plane",
"Загрузочный экран: Elements of the Endless Plane",
"Belt of the Endless Plane",
"Helm of the Endless Plane",
"Mace of the Endless Plane",
"Tail of Sundering",
"Frostwatch Warmonger Back",
"Frostwatch Warmonger Weapon",
"Shadows of the Fall Weapon",
"Crystal Blades of the Burning Sand",
"Mistplates of Ruin",
"Feelers of Entwined Fate",
"Blazing Tiger Weapon",
"Snowdrop Mittens",
"Spring Lineage Phalanx of the Fallen Spear Golem",
"Spring Lineage Tail of Unfettered Malevolence",
"Spring Lineage Belt of Unfettered Malevolence",
"Spring Lineage Helm of Unfettered Malevolence",
"Spring Lineage Bracers of Unfettered Malevolence",
"Riftshadow Roamer's Grabbin' Wraps",
"Набор Armor of Renewed Faith",
"Spring Lineage Phalanx of the Fallen Spear Armor",
"Spring Lineage Phalanx of the Fallen Spear Helm",
"Spring Lineage Wings of Unfettered Malevolence",
"Drowned Horseman",
"Lineage Bracer of Petaluna",
"Lineage Mask of Petaluna",
"Lineage Shoulders of Petaluna",
"Spring Lineage Phalanx of the Fallen Spear Bracers",
"Lineage Skirt of Petaluna",
"Lineage Staff of Petaluna",
"Dark Debutante Belt",
"Frostwatch Warmonger Arms",
"Trophies of the Evernight",
"Golden Siblings Dagger",
"Загрузочный экран: Rightful Heir",
"Загрузочный экран: Wyvern Skin",
"Quiver of the Wyvern Skin",
"Mantle of the Wyvern Skin",
"Cowl of the Wyvern Skin",
"Cape of the Wyvern Skin",
"Bracers of the Wyvern Skin",
"Bow of the Wyvern Skin",
"Boots of the Wyvern Skin",
"Heir's Steps",
"Heir's Glance",
"Belt of the Arsenal Magus",
"Frostiron Raider Wrap",
"Dark Pauldrons of the Conjurer",
"Pride of the Woodland Outcast",
"Golden Siblings Offhand",
"Heir's Gift",
"Heir's Cover",
"Comb of the Great Grey",
"Arctic Hunter's Glove",
"Direstone Bracers",
"Blade of the Dark Ancients",
"Back Hook",
"Cranial Clap Trap",
"Spear of Teardrop Ice",
"Head-Ache",
"Tail of Fury",
"Heavenly Guardian Bracers",
"Golden Reel Guardian Belt",
"Stone Helmet",
"Thorns of the Highborn",
"Sentinel Quiver",
"Загрузочный экран: Relentless Warbringer",
"Barding of the Warbringer",
"Tail of the Warbringer",
"Armor of the Warbringer",
"Helm of the Warbringer",
"Belt of the Warbringer",
"Armguard of the Warbringer",
"Relentless Warbringer's Decapitator",
"Gift of the Sea Arms",
"Bracer of a Savage Age",
"Helm of Retribution",
"Cloud Forged Great Girdle",
"Horns of Blight"
],
"730": [
"Запечатанный граффити | Плак-плак (Акулий белый)",
"Медаль за службу в 2022",
"Запечатанный граффити | Зубастик (Пустынный янтарный)",
"Запечатанный граффити | Песик (Королевский синий)",
"Запечатанный граффити | Шар судьбы (Пластиковый синий)",
"Наклейка | Heroic | Антве
Всем хорошего вечера, да статья возможно получилась немного сложная, ведь много информации приходится парсить из HTML, статья рассчитана на разработчиков.