-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
768898b
commit 5de3161
Showing
14 changed files
with
582 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
############################################################################### | ||
# Set default behavior to automatically normalize line endings. | ||
############################################################################### | ||
* text=auto | ||
|
||
############################################################################### | ||
# Set default behavior for command prompt diff. | ||
# | ||
# This is need for earlier builds of msysgit that does not have it on by | ||
# default for csharp files. | ||
# Note: This is only used by command line | ||
############################################################################### | ||
#*.cs diff=csharp | ||
|
||
############################################################################### | ||
# Set the merge driver for project and solution files | ||
# | ||
# Merging from the command prompt will add diff markers to the files if there | ||
# are conflicts (Merging from VS is not affected by the settings below, in VS | ||
# the diff markers are never inserted). Diff markers may cause the following | ||
# file extensions to fail to load in VS. An alternative would be to treat | ||
# these files as binary and thus will always conflict and require user | ||
# intervention with every merge. To do so, just uncomment the entries below | ||
############################################################################### | ||
#*.sln merge=binary | ||
#*.csproj merge=binary | ||
#*.vbproj merge=binary | ||
#*.vcxproj merge=binary | ||
#*.vcproj merge=binary | ||
#*.dbproj merge=binary | ||
#*.fsproj merge=binary | ||
#*.lsproj merge=binary | ||
#*.wixproj merge=binary | ||
#*.modelproj merge=binary | ||
#*.sqlproj merge=binary | ||
#*.wwaproj merge=binary | ||
|
||
############################################################################### | ||
# behavior for image files | ||
# | ||
# image files are treated as binary by default. | ||
############################################################################### | ||
#*.jpg binary | ||
#*.png binary | ||
#*.gif binary | ||
|
||
############################################################################### | ||
# diff behavior for common document formats | ||
# | ||
# Convert binary document formats to text before diffing them. This feature | ||
# is only available from the command line. Turn it on by uncommenting the | ||
# entries below. | ||
############################################################################### | ||
#*.doc diff=astextplain | ||
#*.DOC diff=astextplain | ||
#*.docx diff=astextplain | ||
#*.DOCX diff=astextplain | ||
#*.dot diff=astextplain | ||
#*.DOT diff=astextplain | ||
#*.pdf diff=astextplain | ||
#*.PDF diff=astextplain | ||
#*.rtf diff=astextplain | ||
#*.RTF diff=astextplain |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio 14 | ||
VisualStudioVersion = 14.0.23107.0 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamPageParser", "SteamPageParser\SteamPageParser.csproj", "{4903B432-A0F7-4E1A-9068-2560E29BAE7C}" | ||
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamPageParserRunner", "SteamPageParserRunner\SteamPageParserRunner.csproj", "{7F4EBFF3-90A6-4A5D-9BDA-DBE238A64A31}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{4903B432-A0F7-4E1A-9068-2560E29BAE7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{4903B432-A0F7-4E1A-9068-2560E29BAE7C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{4903B432-A0F7-4E1A-9068-2560E29BAE7C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{4903B432-A0F7-4E1A-9068-2560E29BAE7C}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{7F4EBFF3-90A6-4A5D-9BDA-DBE238A64A31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{7F4EBFF3-90A6-4A5D-9BDA-DBE238A64A31}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{7F4EBFF3-90A6-4A5D-9BDA-DBE238A64A31}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{7F4EBFF3-90A6-4A5D-9BDA-DBE238A64A31}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
namespace SteamPageParser | ||
{ | ||
public class AppPackage | ||
{ | ||
private AppPackage(SteamApp associatedSteamApp) | ||
{ | ||
AssociatedSteamApp = associatedSteamApp; | ||
} | ||
|
||
public string Title { get; internal set; } | ||
public decimal OriginalPrice { get; internal set; } | ||
public decimal CurrentPrice { get; internal set; } | ||
|
||
public decimal CurrentPricePercentage => CurrentPrice/OriginalPrice; | ||
public decimal DiscountPercentage => 1 - CurrentPricePercentage; | ||
|
||
public SteamApp AssociatedSteamApp { get; } | ||
|
||
public static AppPackage NewAppPackage(SteamApp associatedSteamApp) => new AppPackage(associatedSteamApp); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
|
||
namespace SteamPageParser | ||
{ | ||
public class InvalidAppException : Exception | ||
{ | ||
public InvalidAppException(int appId) | ||
{ | ||
AppId = appId; | ||
} | ||
|
||
public int AppId { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace SteamPageParser | ||
{ | ||
public static class Navigator | ||
{ | ||
public static string SteamAppUrl => @"http://store.steampowered.com/app/"; | ||
|
||
private static int MaximumAppId => 1000000; | ||
private static int MininumAppId => 0; | ||
private static int MaximumCoresToOccupy => 5; | ||
|
||
public static ParallelQuery<SteamApp> GetSteamApps() | ||
{ | ||
var appIdsToRun = new List<int>(); | ||
|
||
for (var i = MininumAppId; i < MaximumAppId; i++) | ||
{ | ||
appIdsToRun.Add(i); | ||
} | ||
|
||
return appIdsToRun | ||
.AsParallel() | ||
.WithDegreeOfParallelism(MaximumCoresToOccupy) | ||
.Select(async appId => await GetSteamApp(appId)) | ||
.Select(sa => sa.Result) | ||
.Where(sa => sa != null); | ||
} | ||
|
||
public static async Task<SteamApp> GetSteamApp(int appId) | ||
{ | ||
SteamApp app = null; | ||
|
||
await Task.Run(() => | ||
{ | ||
try | ||
{ | ||
var response = GetResponse(appId); | ||
var html = GetHtml(appId, response); | ||
app = Parser.ParsePage(appId, html); | ||
} | ||
catch (Exception exception) | ||
{ | ||
Debug.WriteLine(exception.Message); | ||
} | ||
}); | ||
|
||
return app; | ||
} | ||
|
||
private static string GetHtml(int appId, HttpWebResponse response) | ||
{ | ||
if (response.StatusCode != HttpStatusCode.OK) throw new InvalidAppException(appId); | ||
|
||
using (var receiveStream = response.GetResponseStream()) | ||
{ | ||
if (receiveStream == null) throw new InvalidAppException(appId); | ||
|
||
var readStream = response.CharacterSet == null | ||
? new StreamReader(receiveStream) | ||
: new StreamReader(receiveStream, Encoding.GetEncoding(response.CharacterSet)); | ||
|
||
var data = readStream.ReadToEnd(); | ||
|
||
response.Close(); | ||
readStream.Close(); | ||
|
||
return data; | ||
} | ||
} | ||
|
||
private static HttpWebResponse GetResponse(int appId) | ||
{ | ||
var request = (HttpWebRequest) WebRequest.Create($"{SteamAppUrl}{appId}/"); | ||
|
||
return (HttpWebResponse) request.GetResponse(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using System; | ||
using System.Linq; | ||
using HtmlAgilityPack; | ||
|
||
namespace SteamPageParser | ||
{ | ||
public static class Parser | ||
{ | ||
private static string AppTitleClass => "apphub_AppName"; | ||
private static string PackageClass => "game_area_purchase_game_wrapper"; | ||
private static string PackagePriceXPath => "game_purchase_price price"; | ||
private static string PackageOriginalPriceXPath => "discount_original_price"; | ||
private static string PackageDiscountPriceXPath => "discount_final_price"; | ||
private static string PackageTitle => "h1"; | ||
private static string ThousandsSeparator => ","; | ||
private static string CurrencySymbol => "$"; | ||
|
||
public static SteamApp ParsePage(int appId, string html) | ||
{ | ||
if (string.IsNullOrWhiteSpace(html)) throw new ArgumentNullException(nameof(html)); | ||
|
||
try | ||
{ | ||
var app = SteamApp.NewSteamApp(appId, html); | ||
|
||
var htmlDocument = new HtmlDocument(); | ||
|
||
var htmlCleaned = html.Replace("\"", "'"); | ||
|
||
htmlDocument.LoadHtml(htmlCleaned); | ||
|
||
var documentNode = htmlDocument.DocumentNode; | ||
|
||
var titleNode = documentNode.SelectSingleNode($"//div[@class='{AppTitleClass}']"); | ||
|
||
app.Title = titleNode.InnerHtml.Trim(); | ||
|
||
var packageNodes = documentNode.SelectNodes($"//div[@class='{PackageClass}']").ToArray(); | ||
|
||
foreach (var packageNode in packageNodes) | ||
{ | ||
AddPackage(app, packageNode); | ||
} | ||
|
||
return app; | ||
} | ||
catch (Exception) | ||
{ | ||
throw new InvalidAppException(appId); | ||
} | ||
} | ||
|
||
private static void AddPackage(SteamApp app, HtmlNode packageNode) | ||
{ | ||
var package = app.AddNewPackage(); | ||
|
||
var packageTitleNode = packageNode.SelectSingleNode($"//{PackageTitle}"); | ||
|
||
package.Title = packageTitleNode.InnerHtml.Replace("Buy ", "").Trim(); | ||
|
||
var priceNodes = packageNode.SelectNodes($"//div[@class='{PackagePriceXPath}']"); | ||
|
||
if (priceNodes != null) | ||
{ | ||
var priceNode = priceNodes[0]; | ||
|
||
package.CurrentPrice = ParseNodeWithCurrencyToDecimal(priceNode); | ||
|
||
package.OriginalPrice = package.CurrentPrice; | ||
} | ||
else | ||
{ | ||
var originalPriceNode = packageNode.SelectSingleNode($"//div[@class='{PackageOriginalPriceXPath}']"); | ||
|
||
package.OriginalPrice = ParseNodeWithCurrencyToDecimal(originalPriceNode); | ||
|
||
var discountPriceNode = packageNode.SelectSingleNode($"//div[@class='{PackageDiscountPriceXPath}']"); | ||
|
||
package.CurrentPrice = ParseNodeWithCurrencyToDecimal(discountPriceNode); | ||
} | ||
} | ||
|
||
private static decimal ParseNodeWithCurrencyToDecimal(HtmlNode node) | ||
{ | ||
var stringValue = node.InnerHtml.Replace(CurrencySymbol, "").Replace(ThousandsSeparator, ""); | ||
|
||
return decimal.Parse(stringValue); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("SteamPageParser")] | ||
[assembly: AssemblyDescription("")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("")] | ||
[assembly: AssemblyProduct("SteamPageParser")] | ||
[assembly: AssemblyCopyright("Copyright © 2015")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Setting ComVisible to false makes the types in this assembly not visible | ||
// to COM components. If you need to access a type in this assembly from | ||
// COM, set the ComVisible attribute to true on that type. | ||
[assembly: ComVisible(false)] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("4903b432-a0f7-4e1a-9068-2560e29bae7c")] | ||
|
||
// Version information for an assembly consists of the following four values: | ||
// | ||
// Major Version | ||
// Minor Version | ||
// Build Number | ||
// Revision | ||
// | ||
// You can specify all the values or you can default the Build and Revision Numbers | ||
// by using the '*' as shown below: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace SteamPageParser | ||
{ | ||
public class SteamApp | ||
{ | ||
private SteamApp(int appId, string html) | ||
{ | ||
AppId = appId; | ||
Html = html; | ||
|
||
PackageList = new List<AppPackage>(); | ||
} | ||
|
||
public int AppId { get; } | ||
public string Html { get; } | ||
public string Title { get; internal set; } | ||
|
||
private List<AppPackage> PackageList { get; } | ||
|
||
public AppPackage[] Packages => PackageList.ToArray(); | ||
|
||
internal AppPackage AddNewPackage() | ||
{ | ||
var newPackage = AppPackage.NewAppPackage(this); | ||
|
||
PackageList.Add(newPackage); | ||
|
||
return newPackage; | ||
} | ||
|
||
public static SteamApp NewSteamApp(int appId, string html) => new SteamApp(appId, html); | ||
} | ||
} |
Oops, something went wrong.