C# tekst van een website krijgen

Status
Niet open voor verdere reacties.
Omdat dit gewoon een port is van de silverlight app (wp8) naar een wp8.1 silverlight app, niet een 'echte' wp8.1 app. Je hebt dus bijna geen toegang tot de nieuwe dingen. De code blijft hetzelfde voor het grote deel maar je mist een hoop. Ook kun je er geen universal app van maken omdat je niet de winrt code deelt die een wp8.1 app deelt met w8.1.

En daarom heb je ook die extensions nodig om dingen async te doen en bijvoorbeel de toolkit die in wp8.1 voor zover ik heb gekeken volledig ingebakken zit. Veel dingen worden zelfs al standaard gedaan en hoef ik niet eens meer te doen.

Het voorbeeld een aantal posts terug is een universal app en de wp8.1 app is dus ook geen oude silverlight app. (kun je in de project naam in de solution explorer zien wat voor app het is)

Edit

Ow ik weet het al, het is nog steeds een silverlight app.
Ik dacht als het wordt geport naar 8.1 dan veranderd meteen panorama in een hub enzovoort.

Nee, daarom kun je ook het beste 'from scratch' beginnen zoals in dat voorbeeld. De app is nog niet af en is vrij klein dus perfect moment zou ik zeggen ;)
Veel UI dingen kun je nog gewoon bewaren zoals de data templates. Maar sommige controls zijn anders. (ben ik zelf ook nog aan het ontdekken)
 
Laatst bewerkt:
In je roosten app heb ik gewoon een nieuw project gemaakt en ben ik gaan copy/pasten. Stuk voor stuk en ben begonnen met bijvoorbeeld de model classes student/subject want die zijn 1:1 over te nemen.

En de method die classes/students ophaalt was een nieuwe regel nodig omdat de xpath vervangen moest worden.
 
Hier is bijvoorbeeld een verschil:

