したがって、問題は、XPath 内の位置に依存していることです。場合によってはこれで問題ありませんが、指定された a の最初 の aがクラスに a を持つことを期待しているため、ここではそうではありません。td
tr
div
Chrome のソースを見ると、これが常に当てはまるとは限らないことがわかります。これは、カレンダーの「1」要素を「2」および「3」と比較するとわかります。「1」要素の周りには多くの要素があり、他の要素にはありません。
元の XPath クエリは要素を返さないため、エラーが発生しています。HtmlAgilityPack に指定した XPath クエリが DOM 要素にならない場合は、null が返されます。
コード全体を表示していないため、このコードがどのように実行されているかわかりません。ただし、すべてのカレンダー アイテムをループしようとしていると思います。とにかく、これを行うには複数の方法がありますが、descendant
XPath セレクターを使用すると、すべてを一度に取得できることをお見せします。
//div[@class='kal']//table//descendant::div[@class='cipars']
これにより、すべてのカレンダー アイテム (つまり、1 ~ 30)が返されます。
tr
ただし、特定の行のすべてのアイテムを取得するには、それをクエリに貼り付けるだけです。
//div[@class='kal']//table//tr[3]/descendant::div[@class='cipars']
これは 2 から 8 を返します (予定表アイテムの 2 行目)。
特定のサイトをターゲットにするには、Web サイトのソース コードを推測する必要があります。すべての「cipars」には、クラスを持つ adiv
の祖先があるようです....だから、質問から「3」の値を取得するには:td
datums
//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']
うまくいけば、少なくとも問題を示すにはこれで十分です。
編集
XPath の問題はありますが、別の問題もあります。
このサイトは非常に奇妙に作成されています。カレンダーは奇妙な方法で読み込まれます。その URL にアクセスすると、Javascript が XML Web サービス (PHP で作成) を呼び出してカレンダーが作成されtable
、カレンダーに使用されるすべてが計算されます。
これは Javascript (クライアント側コード) であるため、HtmlAgilityPack はそれを実行しません。したがって、HtmlAgilityPack はテーブルを「見る」ことさえしません。したがって、それに対するクエリは「見つかりません」(null) として返されます。
これを回避する方法: 1) スクリプトを呼び出すツールを使用します。これは、ブラウザをロードすることを意味します。これに使用する優れたツールはSeleniumと呼ばれます。これは、サイトで使用されるすべてのスクリプトが実際に呼び出されることを意味するため、おそらく全体的な解決策として優れています。引き続き XPath を使用できるため、クエリは変更されません。
2 番目の方法は、ページと同じWeb サービスに要求を送信することです。これは基本的に、ページが取得しているのと同じHTML を取得し、それを HtmlAgilityPack で使用することです。どうやってそれを行うのですか?
C# を使用して、Web サービスにデータを簡単に POST することができます。使いやすさのために、この SO questionからコードを盗みました。これにより、ページと同じリクエストを送信して、同じ HTML を返すことができます。
したがって、いくつかの POST データを送信するには、次のようなメソッドを生成します.....
public static string SendPost(string url, string postData)
{
string webpageContent = string.Empty;
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = byteArray.Length;
using (Stream webpageStream = webRequest.GetRequestStream())
{
webpageStream.Write(byteArray, 0, byteArray.Length);
}
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
{
using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
{
webpageContent = reader.ReadToEnd();
}
}
return webpageContent;
}
次のように呼び出すことができます。
string responseBody = SendPost("http://lekcijas.va.lv/lekcijas_request.php", "nodala=IT&kurss=1&gads=2013&menesis=9&c_dala=");
どうやってこれを手に入れたのですか?呼び出しているphp
ファイルはページの Web サービスであり、POST データも同様です。サービスに送信されるデータを見つける方法は、(Chrome の開発者コンソールを使用して) Javascript をデバッグすることですが、URL にあるものとほとんど同じであることにお気付きかもしれません。それは意図的なもののようです。
responseBody
返される は、カレンダーのだけの物理 HTMLです。table
今それで何をしますか?純粋な HTML を受け入れることができるため、それを HtmlAgilityPack にロードします。
var document = new HtmlDocument();
document.LoadHtml(webpageContent);
ここで、元の XPath を次のように貼り付けます。
var node = document.DocumentNode.SelectSingleNode("//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']");
ここで、うまくいけば「3」になるはずの値を出力します。
Console.WriteLine(node.InnerText);
ローカルで実行した私の出力は、確かに3です。
ただし、これで問題は解決しますが、サイトの残りの部分はこのようになっていると思います。この場合、上記の手法を使用して回避できる可能性がありますが、Selenium などのツールはまさにこの理由で作成されました。