Showing posts with label NuGet. Show all posts
Showing posts with label NuGet. Show all posts

Monday, 3 September 2012

Screen-Scraping in C# using LINQPad and HTML Agility Pack

I am a big fan of LINQPad for prototyping small bits of code, but every now and then you find you need to reference another library. LINQPad, does allow this by editing the Query Properties and adding a reference to a dll in the GAC or at a specific path. But the new LINQPad beta release makes life even easier by allowing you to reference NuGet packages.

I recently wanted to do a simple bit of screen-scraping, to extract the results from a web page containing football scores. By examining the HTML with FireBug I was quickly able to see that each month’s fixtures were in a series of tables each with a header row and then one row per result. The HTML looked something like this:

<table class="fixtures" width="502" border="0">
<tbody>
    <tr class="fixture-header">
        <th class="first" scope="col" colspan="6">November</th>
        <th class="goals-for" title="Goals For" scope="col">F</th>
        <th class="goals-against" title="Goals Against" scope="col">A</th>
        <th class="tv-channel" scope="col">
        <th class="last" scope="col"> </th>
    </tr>
    <tr class="home">
        <td class="first ">04</td>
        <td class="month"> Wed </td>
        <td class="fixture-icon">
        <td class="competition">UEFA Champions League</td>
        <td class="home-away">H</td>
        <td class="bold opposition ">
        <td class="goals-for"> 4 </td>
        <td class="goals-against"> 1 </td>
        <td class="tv-channel"> </td>
        <td class="menu-button" valign="middle">
    </tr>

To be able to navigate around HTML in .NET, by far the best library I have found is the HTML Agility Pack. Adding this to your LINQPad Query is very simple with the new beta. Press F4 to bring up Query Properties, then click Add NuGet, find the Html Agility Pack in the list and click Add To Query.

Now we are ready to load the document and find all the tables with the class of “fixtures”. You can use a special XPath syntax to do this in one step:

var web = new HtmlAgilityPack.HtmlWeb();
var doc = web.Load("http://www.arsenal.com/fixtures/fixtures-reports?season=2009-2010&x=11&y=15");
foreach(var fixturesTable in doc.DocumentNode.SelectNodes("//table[@class='fixtures']"))
{
    // ...
}

Having got each fixture table, I then ignore the top row (which has a class of “fixture-header”), and use the classes on each of the table columns to pull out the information I am interested in. Finally, I use the handy Dump extension method in LINQPad to output my information to the results window:

foreach(var fixture in fixturesTable.SelectNodes("tr"))
{
    var fixtureClass = fixture.Attributes["class"];
    // header rows have class of fixture-header
    if(fixtureClass == null || !fixtureClass.Value.Contains("fixture-header"))
    {
        var day = fixture.SelectSingleNode("td[@class='first ']").InnerText.Trim();
        var month = fixture.SelectSingleNode("td[@class='month']").InnerText.Trim();
        var venue = fixture.SelectSingleNode("td[@class='home-away']").InnerText.Trim();
        var oppositionNode = fixture.SelectNodes("td").FirstOrDefault(n => n.Attributes["class"].Value.Contains("opposition"));
        var opposition = oppositionNode.SelectSingleNode("a").InnerText.Trim();
        var matchReportUrl = oppositionNode.SelectSingleNode("a").Attributes["href"].Value.Trim();
        var goalsFor = fixture.SelectSingleNode("td[@class='goals-for']").InnerText.Trim();
        var goalsAgainst = fixture.SelectSingleNode("td[@class='goals-against']").InnerText.Trim();
        string.Format("{0} {1} {2} {3} {4}-{5}", day, month, venue, opposition, goalsFor, goalsAgainst).Dump();
    }
}

This gives me the data I am after, and from here it is easy to convert it into any other format I want such as XML or insert it into a database (something that LINQPad also makes very easy).

04 Sun H Blackburn Rovers 6-2
17 Sat H Birmingham City 3-1
20 Tue A AZ Alkmaar 1-1
25 Sun A West Ham United 2-2
28 Wed H Liverpool 2-1
31 Sat H Tottenham Hotspur 3-0
04 Wed H AZ Alkmaar 4-1
07 Sat A Wolverhampton W. 4-1

And the really nice thing about using LINQPad for this rather than creating a Visual Studio project is that the entire thing is stored in a single compact .linq file without all the extraneous noise of sln, csproj, AssemblyInfo files etc.

Saturday, 3 March 2012

NAudio Preview Versions

NAudio 1.5 was the first release of NAudio I managed to get up onto NuGet. Although the vast majority of NAudio users still continue to get their downloads from CodePlex, I’m hoping that NuGet will become the preferred means of adding it to a project, as it makes adding a reference very simple and allows easy upgrades to new versions.

Recently, NuGet added a feature that allows you to upload preview releases, by using semantic versioning such as NAudio 1.5.1-beta. Packages versioned like this won’t appear by default, but can be installed using the NuGet package manager console window using the –Pre switch on the Install-Package command.

Install-Package NAudio -Pre

The benefit of this is that it gives me a very quick and simple way of uploading preview versions of NAudio. This is useful for me, as I use NAudio in a lot of small projects, and often I want to use the features I have just added. The ability to push pre-release versions to NuGet is a big time-saver, and much quicker than creating a full release on CodePlex. So if you are looking for the latest build of NAudio, head over to NuGet. The NAudio feed page will show the latest version available (scroll to bottom for the full list).

Saturday, 28 May 2011

NAudio and NuGet (and the x64 problem)

Several people have contacted me recently asking when NAudio will appear on nuget.org. The short answer is, hopefully soon. For the longer answer, read on…

