4
5

More than 5 years have passed since last update.

C#でHTMLパーサを作ろうとする

Posted at

ちょっと必要になったので、ここの記事を参考にしました。
随分簡易的な物になってしまいました。
投稿用に改めて修正したのですが、目的の情報(ここから遅延情報の取得)が取れた事以外確認してないです。その為、しっかりとした検証が出来てないです。参考程度にしてください

ちょっと説明

  • pythonのHTMLparserクラスのような使い勝手の物を目指しました。
  • タグ、属性、データそれぞれが読みこまれた時に各々のメソッドが呼ばれます。
  • 属性値はディクショナリで渡されます。
  • コメント、空要素はタグとして認識しないようにしています。

コード

paser.cs
class parser
{
    private string html_data;
    private int data_index = 0;
    private int reverse_index = 0;
    private string html_tag;
    private string attr_name;
    private string tag_attr;
    private string main_data;
    private char delim;
    private Dictionary<string, string> attribute_list = new Dictionary<string, string>();
    private string[] empty_tag_list = new [] {"area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param"};

    //value reset
    public void reset()
    {
        html_data = "";
        data_index = 0;
        reverse_index = 0;
        html_tag = "";
        attr_name = "";
        tag_attr = "";
        main_data = "";
    }

    //check empty tag
    public bool empty_tag()
    {
        bool tag_flg = true;
        foreach (string n in empty_tag_list) 
        {
            if (n.ToLower() == html_tag) 
            {
                tag_flg = false;
            }
        }
        return tag_flg;
    }

    //check end tag
    public void end_tag_check()
    {
        string e_tag = "";
        int tmp_index = data_index;
        data_index++;
        if (html_data [data_index] == '/') 
        {
            data_index++;
            blank_remove ();
            while ((html_data [data_index] != '>') && (html_data [data_index] != ' ') && (!check_eof ())) 
            {
                e_tag += html_data [data_index];
                data_index++;
            }
        }

        //reverse index
        data_index = tmp_index;

        if (e_tag != html_tag) 
        {
            main_data += html_data [data_index];
            data_index++;
        }
    }


    //check end of file
    public bool check_eof()
    {
        if (data_index >= (html_data.Length - 1))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    //remove blank
    public void blank_remove()
    {
        while ((html_data[data_index] == ' ') && (!check_eof()))
        {
            data_index++;
        }

    }

    //check tag 
    public void check()
    {

        while ((html_data[data_index] != '<') && (!check_eof()))
        {
            data_index++;
            if ((html_data.Length - data_index) > 3)
            {
                if ((html_data[data_index] == '<')
                    && (html_data[data_index + 1] == '!')
                    && (html_data[data_index + 2] == '-')
                    && (html_data[data_index + 3] == '-'))
                {
                    while ((html_data[data_index] != '-')
                        && (html_data[data_index + 1] != '-')
                        && (html_data[data_index + 2] != '>'))
                    {
                        data_index++;
                    }
                    data_index += 3;
                }
            }

        }

        //end tag check
        if ((html_data.Length - data_index) > 1) {
            if (html_data[data_index + 1] == '/' || (html_data[data_index + 1] == '!'))
            {
                while ((html_data[data_index] != '>') && (!check_eof()))
                {
                    data_index++;
                }
                data_index++;
            }
        }
    }

    //get tag
    public void get_tag()
    {
        while ((html_data[data_index] != '>') && (html_data[data_index] != ' ') && (!check_eof()))
        {
            html_tag += html_data[data_index];
            data_index++;
        }
                //html_tag = html_tag.ToLower(); こっちの方がいいかもしれない
        find_tag(html_tag);
    }

    //get attribute
    public void get_attribute()
    {
        while( (html_data[data_index] != '>') && (html_data[data_index] != '/') && (!check_eof() ) )
        {
        while ((html_data[data_index] != '=') && (!check_eof()))
        {
            attr_name += html_data[data_index];
            data_index++;
        }

        data_index++;
        delim = html_data[data_index];
        data_index++;

        while ((html_data[data_index] != delim) && (!check_eof()))
        {
            tag_attr += html_data[data_index];
            data_index++;
        }
        attribute_list.Add(attr_name, tag_attr);
        attr_name = "";
        tag_attr = "";
            data_index++;
        blank_remove();
        }
        find_attribute(html_tag, attribute_list);
    }

    //get data
    public void get_data()
    {
        while ((html_data[data_index] != '<') &&(html_data[data_index+1] != '/') && (!check_eof()))
        {
            main_data += html_data[data_index];
            data_index++;

            if ((html_data[data_index] == '<'))
            {
                end_tag_check();
            }

        }
        find_data(html_tag, attribute_list, main_data);
    }

    //find tag
    public virtual void find_tag(string tag)
    {
    }

    //find atrribute
    public virtual void find_attribute(string tag, Dictionary<string, string> attr_list)
    {
    }

    //find data
    public virtual void find_data(string tag, Dictionary<string, string> attr_list, string data)
    {
    }

    public void parse()
    {
        while (!check_eof())
        {
            check();
            if (!check_eof())
            {
                if ((html_data[data_index] == '<') && (html_data[data_index+1] != '/'))
                {
                    data_index++;
                    blank_remove();
                    get_tag();
                    blank_remove();
                    if (html_data[data_index] != '>' && empty_tag())
                    {
                        get_attribute();
                        blank_remove();
                    }
                    if (html_data[data_index] == '>')
                    {
                        data_index++;
                        blank_remove();
                        if (html_data[data_index] != '<')
                        {
                            reverse_index = data_index;
                            get_data();
                            data_index = reverse_index;
                        }
                    }
                    if(html_data[data_index] == '/')
                    {
                        data_index++;
                    }
                }
                else if (html_data[data_index] == '>'  && empty_tag())
                {
                    data_index++;
                    reverse_index = data_index;
                    get_data();
                    data_index = reverse_index;
                }
            }
            attribute_list.Clear();
            html_tag = "";
            main_data = "";
        }
    }


    public string paga_data
    {
        get
        {
            return html_data;
        }
        set
        {
            reset();
            html_data = value;
        }
    }
}

実装方法

下記をオーバーライドしてください。

paser.cs
//~~~~~~~~~~
//find tag method
public virtual void find_tag(string tag)
{
}

//find atrribute 
public virtual void find_attribute(string tag, Dictionary<string, string> attr_list)
{
}

//find data
public virtual void find_data(string tag, Dictionary<string, string> attr_list, string data)
{
}
//~~~~~~~~~~

ここから遅延情報を取り出す例です。
呼び出し元でこんな感じに実装します。

main.cs

//~~~~~~~~~~

