1

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


UPDATE

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");
        }


    }
}
}

Output

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.

UPDATE 2

I also tried:

var inputField =  wb.Document.GetElementsByTagName("text")["login"];

but it returns null.

Community
  • 1
  • 1
Yoda
  • 17,363
  • 67
  • 204
  • 344
  • @DeeMac Neither, it is console application. The site is not mine I want to fill this form on: http://www.e-korepetycje.net/ and submit it by code. – Yoda Oct 20 '14 at 14:18
  • Ok, I see. This question is massively misleading - I, at first, assumed you wanted to use C# to perform some sort of client-side functionality typically handled with JS. I would've thought it was a case of making a web request to the server with the relevant credentials being passed in, in which case - isn't that exactly what that answer suggests? –  Oct 20 '14 at 14:19
  • @DeeMac The answer(in other topic) does not clarify where to use `action` from the form `action="http://www.e-korepetycje.net/zaloguj"` also does not clarify are these `String strPost = "username="+username+"&password="+password+"&firstname="+firstname+"&lastname="+lastname;` are the names of input fields in form etc. The problem is that that author of answer did not post the HTML his code refers to so it is not self explanatory code. – Yoda Oct 20 '14 at 14:23
  • I believe Adriano has given you a thorough answer. Can I ask (purely out of curiosity) what you're doing with this/why you're doing it? –  Oct 20 '14 at 15:05
  • 1
    @Yoda document is null because download and DOM parsing is not completed. Add your code (to search and fill form) inside DocumentCompleted event (updated answer to make it clear). – Adriano Repetti Oct 20 '14 at 15:11
  • Is Descendants() empty or Descendants().First()? What's page content when you do it? – Adriano Repetti Oct 20 '14 at 15:58
  • @AdrianoRepetti ` Console.WriteLine(Descendants(form).Count());` this prints 0, later on this causes an exception `Descendants(form).First(x => x.GetAttribute("name") == "login");` because there is no element which has `name=login`. I paste full code in 2 miutes(without password, thank you). – Yoda Oct 20 '14 at 16:01
  • @AdrianoRepetti Ok, I have updated code and its output. The page is fully loaded but it cannot find element with `name=login` – Yoda Oct 20 '14 at 16:05
  • @Yoda there was an error in Descendants() function, fixed! – Adriano Repetti Oct 20 '14 at 16:07
  • @AdrianoRepetti I updated code and output. Now it gets 12 of descendants but cannot find the element with name login or passwd but it is there: view-source:http://www.e-korepetycje.net/ – Yoda Oct 20 '14 at 16:18
  • Is it in the list returned by Descendants()? Can you log, for each, name name attribute value? – Adriano Repetti Oct 20 '14 at 18:24

2 Answers2

2

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...

Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • 1
    @yoda It's a custom extension method (I included code). To use it that way you have to put it in another static class otherwise just refactor to remove "this". – Adriano Repetti Oct 20 '14 at 14:53
  • Thank you could you have a look please at update at OP? When the document is completed I ask for descendants of that form and print number of them and there is 0 of them. It is 3rd line of `wb_DocumentCompleted`. – Yoda Oct 20 '14 at 15:42
0

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.

Mihir
  • 419
  • 3
  • 10