Ich versuche, die BrowserSession- Klasse von Rohit Agarwal zusammen mit HtmlAgilityPack zu verwenden, um sich bei Facebook anzumelden und anschließend zu navigieren.
Ich habe es vorher schon geschafft, indem ich meine eigenen HttpWebRequests geschrieben habe. Dies funktioniert jedoch nur, wenn ich den Cookie manuell von meinem Browser abrufe und einen neuen Cookie-String in die Anfrage einfüge, wenn ich eine neue "Sitzung" mache. Jetzt versuche ich BrowserSession zu verwenden, um intelligentere Navigation zu erhalten.
Hier ist der aktuelle Code:
BrowserSession b = new BrowserSession();
b.Get(@"http://www.facebook.com/login.php");
b.FormElements["email"] = "some@email.com";
b.FormElements["pass"] = "xxxxxxxx";
b.FormElements["lsd"] = "qDhIH";
b.FormElements["trynum"] = "1";
b.FormElements["persistent_inputcheckbox"] = "1";
var response = b.Post(@"https://login.facebook.com/login.php?login_attempt=1");
Das obige funktioniert gut. Probleme treten auf, wenn ich versuche, diese BrowserSession erneut zu verwenden, um eine andere Seite zu holen. Ich mache es auf diese Weise, da BrowserSession die Cookies von der letzten Antwort speichert und sie in die nächste Anfrage einfügt, so dass ich die von meinem Browser abgerufenen Cookiedaten nicht mehr manuell eingeben muss.
Wenn ich jedoch versuche, so etwas zu tun:
var profilePage = b.Get(@"https://m.facebook.com/profile.php?id=1111111111");
Das Dokument, das ich zurückbekomme, ist leer. Ich würde mich über jede Eingabe darüber freuen, was ich falsch mache.
Entschuldigung, ich weiß nicht viel über das HTML-Agility-Pack oder die BrowserSession-Klasse, die Sie erwähnt haben. Aber ich habe das gleiche Szenario mit HtmlUnit versucht und es funktioniert gut. Ich bin mit einem .NET - Wrapper (der Quellcode von denen gefunden werden kann hier und ein bisschen mehr erklärt hier ), und hier ist der Code , den ich verwendet habe (einige Details entfernt zum Schutz der Unschuldigen):
var driver = new HtmlUnitDriver(true);
driver.Url = @"http://www.facebook.com/login.php";
var email = driver.FindElement(By.Name("email"));
email.SendKeys("some@email.com");
var pass = driver.FindElement(By.Name("pass"));
pass.SendKeys("xxxxxxxx");
var inputs = driver.FindElements(By.TagName("input"));
var loginButton = (from input in inputs
where input.GetAttribute("value").ToLower() == "login"
&& input.GetAttribute("type").ToLower() == "submit"
select input).First();
loginButton.Click();
driver.Url = @"https://m.facebook.com/profile.php?id=1111111111";
Assert.That(driver.Title, Is.StringContaining("Title of page goes here"));
Hoffe das hilft.
Ich habe die Ursache dafür behoben, wenn es jemanden interessiert. Es stellt sich heraus, dass die Cookies im CookieContainer des REQUEST-Objekts und nicht im Antwortobjekt gespeichert wurden. Ich habe auch die Möglichkeit hinzugefügt, eine Datei herunterzuladen (sofern die Datei stringbasiert ist). Der Code ist definitiv NICHT Thread-sicher, aber das Objekt war nicht threadsicher:
public class BrowserSession
{
private bool _isPost;
private bool _isDownload;
private HtmlDocument _htmlDoc;
private string _download;
/// <summary>
/// System.Net.CookieCollection. Provides a collection container for instances of Cookie class
/// </summary>
public CookieCollection Cookies { get; set; }
/// <summary>
/// Provide a key-value-pair collection of form elements
/// </summary>
public FormElementCollection FormElements { get; set; }
/// <summary>
/// Makes a HTTP GET request to the given URL
/// </summary>
public string Get(string url)
{
_isPost = false;
CreateWebRequestObject().Load(url);
return _htmlDoc.DocumentNode.InnerHtml;
}
/// <summary>
/// Makes a HTTP POST request to the given URL
/// </summary>
public string Post(string url)
{
_isPost = true;
CreateWebRequestObject().Load(url, "POST");
return _htmlDoc.DocumentNode.InnerHtml;
}
public string GetDownload(string url)
{
_isPost = false;
_isDownload = true;
CreateWebRequestObject().Load(url);
return _download;
}
/// <summary>
/// Creates the HtmlWeb object and initializes all event handlers.
/// </summary>
private HtmlWeb CreateWebRequestObject()
{
HtmlWeb web = new HtmlWeb();
web.UseCookies = true;
web.PreRequest = new HtmlWeb.PreRequestHandler(OnPreRequest);
web.PostResponse = new HtmlWeb.PostResponseHandler(OnAfterResponse);
web.PreHandleDocument = new HtmlWeb.PreHandleDocumentHandler(OnPreHandleDocument);
return web;
}
/// <summary>
/// Event handler for HtmlWeb.PreRequestHandler. Occurs before an HTTP request is executed.
/// </summary>
protected bool OnPreRequest(HttpWebRequest request)
{
AddCookiesTo(request); // Add cookies that were saved from previous requests
if (_isPost) AddPostDataTo(request); // We only need to add post data on a POST request
return true;
}
/// <summary>
/// Event handler for HtmlWeb.PostResponseHandler. Occurs after a HTTP response is received
/// </summary>
protected void OnAfterResponse(HttpWebRequest request, HttpWebResponse response)
{
SaveCookiesFrom(request, response); // Save cookies for subsequent requests
if (response != null && _isDownload)
{
Stream remoteStream = response.GetResponseStream();
var sr = new StreamReader(remoteStream);
_download = sr.ReadToEnd();
}
}
/// <summary>
/// Event handler for HtmlWeb.PreHandleDocumentHandler. Occurs before a HTML document is handled
/// </summary>
protected void OnPreHandleDocument(HtmlDocument document)
{
SaveHtmlDocument(document);
}
/// <summary>
/// Assembles the Post data and attaches to the request object
/// </summary>
private void AddPostDataTo(HttpWebRequest request)
{
string payload = FormElements.AssemblePostPayload();
byte[] buff = Encoding.UTF8.GetBytes(payload.ToCharArray());
request.ContentLength = buff.Length;
request.ContentType = "application/x-www-form-urlencoded";
System.IO.Stream reqStream = request.GetRequestStream();
reqStream.Write(buff, 0, buff.Length);
}
/// <summary>
/// Add cookies to the request object
/// </summary>
private void AddCookiesTo(HttpWebRequest request)
{
if (Cookies != null && Cookies.Count > 0)
{
request.CookieContainer.Add(Cookies);
}
}
/// <summary>
/// Saves cookies from the response object to the local CookieCollection object
/// </summary>
private void SaveCookiesFrom(HttpWebRequest request, HttpWebResponse response)
{
//save the cookies ;)
if (request.CookieContainer.Count > 0 || response.Cookies.Count > 0)
{
if (Cookies == null)
{
Cookies = new CookieCollection();
}
Cookies.Add(request.CookieContainer.GetCookies(request.RequestUri));
Cookies.Add(response.Cookies);
}
}
/// <summary>
/// Saves the form elements collection by parsing the HTML document
/// </summary>
private void SaveHtmlDocument(HtmlDocument document)
{
_htmlDoc = document;
FormElements = new FormElementCollection(_htmlDoc);
}
}
/// <summary>
/// Represents a combined list and collection of Form Elements.
/// </summary>
public class FormElementCollection : Dictionary<string, string>
{
/// <summary>
/// Constructor. Parses the HtmlDocument to get all form input elements.
/// </summary>
public FormElementCollection(HtmlDocument htmlDoc)
{
var inputs = htmlDoc.DocumentNode.Descendants("input");
foreach (var element in inputs)
{
string name = element.GetAttributeValue("name", "undefined");
string value = element.GetAttributeValue("value", "");
if (!this.ContainsKey(name))
{
if (!name.Equals("undefined"))
{
Add(name, value);
}
}
}
}
/// <summary>
/// Assembles all form elements and values to POST. Also html encodes the values.
/// </summary>
public string AssemblePostPayload()
{
StringBuilder sb = new StringBuilder();
foreach (var element in this)
{
string value = System.Web.HttpUtility.UrlEncode(element.Value);
sb.Append("&" + element.Key + "=" + value);
}
return sb.ToString().Substring(1);
}
}