1

I need to fetch a particular element in HTML that has the following pattern: (C#)

<td class="blah" ...........>Some text blah: page x of xx<br>

I need to get the value of xx.

The only thing that is constant in the above pattern is:

  1. its a TD element
  2. it has class="blah" in it
  3. it has the text pattern ": page x of xx

You can assume there is only 1 occurance of the above pattern.

mrblah
  • 99,669
  • 140
  • 310
  • 420
  • 1
    I hate to sound like a broken record, but STOP using regex to parse html! It doesn't work (robustly). Really! – Marc Gravell Dec 09 '09 at 20:58
  • Admittedly, it does work in very limited circumstances (e.g. when you're scraping the contents of a website with a well-defined and relatively simple design that is unlikely to change often) - in those cases using regex (with understanding that you need to fix it every now and then) is often a cheap solution to the problem at hand. Generally, though, you're definitely correct, and in this case it's better to be overzealous. Especially now that HTML Agility Pack is available for use in proprietary closed-source code... – Pavel Minaev Dec 09 '09 at 21:02

4 Answers4

7

Please don't use regexes to parse HTML!

Grab a copy of the HTML agility pack and your life will be much simpler, and your application much less brittle.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jerod Venema
  • 44,124
  • 5
  • 66
  • 109
4

Using regex is not the correct way to do this. As others have pointed out, use an HTML parser. If you have HTML Agility Pack, you can do this:

using System;
using System.Linq;
using System.Text.RegularExpressions;
using HtmlAgilityPack;

class Program
{
    static void Main(string[] args)
    {
        string html = @"<html><body><td class=""blah"" ...........>Some text blah: page 13 of 99<br> more stuff</td></body></html>";
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(html);
        var nodes = doc.DocumentNode.SelectNodes("//td[@class='blah']");
        if (nodes != null)
        {
            var td = nodes.FirstOrDefault();
            if (td != null)
            {
                Match match = Regex.Match(td.InnerText, @"page \d+ of (\d+)");
                if (match.Success)
                {
                    Console.WriteLine(match.Groups[1].Value);
                }
            }
        }
    }
}

Output:

99

However, it can be done with regex, as long as you accept that it's not going to be a perfect solution. It's fragile, and can easily be tricked, but here it is:

class Program
{
    static void Main(string[] args)
    {
        string s = @"stuff <td class=""blah"" ...........>Some text blah: page 13 of 99<br> more stuff";
        Match match = Regex.Match(s, @"<td[^>]*\sclass=""blah""[^>]*>[^<]*page \d+ of (\d+)<br>");

        if (match.Success)
        {
            Console.WriteLine(match.Groups[1].Value);
        }
    }
}

Output:

99

Just make sure no-one ever sees you do this.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
0

Is it arbitrary HTML? Can it have CDATA blocks, comments, external character entities?

If any the above is true, then you should forget about regex for this purpose, and use something like HTML Agility Pack to properly parse it to DOM, and then work with that.

Pavel Minaev
  • 99,783
  • 25
  • 219
  • 289
0

Use a parser to get the content from the particular TD you care about, then use a regex along the lines of \d of (\d{2})$ which should get the value of xx in the first capture group.

I'm specifically not trying to write a regex that will handle the HTML part of this question; see also the <center> cannot hold.

Community
  • 1
  • 1
Hank Gay
  • 70,339
  • 36
  • 160
  • 222