Reputation: 18068
On a website: http://www.e-korepetycje.net/ there is form which is used to log in:
<form method="post" action="http://www.e-korepetycje.net/zaloguj" id="login-box">
<fieldset>
<ul>
<li><input type="text" name="login" placeholder="Login or email"></li>
<li><input type="password" name="passwd" placeholder="Password"></li>
<li><input type="submit" value="Log in"></li>
</ul>
</fieldset>
</form>
I woule like to fill input field login
and passwd
and then submit this form programatically by C#.
I have seen THIS TOPIC, but the most upvoted answer is just somebodies code which does not refer to the HTML posted in question and there is no HTML to which response refer to so it is hard to understand
I have used Adriano Repetti's answer. I get exception here var inputField = Descendants(form).First(x => x.GetAttribute("name") == "login");
sequence does not contain specified element (InvalidOperationException
).
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;
namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); WebBrowser wb = new System.Windows.Forms.WebBrowser(); wb.DocumentCompleted += wb_DocumentCompleted; wb.Navigate("http://www.e-korepetycje.net/"); Console.WriteLine("After navigate"); } public static IEnumerable Descendants( HtmlElement root) { foreach (HtmlElement child in root.Children) { yield return child;
if (!child.CanHaveChildren)
continue;
foreach (var subChild in Descendants(child))
yield return child;
}
}
static void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
WebBrowser wb = ((WebBrowser)sender);
if (e.Url.AbsolutePath == (sender as WebBrowser).Url.AbsolutePath) {
Console.WriteLine("COMPLETED");
//HtmlElementCollection elems = wb.Document.GetElementsByTagName("HTML");
//Console.WriteLine(elems[0].OuterHtml);
var form = wb.Document.GetElementById("login-box");
Console.WriteLine(Descendants(form).Count());
var inputField = Descendants(form).First(x => x.GetAttribute("name") == "login");
inputField.SetAttribute("value", "login");
inputField = Descendants(form).First(x => x.GetAttribute("name") == "passwd");
inputField.SetAttribute("value", "passwd");
var submitButton = Descendants(form).First(x => x.TagName == "input" && x.GetAttribute("type") == "submit");
submitButton.RaiseEvent("click");
}
}
}
}
After navigate
'WindowsFormsApplication1.vshost.exe' (CLR v4.0.30319: WindowsFormsApplication1.vshost.exe): Loaded 'C:\Windows\assembly\GAC\Microsoft.mshtml\7.0.3300.0__b03f5f7f11d50a3a\Microsoft.mshtml.dll'. Module was built without symbols.
COMPLETED
'WindowsFormsApplication1.vshost.exe' (CLR v4.0.30319: WindowsFormsApplication1.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core.resources\v4.0_4.0.0.0_pl_b77a5c561934e089\System.Core.resources.dll'. Module was built without symbols.
A first chance exception of type 'System.InvalidOperationException' occurred in System.Core.dll
12
'WindowsFormsApplication1.vshost.exe' (CLR v4.0.30319: WindowsFormsApplication1.vshost.exe): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
I also tried:
var inputField = wb.Document.GetElementsByTagName("text")["login"];
but it returns null
.
Upvotes: 1
Views: 1692
Reputation: 67070
Easiest way to programmatically interact with a web site (from a C# application) IMO is to use WebBrowser
control:
WebBrowser wb = new System.Windows.Forms.WebBrowser();
wb.Navigate(" http://www.e-korepetycje.net/");
Now that site has been loaded in the embedded web browser (IE based). You may inject some JavaScript code to perform this task but also from C# is pretty easy. When document downloading and DOM parsing is completed you can go find the form (using its ID). Put all subsequent code in wb.Document.DocumentCompleted
event handler (you may also, if you wish so, wait on wb.Document.DocumentStatus
property).
var form = wb.Document.GetElementById("login-box");
Then find submit button inside it:
var submitButton = form
.Descendants()
.First(x => x.TagName == "input" && x.GetAttribute("type") == "submit");
Then simulate a click:
submitButton.RaiseEvent("click");
I used a small helper function to iterate through all children of a HtmlElement
:
public static IEnumerable<HtmlElement> Descendants(this HtmlElement root)
{
foreach (HtmlElement child in root.Children)
{
yield return child;
if (!child.CanHaveChildren)
continue;
foreach (var subChild in Descendants(child))
yield return child;
}
}
BTW if you want to inject JavaScript code it has to be simply this (of course you'll need much more code to create the script function with Document.CreateElement()
and to invoke it with Document.InvokeScript()
):
document.forms["login-box"].submit();
Please note that same technique may be applied to also fill the form:
var inputField = form
.Descendants()
.First(x => x.GetAttribute("name") == "login");
inputField.SetAttribute("value", "login name to post");
Of course all this code may be generalized enough to be reused...
Upvotes: 2
Reputation: 449
You can create a windows form, add WebBrowser control to it and then set url to website. After url loaded into the browser control you can access Document property to invoke script (to populate userid and password and submit form) using InvokeScript(script) method.
Upvotes: 0