The need for a decent build process

To release NAudio, there is a bunch of stuff I have to do, such as remembering to run all the unit tests and manual tests on all the operating systems and soundcards I can get my hands on (which in truth is a pretty limited selection). I then have to create a bunch of zip files for the binaries, the demo apps, and the source code to go on CodePlex. Then there is the release notes. A NuGet package adds yet another thing to a process that I would rather not be spending my free time on.

The hassle of doing all this means I don’t release as often as I should. However, I have created an MSBuild script for NAudio now that should make it a lot easier for me to create releases. And yes, one of the things it now does is creates a nupkg for NAudio.

The need to decide on a version numbering strategy

The approach to versioning I have taken with NAudio is that there is a major and minor version, and then the third digit gets updated regularly (used to be every checkin, but I have got lazy). If I am to release NAudio to nuget, I need to decide now how I will version it. Ideally I just want the public releases to be called “1.4”, “1.5” etc. But when I do bugfixes, what should their package number be? It doesn’t seem right if the package is 1.4.1 but the NAudio.dll version is 1.4.272.0. There seems to be a mixture of choices taken by other libraries on nuget.org, but I guess the safest is simply to use all four numbers as the package version and eliminate any possible confusion.

The need for an updated NAudio 1.4

After releasing NAudio 1.4 RC a while ago, I spotted a couple of bugs that I would really like to fix before calling it NAudio 1.4 final. Since I suspect that NAudio will get a lot more downloads once it is available on nuget.org, I want to make people’s first experience as good as possible.

However, since I am well into writing the code for NAudio 1.5, I will need to create a branch to back-port the crucial fixes in. I’ve contacted CodePlex asking them to turn the NAudio repository into Mercurial so I can branch more easily. Hopefully once the conversion has taken place I can create my branch and start work towards an updated 1.4 release that can go on nuget. There will however be one outstanding issue remaining…

The need to decide on an approach to the x64 problem

The NAudio.dll is currently compiled with the platform target explicitly set to x86 . This means if your application is running as a 64 bit process it will fail when it tries to load NAudio. You need to mark your main project as x86 too for it to work.

There are two reasons for this. The main reason is that since my development environment is x86 (soon to change – I have ordered a new laptop and will finally enter the world of 64 bit), I cannot be sure that the interop all works properly in x64, even though I have heard reports that the vast majority of it is good to go on both architectures. However, until I am sure that all the interop (and there is a lot of interop in NAudio) is safe in Windows x64, it is easier for me to simply enforce running as a 32 bit process.

The second issue is that NAudio uses ACM codecs to perform its MP3 decoding, and I have heard rumours that when running in 64 bit windows, not all your ACM codecs appear. I will have a chance to test this for myself in the near future.

Going forwards, it may be easiest to unset the x86 only flag on the NAudio DLL and just strongly recommend to people that they set it on their calling executable. We could actually do this for people using nuget’s ability to use PowerShell to automate Visual Studio. But I would rather not fiddle with people’s project settings in this way.

Conclusion

I think NuGet is a brilliant development for the .NET open source community and I can’t wait to get NAudio on there. But it might be a few more weeks yet before I have sorted my own build and release processes out.

Tuesday, 26 October 2010

Change to Solution Folder in Package Manager Console

When you install nupack (which seems likely to be renamed nuget in the near future), you get a new dockable Visual Studio 2010 window called the Package Manager Console which allows you to run nupack commands right from within VS2010.

nuget-console

The great thing about this window is that it can be used for more than just nupack commands. It is a fully working PowerShell window and all the commands on your path are also available. For example, I use Mercurial on a number of my applications, so it allows me to input commands such as hg add or hg commit directly within the console window.

There was just one slight snag, and that is that the current working directory of the Package Manager Console seems to default to your user account:

PM> pwd
Path
----
C:\Users\Mark

So, despite knowing virtually nothing about PowerShell, I set about working out how I could automate this process. The first thing I discovered was that you can query PowerShell for all the variables that are available using the Get-Variable command. This will show the names and current values of all variables.

Sadly, there seemed to be none containing the path of the loaded solution, but asking a question on StackOverflow pointed me in the right direction. There is a variable called $dte which is a COM object allowing automation of the VS development environment. We can ask this for the path of the loaded solution:

PM> $dte.Solution.FileName
C:\Users\Mark\Code\TestApp\TestApp.sln

We now need to find a way to strip off the filename to get the folder. You can list all available PowerShell commands with by typing Get-Command. Eventually after some searching on Google I found a way to strip the filename off this path:

PM> Split-Path -parent $dte.Solution.FileName
C:\Users\Mark\Code\TestApp

To change to this folder you take this string and pipe it into the cd command as follows:

PM> Split-Path -parent $dte.Solution.FileName | cd
PM> pwd

Path
----
C:\Users\Mark\Code\TestApp

Mission almost accomplished, but that is a rather cumbersome command to remember. I wanted to make a PowerShell command immediately available to me. This requires editing my PowerShell user profile. The path to this is found in the $profile variable:

PM> $profile
C:\Users\Mark\Documents\WindowsPowerShell\NuPack_profile.ps1

This file didn’t actually exist (nor did the WindowsPowerShell folder), so I had to create a blank one. Then, I added my snippet of script into a function:

nuget-powershell

Having done that, you need to reload Visual Studio, and after loading a solution, you can type solutionFolder to navigate to the solution folder:

PM> solutionFolder
PM> pwd

Path
----
C:\Users\Mark\Code\TestApp

And that's it. Now I can run my Mercurial commands from within the Package Manager Console:

PM> hg status
M UnitTests\UnitTests.csproj
? UnitTests\Thread.cs