(debug eruit gehaald omdat het 100% werkt en zo mooier is

[CPP]
public async Task<List<string>> DownloadClassesAsync()
{
var url = string.Format("{0}?wijzigingen=1&type=Leerlingrooster&school=620", BaseUrl);

Debug.WriteLine("DownloadClassesAsync");
Debug.WriteLine("\turl:{0}", url);

var source = await DownloadPageSourceAsync(url);

var doc = new HtmlDocument();
doc.LoadHtml(source);

var classes = new List<string>();

var optionNodes = doc.DocumentNode.SelectNodes("//select[@name=\"afdeling\"]/option"); //<option>4gym</option> this is how the combobox content it looks in html source
foreach (var optionNode in optionNodes)
{
var className = optionNode.NextSibling.InnerText.Trim();

classes.Add(className);
}

Debug.WriteLine("Parsed {0} Classes", classes.Count);

return classes;
}[/CPP]



[CPP]public async Task<List<string>> DownloadClassesAsync()
{
using (var client = new HttpClient())
{
var url = string.Format("{0}?wijzigingen=1&type=Leerlingrooster&school=620", BaseUrl);

var source = await client.GetStringAsync(url);
var doc = CreateDocument(source);

var optionNodes = doc.DocumentNode.Descendants("select")
.Where(node => node.HasAttributes && node.Attributes["name"].Value != null)
.First(node => node.Attributes["name"].Value.Equals("afdeling"))
.Descendants("option");

return optionNodes.Select(node => node.NextSibling.InnerText.Trim()).ToList();
}
}[/CPP]


En ik heb zelf inmiddels wat methods gemaakt om deze dingen te vervangen omdat dit veel gebruikt wordt en dan is het nog maar 1 korte regel:

[CPP] var optionNodes = doc.DocumentNode.Descendants("select")
.Where(node => node.HasAttributes && node.Attributes["name"].Value != null)
.First(node => node.Attributes["name"].Value.Equals("afdeling"))
.Descendants("option");[/CPP]

=>

[CPP]var optionNodes = SelectNode(doc.DocumentNode, "select", "name", "afdeling").Descendants("option");[/CPP]

Zo ben ik het stap voor stap makkelijker aan het maken voor mezelf.
 
En wat wordt de code hiervan

[CPP]public async Task<List<MatchResult>> DownloadResultsAsync()
{
var url = string.Format("{0}/113/laatste-uitslagen/", BaseUrl);

Debug.WriteLine("DownloadClassesAsync");
Debug.WriteLine("\turl:{0}", url);

var source = await DownloadPageSourceAsync(url);

var doc = new HtmlDocument();
doc.LoadHtml(source);


var results = new List<MatchResult>();

var resultNodes = doc.DocumentNode.SelectNodes("//div[@id=\"ctl00_Mp_SkinHtml_content_ctl01_ctl04_UitslagenControl1_LaatsteUitslagenPanel\"]//table//tr");
foreach (HtmlNode resultRow in resultNodes)
{
if (resultRow.HasAttributes) //title row has no attributes
{
try
{
var date = resultRow.SelectSingleNode("./td[@class=\"datum\"]").InnerText.Trim();
var time = resultRow.SelectSingleNode("./td[@class=\"tijd\"]").InnerText.Trim();
var home = resultRow.SelectSingleNode("./td[@class=\"thuis\"]").InnerText.Trim();
var visitors = resultRow.SelectSingleNode("./td[@class=\"uit\"]").InnerText.Trim();
var result = resultRow.SelectSingleNode("./td[@class=\"uitslag\"]").InnerText.Trim();

var matchResult = new MatchResult(date, time, home, visitors, result);
results.Add(matchResult);

Debug.WriteLine("\t\t Found Result: [{0}]", matchResult);
}
catch (Exception ex)
{
Debug.WriteLine("\t\t Error getting match result: [{0}]", ex.Message);
}
}
}

return results;
}[/CPP]
 
Stap 1:

[CPP]public class Parser
{
private const string BaseUrl = "http://www.wakanutgeve.nl/";

public async Task<List<MatchResult>> RefactoredDownloadResultsAsync()
{
var url = string.Format("{0}113/laatste-uitslagen/", BaseUrl);

using (var client = new HttpClient())
{
var source = await client.GetStringAsync(new Uri(url));
var doc = CreateDocument(source);

var results = new List<MatchResult>();

var resultNodes = doc.DocumentNode.Descendants("div")
.Where(node => node.HasAttributes && node.Attributes.Contains("id"))
.First(node => node.Attributes["id"].Value.Equals("ctl00_Mp_SkinHtml_content_ctl01_ctl04_UitslagenControl1_LaatsteUitslagenPanel"))
.Descendants("table").First()
.Descendants("tr");

foreach (HtmlNode resultRow in resultNodes)
{
if (resultRow.HasAttributes) //title row has no attributes
{
try
{
var date = resultRow.Descendants("td")
.Where(node => node.HasAttributes && node.Attributes.Contains("class"))
.First(node => node.Attributes["class"].Value.Equals("datum"))
.InnerText.Trim();


var time = resultRow.Descendants("td")
.Where(node => node.HasAttributes && node.Attributes.Contains("class"))
.First(node => node.Attributes["class"].Value.Equals("tijd"))
.InnerText.Trim();


var home = resultRow.Descendants("td")
.Where(node => node.HasAttributes && node.Attributes.Contains("class"))
.First(node => node.Attributes["class"].Value.Equals("thuis"))
.InnerText.Trim();


var visitors = resultRow.Descendants("td")
.Where(node => node.HasAttributes && node.Attributes.Contains("class"))
.First(node => node.Attributes["class"].Value.Equals("uit"))
.InnerText.Trim();


var result = resultRow.Descendants("td")
.Where(node => node.HasAttributes && node.Attributes.Contains("class"))
.First(node => node.Attributes["class"].Value.Equals("uitslag"))
.InnerText.Trim();

var matchResult = new MatchResult(date, time, home, visitors, result);
results.Add(matchResult);

Debug.WriteLine("\t\t Found Result: [{0}]", matchResult);
}
catch (Exception ex)
{
Debug.WriteLine("\t\t Error getting match result: [{0}]", ex.Message);
}
}
}

return results;
}
}

private HtmlDocument CreateDocument(string source)
{
var doc = new HtmlDocument();
doc.LoadHtml(source);

return doc;
}
}[/CPP]


Ik zal zo even de dubbele code vervangen door methods maar dit werkt al.

Hier staan wat methods die het makkelijker & leesbaar maken. Maakt het omzetten van de andere ook een stuk makkelijker.

[CPP]public class Parser
{
private const string BaseUrl = "http://www.wakanutgeve.nl/";

public async Task<List<MatchResult>> RefactoredDownloadResultsAsync()
{
var url = string.Format("{0}113/laatste-uitslagen/", BaseUrl);

using (var client = new HttpClient())
{
var source = await client.GetStringAsync(new Uri(url));
var doc = CreateDocument(source);

var results = new List<MatchResult>();

var resultNodes = SelectNode(doc.DocumentNode, "div", "id", "ctl00_Mp_SkinHtml_content_ctl01_ctl04_UitslagenControl1_LaatsteUitslagenPanel")
.Descendants("table").First()
.Descendants("tr");

foreach (var resultRow in resultNodes)
{
if (resultRow.HasAttributes) //title row has no attributes
{
try
{
var dateNode = SelectNode(resultRow, "td", "class", "datum");
var date = SelectContent(dateNode);

var timeNode = SelectNode(resultRow, "td", "class", "tijd");
var time = SelectContent(timeNode);

var homeNode = SelectNode(resultRow, "td", "class", "thuis");
var home = SelectContent(homeNode);

var visitorsNode = SelectNode(resultRow, "td", "class", "uit");
var visitors = SelectContent(visitorsNode);

var resultNode = SelectNode(resultRow, "td", "class", "uitslag");
var result = SelectContent(resultNode);

//of
//var result = SelectContent(SelectNode(resultRow, "td", "class", "uitslag"));

results.Add(new MatchResult(date, time, home, visitors, result));

Debug.WriteLine("\t\t Found Result: [{0}]", results.Last());
}
catch (Exception ex)
{
Debug.WriteLine("\t\t Error getting match result: [{0}]", ex.Message);
}
}
}

return results;
}
}

private HtmlDocument CreateDocument(string source)
{
var doc = new HtmlDocument();
doc.LoadHtml(source);

return doc;
}

private HtmlNode SelectNode(HtmlNode inputNode, string nodeName, string attributeName, string attributeValue)
{
return inputNode.Descendants(nodeName)
.Where(node => node.HasAttributes && node.Attributes.Contains(attributeName))
.First(node => node.Attributes[attributeName].Value.Equals(attributeValue));
}

private string SelectContent(HtmlNode node)
{
return node.InnerText.Trim();
}
}[/CPP]
 
Laatst bewerkt:
gebruik

Code:
using System.Linq;



Als je naar de rooster app wp8.1 kijkt zie je dit ook allemaal




Het idee van de .Where method is een loop door alle nodes en dan geeft deze een lijst terug van alle nodes die aan de criteria in de ( ) voldoen.

De .First method looped door de nodes en geeft de eerste node die aan de criteria voldoet.


Linq heeft het voordeel om alles te 'chained' in plaats van losse loops te maken. Wat je ook zou kunnen doen als je dat wilt. Je code wordt daardoor wel een stuk langer.




where voorbeeld:
(niet werkend)


Code:
var existingList = new List<string>(); //add some items

loop:
var newList = new List<string>();

foreach(var str in existingList)
{
	if(str.Length > 5)
	{
		newList.Add(str);
	}
}

of met linq

Code:
var existingList = new List<string>(); //add some items
var newList = existingList.Where(str => str.Length > 5);
 
Laatst bewerkt:
Je kunt als je dat fijner vind gewoon alles in de windows phone project maken en daar opslaan. En dan als je een w8.1 versie wilt maken pas alles in shared zetten wat nodig is in beide projecten.


het makkelijkste is nu in plaats van deze httpclient:

Code:
using Windows.Web.Http;

deze te gebruiken

Code:
using System.Net.Http;


Dan moet het werken, ik zal nog even kijken waarom de Windows.Web niet werkt zonder de standaard filter. (was ik zelf gisteren ook mee bezig)

Wel krijg je een exception in de 2e method bij nieuws title omdat er geen td nodes zijn, dit kun je misschien beter eerst in stapjes coden en debuggen. Als je .First aanroept op een collection waar er 0 in zitten krijg je zo'n exceptions.

Het is net zoals met xpath soms even zoeken welke node je moet hebben. (debuggen werkt hier het beste bij)
 
Wat moet dit dan worden?

[CPP]
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);

