It's currently not possible to collect user achievement data wit the "new" API. It's possible with the "old" XML API though.
It's basically just adding an uri parameter named xml
with value 1
to a normal community uri. (So for a community page of http://steamcommunity.com/profiles/XYZ
this becomes http://steamcommunity.com/profiles/XYZ?xml=1
)
This is a sample C# implementation (for demonstration purposes only!) which exposes a User.Load
method which takes the community id as a parameter. (That id would be XYZ
in the example above.)
// System.dll
// System.Xml.dll
// System.Xml.Linq.dll
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using System.Xml.XPath;
public class Achievement
{
public string Name { get; set; }
public string Description { get; set; }
public Uri Icon { get; set; }
}
public class Game
{
public int Id { get; set; }
public string Name { get; set; }
public Uri Logo { get; set; }
}
public class OwnedGame : Game
{
public User User { get; set; }
public Stats Stats { get; set; }
}
public class Stats
{
public OwnedGame Game { get; set; }
public IEnumerable<Achievement> Achievements { get; set;}
}
public class User
{
private const string SteamCommunityProfileUriFormat = "http://steamcommunity.com/profiles/{0}?xml=1";
private const string SteamCommunityProfileGamesUriFormat = "http://steamcommunity.com/profiles/{0}/games?xml=1";
public long Id { get; set; }
public string Name { get; set; }
public Uri Avatar { get; set; }
public IEnumerable<OwnedGame> Games { get; set; }
public static User Load(long id)
{
XDocument profileDocument = XDocument.Load(string.Format(SteamCommunityProfileUriFormat, id));
XElement profileElement = profileDocument.Element("profile");
User user = new User()
{
Id = long.Parse(profileElement.Element("steamID64").Value),
Name = profileElement.Element("steamID").Value,
Avatar = new Uri(profileElement.Element("avatarIcon").Value, UriKind.Absolute),
};
XDocument gamesDocument = XDocument.Load(string.Format(SteamCommunityProfileGamesUriFormat, id));
List<OwnedGame> games = new List<OwnedGame>();
foreach (XElement gameElement in gamesDocument.XPathSelectElements("gamesList/games/game"))
{
OwnedGame game = new OwnedGame()
{
Id = int.Parse(gameElement.Element("appID").Value),
Name = gameElement.Element("name").Value,
Logo = new Uri(gameElement.Element("logo").Value, UriKind.Absolute),
User = user
};
XElement statsLinkElement = gameElement.Element("statsLink");
if (statsLinkElement != null)
{
try
{
XDocument statsDocument = XDocument.Load(statsLinkElement.Value + "?xml=1");
game.Stats = new Stats()
{
Game = game,
Achievements = statsDocument
.XPathSelectElements("playerstats/achievements/achievement")
.Where(achievementElement => achievementElement.Attribute("closed").Value == "1")
.Select(achievementElement => new Achievement()
{
Name = achievementElement.Element("name").Value,
Description = achievementElement.Element("description").Value,
Icon = new Uri(achievementElement.Element("iconClosed").Value, UriKind.Absolute)
})
};
}
catch (WebException)
{
continue;
}
}
games.Add(game);
}
user.Games = games.AsReadOnly();
return user;
}
}
Example usage:
long id = ...
User user = User.Load(id);
int totalAchievementCount = user.Games.Where(game => game.Stats != null).SelectMany(game => game.Stats.Achievements).Count();
Let me hope there are not too much typos in there :-)
Note:
- As stated in the Steam Web API Terms of Use "you are limited to one hundred thousand (100,000) calls to the Steam Web API per day."
- All HTTP responses of steamcommunity.com have a
Cache-Control: no-cache
Header.
- The XML API is "old" and was meant to be fully replaced. That means: XML-Data may be invalid and not available for all resources. (That's currently the case for Team Fortress 2 Stats)
- You don't need an API-Key as it's the case for the "new" API.