how to make a for loop for xpath in C#? - c#

I'm trying to make a for loop to get all the data in the div, it did worked with one but didn't for the other
my code
if(websearch != mainSearchUrl) {
var webGet = new HtmlWeb();
var doc = webGet.Load(websearch);
var webnode = doc.DocumentNode.SelectNodes("/html/body/div/div[1]/div/div[2]");
foreach (HtmlNode node in webnode)
{
for (int i = 1; i < 15; i++)
{
var title = node.SelectSingleNode("/html/body/div/div[1]/div/div[2]/div["+i+"]/div/a");
var chapters = node.SelectSingleNode("/html/body/div/div[1]/div/div[2]/div[1]/div/div[4]"); //here is the error when i put "i" instead of the pre last number it results null
comboBox1.Items.Add(title.InnerText + chapters.InnerText);// error chapters null
}
}

Related

C# loop through two variables

I have 2 variables div1, div2 and want to get all value from them.
I can loop through one variable with foreach, but it's possible to get both divs InnerHtml?
HtmlWeb web = new HtmlWeb();
HtmlAgilityPack.HtmlDocument doc = web.Load(url);
var div1 = doc.DocumentNode.SelectNodes("//div[contains(#class,'class1')]");
var div2 = doc.DocumentNode.SelectNodes("//div[contains(#class,'class2')]");
foreach (HtmlNode div in div1)
{
String text = div.InnerHtml;
Debug.WriteLine(text);
}
#mcjmzn, #Jonathan, and #Nenad answers are correct as far as printing all innerHtmls.
I'm guessing you want to print the first div1 innerHtml and then the first div2 innerHtml, and then second div1 innerHtml, and second div2 innerHtml, and so on. You'll want a regular loop instead of a foreach, and add checks to make sure you don't exceed div1 or div2 array lengths:
var div1Max = div1.Count;
var div2Max = div2.Count;
var overallMax = Math.Max(div1Max, div2Max);
for(var i = 0; i < overallMax; i++)
{
if (i < div1Max)
{
String text1 = div1[i].InnerHtml;
Debug.WriteLine(text1);
}
if (i < div2Max)
{
String text2 = div2[i].InnerHtml;
Debug.WriteLine(text2);
}
}
You can use Concat extension method of IEnumerable to combine both collections of nodes.
HtmlWeb web = new HtmlWeb();
HtmlAgilityPack.HtmlDocument doc = web.Load(url);
var div1 = doc.DocumentNode.SelectNodes("//div[contains(#class,'class1')]");
var div2 = doc.DocumentNode.SelectNodes("//div[contains(#class,'class2')]");
var allNodes = div1.Concat(div2);
foreach (HtmlNode div in allNodes)
{
String text = div.InnerHtml;
Debug.WriteLine(text);
}
Why don't you simply iterate one after another, instead of concatenating, etc?
foreach (HtmlNode div in div1)
{
String text = div.InnerHtml;
Debug.WriteLine(text);
}
foreach (HtmlNode div in div2)
{
String text = div.InnerHtml;
Debug.WriteLine(text);
}
Create a container list at the beginning, add the results to it, and then loop through the container list:
var nodes = new HtmlNodeCollection();
nodes.Add(doc.DocumentNode.SelectNodes("//div[contains(#class,'class1')]"));
nodes.Add(doc.DocumentNode.SelectNodes("//div[contains(#class,'class2')]"));
foreach(HtmlNode node in nodes){
Debug.WriteLine(node.InnerHtml);
}
It is also possible to build up a different query that will get all the class1s and class2s at the same time:
doc.DocumentNode.SelectNodes("//div[contains(#class,'class1') or contains(#class,'class2')]");
Edit after comment # 22:24:56Z:
If there is only one result for each selector, you could simplify your approach something like this:
var text1 = doc.DocumentNode.SelectSingleNode("//div[contains(#class,'class1')]")?.InnerHtml ?? String.Empty;
var text2 = doc.DocumentNode.SelectSingleNode("//div[contains(#class,'class2')]")?.InnerHtml ?? String.Empty;
Those question marks are null-coalescing operators. See:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator

How I can get data from multiple pages with HTML Agility Pack

Hello I've got a problem with Agility pack in C#. Maybe I don't see somethink what I'm doing wrong.
I want to get movies from multiple pages but when I run my app then getting everythink from 1st page and repeating that n - times (n it's a number what I give). For exaple 10 titles from page is written x4 times in loop
bool looping = true;
string mainUrl = "https://www.filmweb.pl/films/search";
HtmlWeb web = new HtmlWeb();
HtmlDocument docu = web.Load(mainUrl);
int inc = 0;
var tags = docu.DocumentNode.SelectNodes("//h2[#class='filmPreview__title']");
while(looping)
{
var nextPage = docu.DocumentNode.SelectNodes("//a[#title='następna']/#href").ToList();
if(inc < 4)
{
string link = mainUrl + nextPage[0].Attributes["href"].Value;
var urlDecode = HttpUtility.HtmlDecode(link);
docu = web.Load(urlDecode);
foreach (var item in tags)
{
Movie mv = new Movie();
mv.Tytul = item.InnerText;
tytuly.Add(mv);
}
inc++;
}
else
{
looping = false;
}
}
And below my view code
#for (int i = 0; i < Model.Count; i++)
{
<p>#Model[i].Tytul</p>
}
I tried with different loops and everytime was same situation. Can you help me? I think I don't see my mistakes
Thank you in advance!
There are some logic problem in your codes. You get the films' titles by looping the tags, while the tags are always from the first page, you have not overridden it when you get the new page. I made some changes to your codes, and got the right results
bool looping = true;
string mainUrl = "https://www.filmweb.pl/films/search";
HtmlWeb web = new HtmlWeb();
HtmlDocument docu = web.Load(mainUrl);
int inc = 0;
var tags = docu.DocumentNode.SelectNodes("//h2[#class='filmPreview__title']");
while (looping)
{
if (inc < 4)
{
foreach (var item in tags)
{
Movie mv = new Movie();
mv.Tytul = item.InnerText;
tytuly.Add(mv);
}
var nextPage = docu.DocumentNode.SelectNodes("//a[#title='następna']/#href").ToList();
string link = mainUrl + nextPage[0].Attributes["href"].Value;
var urlDecode = HttpUtility.HtmlDecode(link);
docu = web.Load(urlDecode);
tags = docu.DocumentNode.SelectNodes("//h2[#class='filmPreview__title']");
inc++;
}
else
{
looping = false;
}
}

Html Agility Pack xpath IEnumerable

I can not add html code, because it is very very big! 5 scrolls or more. Please, follow link in htmlWeb.load().
I look at this code already 2 hours and I can not figure out what is wrong.
HtmlWeb htmlWeb = new HtmlWeb {OverrideEncoding = Encoding.Default};
HtmlAgilityPack.HtmlDocument document = htmlWeb.Load("https://www.parimatch.com/en/sport/futbol/germanija-bundesliga");
var matches = document.DocumentNode.SelectNodes("//tr[#class='bk']").
Select(tr => new FootballMatch()
{
Number = string.Join(" ", tr.SelectNodes("./td[1]//text()[normalize-space()]").Select(t =>t.InnerText)),
Time = string.Join(" ", tr.SelectNodes("./td[2]//text()[normalize-space()]").Select(t => t.InnerText)),
Teams = string.Join(" ", tr.SelectNodes("./td[3]//text()[normalize-space()]").Select(t => t.InnerText)),
Allowance = string.Join(" ", tr.SelectNodes("./td[4]//text()[normalize-space()]").Select(t => t.InnerText)),
CoefficientAllowance = string.Join(" ", tr.SelectNodes("./td[5]//text()[normalize-space()]").Select(t => t.InnerText)),
Total = tr.SelectSingleNode("./td[7]//text()[normalize-space()]").InnerText,
P1 = tr.SelectSingleNode("./td[10]//text()[normalize-space()]").InnerText,
X = tr.SelectSingleNode("./td[11]//text()[normalize-space()]").InnerText,
/*P2 = tr.SelectSingleNode("./td[12]//text()[normalize-space()]").InnerText,
P1X = tr.SelectSingleNode("./td[13]//text()[normalize-space()]").InnerText,
P1P2 = tr.SelectSingleNode("./td[14]//text()[normalize-space()]").InnerText,
P2X = tr.SelectSingleNode("./td[15]//text()[normalize-space()]").InnerText*/
});
P2,P1X,P1P2,P2X always null.
and it is possible to do this code more neater?
When you click on an event , a popup menu appears , this data is read too , but I do not need this . How can I disable this ?
This is also not the prettiest. But it works. Still some work needs to be done in respect to sperating certain cells. Since some <td>s contain <br> to separate lines. Hope this helps you moving on.
string xpath = "//tr[#class='bk']";
HtmlNodeCollection matches = htmlDoc.DocumentNode.SelectNodes(xpath);
List<List<string>> footballMatches = new List<List<string>>();
foreach (HtmlNode x in matches)
{
List<string> mess = new List<string>();
HtmlNodeCollection hTC = x.SelectNodes("./td");
if (hTC.Count > 15)
{
for (int i = 0; i < 15; i++)
{
if (i != 5)
{
mess.Add(hTC[i].InnerText);
}
}
}
footballMatches.Add(mess);
}

looping through same multiple nodes in an xml and merging numbers that are strings

I am having an issue where i am looping through multiple of the same nodes in an xml bill. As i was looping through the values and merging them accordingly I didnt realize that the datatype was a string. I need some help merging/summing up numbers that are strings. the loop contains all of the values included in the node but i am only showing the portion i need help with.
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNodeList detailPageNodes)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
foreach (XmlNode detailPageNode in detailPageNodes)
{
if (totalSvcUseXMLNodes.Count > 0 && totalSvcUseXMLNodes[0].HasChildNodes)
{
var totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGP_SA_TOTAL_KWH.SERVICE_ACCOUNT_STATEMENT");
if (totalSvcNode == null)
{
totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGU_US_KWH_USAGE.USAGE");
}
saBillDetail.TotalServiceUsage = totalSvcNode.InnerText;
}
}
}
This is how i had it and i figured the datatype was a string. this way just concatenates the values together.
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNodeList detailPageNodes)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
foreach (XmlNode detailPageNode in detailPageNodes)
{
if (totalSvcUseXMLNodes.Count > 0 && totalSvcUseXMLNodes[0].HasChildNodes)
{
var totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGP_SA_TOTAL_KWH.SERVICE_ACCOUNT_STATEMENT");
if (totalSvcNode == null)
{
totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGU_US_KWH_USAGE.USAGE");
}
saBillDetail.TotalServiceUsage += totalSvcNode.InnerText;
}
}
}
then i tried something like this where i am using int.Parse and adding the result back to result. But i am getting an error with the second result in the result = result + int.Parse(totalSvcNode.InnerText); saying it is unassigned variable
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNodeList detailPageNodes)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
foreach (XmlNode detailPageNode in detailPageNodes)
{
if (totalSvcUseXMLNodes.Count > 0 && totalSvcUseXMLNodes[0].HasChildNodes)
{
var totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGP_SA_TOTAL_KWH.SERVICE_ACCOUNT_STATEMENT");
if (totalSvcNode == null)
{
totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGU_US_KWH_USAGE.USAGE");
}
int result;
//saBillDetail.TotalServiceUsage += totalSvcNode.InnerText;
result = result + int.Parse(totalSvcNode.InnerText);
saBillDetail.TotalServiceUsage = result.ToString();
}
}
}
I am stuck with the logic since its not two different TotalServiceUsage i am adding up together. Its the same TotalServiceUsage added up for every node found. I am a pretty new programmer and any help would be appreciated.
In your last example int result needs to be initialized outside the for loop:
int result =0;
If TotalServiceUsage is an int you can just use:
saBillDetail.TotalServiceUsage += int.Parse(totalSvcNode.InnerText);