if (NavigationContext.QueryString != null)
{
var title = NavigationContext.QueryString["name"];

titel.Title = "ZSV " + title;


var url = NavigationContext.QueryString["url"];
var competitieurl = await GetCompetitionUrlAsync(url);
var UitslagenUrl = await GetUitslagenUrlAsync(competitieurl);
var ProgrammaUrl = await GetProgrammaUrlAsync(competitieurl);

var stand = await DownloadCompetitieAsync(competitieurl);
competition.ItemsSource = stand;
var uitslag = await DownloadUitslagenAsync(UitslagenUrl);
Uitslagen.ItemsSource = uitslag;
var programma = await DownloadProgrammaAsync(ProgrammaUrl);
ProgrammaTeam.ItemsSource = programma;


}
}
[/CPP]
 
Nu werkt het een beetje anders bij navigation dus de parameter die je meegeeft kan alles zijn. (zie eerdere posts)
Dus dan neem je de parameter en cast je hem naar dat wat je mee hebt gegeven en gebruik je de data. Voor de rest kun je dan alles ongeveer hetzelfde houden.

Ik zou wel niet zo ineens alles kopieren maar stap voor stap. Beginnen met de parameters ophalen, dan de eerste method toevoegen en kijken of het werkt.
Anders heb je teveel foutmeldingen in eens. (tenzij je alle ophaal methods al hebt omgezet vooraf)