    //使いたいところで下記の"parse();"を呼び出す
    public void parse()
    {
        Dictionary<string, string> url_list = new Dictionary<string, string>();
        string page;
        parser1 ps = new parser1();
        page = GetPage("http://www.jikokuhyo.co.jp/search/detail/line_is/kanto_takasaki");
        ps.paga_data = page;
        ps.parse();
    }

    public static string GetPage(string url)
    {
        WebResponse response = null;
        Stream stream = null;
        StreamReader
        reader = null;
        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

            response = request.GetResponse();
            stream = response.GetResponseStream();

            if (!response.ContentType.ToLower().StartsWith("text/"))
                return null;

            string buffer = "", line = "";

            reader = new StreamReader(stream);

            while ((line = reader.ReadLine()) != null)
            {
                buffer += line + "\r\n";
            }

            return buffer;
        }
        catch (WebException e)
        {
            System.Console.WriteLine("Can't download:" + e);
            return null;
        }
        catch (IOException e)
        {
            System.Console.WriteLine("Can't download:" + e);
            return null;
        }
        finally
        {
            if (reader != null)
                reader.Close();

            if (stream != null)
                stream.Close();

            if (response != null)
                response.Close();
        }
    }

//~~~~~~~~~~

class parser1 : parser
{
    private bool state;

    private string comment;

    public parser1()
    {
        state = true;
    }

    public bool status
    {
        get
        {
            return this.state;
        }
        set
        {
            this.state = value;
        }

    }

    public string m_comment
    {
        set
        {
            this.comment = value;
        }
        get
        {
            return this.comment;
        }
    }

    //find tag
    public override void find_tag(string tag)
    {
    }

    //find atrribute
    public override void find_attribute(string tag, Dictionary<string, string> attr_list)
    {
    }

    //find data
    public override void find_data(string tag, Dictionary<string, string> attr_list, string data)
    {
        if (tag == "div")
        {
            foreach (KeyValuePair<string, string> sPair in attr_list)
            {
                if (sPair.Key == "class" && sPair.Value == "corner_block_row_detail_d")
                {
                    if ( (data != "\r\n現在、平常通り運転しています。") && (data !="\r\n情報提供時間は4:00~翌2:00となっています。") )
                    {
                        //なにかする
                        m_comment = data;
                        status = false;

                    }
                    else
                    {
                    }
                }
            }

        }
    }
}
//~~~~~~~~~~

後々役に立つと思ったけど、今のところ遅延情報の取得以外に使ってないです。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5