c# razor error: The name 'i' does not exist in this current context

I am pulling a list of data from the web and placing my array into my drop down menu. I am using razor script and I get the following error on LINE 4:
The name 'i' does not exist in this current context
Here is my code:
var select = document.getElementById('propertyMenu');
for (var i = 0; i < #Model.propertiesArray.Length; i++)
{
var property = "#Model.propertiesArray[i]"
var element = document.createElement('option');
element.textContent = property;
element.value = property;
}
Now I know that using the '#' symbol allows you to access classes from my models within my javascript via C#. So my guess is that the Javascript variable 'i' won't be accessible unless I give it some sort of directive.
How do I access my int count 'i' within the context?
EDIT:
I answered my own question.
var propertiesArray = new Array();
#foreach (var props in Model.propertiesArray)
{
#:propertiesArray.push("#props");
}
var select = document.getElementById("propertyMenu");
for (var i = 0; i < propertiesArray.length; i++)
{
var property = propertiesArray[i];
var element = document.createElement('option');
element.textContent = property;
element.value = property;
select.appendChild(element);
}
can not tell if your code is C# or JS.
You may have to do it this way
var select = document.getElementById('propertyMenu');
for (var i = 0; i < #Model.propertiesArray.Length; i++)
{
var property = #Model.propertiesArray[i].toString();
var element = document.createElement('option');
element.textContent = property;
element.value = property;
}
because you are using in Javascript codes. You can try this.
#{
string values = "";
for(int i=0;i<Model.propertiesArray.Length;i++)
values += Model.propertiesArray[i].ToString()+(i!=Model.propertiesArray.Lenght-1)?",":"";
}
<script>
var value = '#values';
var values = value.split(',');
var select = document.getElementById('propertyMenu');
for (var i = 0; i < values .length; i++)
{
var property = values[i];
var element = document.createElement('option');
element.textContent = property;
element.value = property;
}
</script>
I wrote Stackoverflow editor.

Categories