Als je hetzelfde principe wilt doen met de parameter["name"] of parameter["url"] kun je als parameter een dictionary meegeven met de waarde inplaats van een class.
Dit is een soort lijst maar dan kun je de dingen in de lijst een naam geven.

http://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx (onderaan staat meestal een voorbeeld)
 
Hier geeft ie aan dat de bovenste "GetCompetitionUrlAsync(url) niet werkt, waarom?

[CPP]

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
var url = e.Parameter;

if (url != null)
{
var competitieurl = await GetCompetitionUrlAsync(url);
}
}

public async Task<string> GetCompetitionUrlAsync(string teamUrl)
{
Debug.WriteLine("GetCompetitionUrlAsync");
Debug.WriteLine("\tfrom url:{0}", teamUrl);

using (var client = new HttpClient())
{
var source = await client.GetStringAsync(new Uri(teamUrl));

var doc = new HtmlDocument();
doc.LoadHtml(source);

var urlNodes = SelectNode(doc.DocumentNode, "div", "id", "Tekstpagina");

var competitionUrl = SelectNode(urlNodes, "a", string.Empty, string.Empty).GetAttributeValue("href", string.Empty);

Debug.WriteLine("parsed competition url:{0}", competitionUrl);

return competitionUrl;
}

}

[/CPP]
 
Het type van e.Parameter (en url dus) is object.

Je zult in url.ToString() moeten gebruiken daar want dat is het type input parameter van je method.

Ook kun je de null check maken op e.Parameter in plaats van de url, en dan kun je ook:

var url = e.Parameter.ToString();
 
[CPP]

var urlNodes = SelectNode(doc.DocumentNode, "div", "id", "Tekstpagina");

var competitionUrl = SelectNode(urlNodes, "a", string.Empty, string.Empty).GetAttributeValue("href", string.Empty);


[/CPP]

Wat klopt hier niet aan?
 
De selectNode method had ik gemaakt voor een bepaald stuk in de code die het nodig had en is niet voor alle dingen te gebruiken.
De string.Empty zal niet werken want er is geen attributeName/Value met een lege string.

Daar zul je dus iets anders moeten doen.

bijvoorbeeld zoiets als dit:

Code:
var competitionUrl = urlNodes.Descendants("a").First().GetAttributeValue("href", string.Empty);

Gebruik deze manier als je de nieuwe dingen maakt. (behalve als je zeker weet dan een van je standaard gemaakte SelectNode methods werkt).


edit:
of je moet de method aanpassen en kijken als je string.empty gebruikt dan gewoon de eerste node terug geven.
 
Laatst bewerkt:
en wat moet ik hiervoor gebruiken, want dit werkt ook niet meer

[CPP]

competitionUrl = HttpUtility.HtmlDecode(competitionUrl);


[/CPP]
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan