2010-08-12 40 views
12

Tôi đang cố gắng sử dụng lớp học BrowserSession của Rohit Agarwal cùng với HtmlAgilityPack để đăng nhập và sau đó điều hướng xung quanh Facebook.Sử dụng BrowserSession và HtmlAgilityPack để đăng nhập vào Facebook thông qua .NET

Tôi đã từng quản lý tương tự bằng cách viết HttpWebRequest của riêng mình. Tuy nhiên, nó chỉ hoạt động khi tôi tìm nạp cookie theo cách thủ công từ trình duyệt của tôi và chèn chuỗi cookie mới vào yêu cầu mỗi lần tôi thực hiện "phiên" mới. Bây giờ tôi đang cố gắng sử dụng BrowserSession để điều hướng thông minh hơn.

Dưới đây là các mã hiện tại:

BrowserSession b = new BrowserSession(); 

b.Get(@"http://www.facebook.com/login.php"); 
b.FormElements["email"] = "[email protected]"; 
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"); 

Trên đây hoạt động tốt. Sự cố xảy ra khi tôi cố gắng sử dụng lại BrowserSession này để tìm nạp một trang khác. Tôi đang làm theo cách này vì BrowserSession lưu các cookie từ phản hồi cuối cùng và chèn chúng vào yêu cầu tiếp theo, do đó tôi không cần phải tự chèn thêm cookiedata từ trình duyệt của mình nữa.

Tuy nhiên, khi tôi cố gắng làm một cái gì đó như thế này:

var profilePage = b.Get(@"https://m.facebook.com/profile.php?id=1111111111"); 

doc tôi nhận được lại là trống rỗng. Tôi sẽ đánh giá cao bất kỳ đầu vào nào về những gì tôi đang làm sai.

Trả lời

9

Xin lỗi, tôi không biết nhiều về gói nhanh nhẹn HTML hoặc lớp BrowserSession mà bạn đã đề cập. Nhưng tôi đã thử cùng một kịch bản với HtmlUnit và nó hoạt động tốt. Tôi đang sử dụng .NET wrapper (mã nguồn có thể được tìm thấy here và được giải thích thêm một chút here), và đây là mã tôi đã sử dụng (một số chi tiết được xóa để bảo vệ người vô tội):

var driver = new HtmlUnitDriver(true); 
driver.Url = @"http://www.facebook.com/login.php"; 

var email = driver.FindElement(By.Name("email")); 
email.SendKeys("[email protected]"); 

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

Hy vọng điều này sẽ hữu ích.

+0

Cảm ơn bạn! Đây là một giải pháp tốt và hoạt động tốt :) –

+0

Bạn được chào đón nhiều nhất. Chúc may mắn với dự án của bạn :) – Mhmmd

+0

tôi nghĩ rằng đây là cho java chỉ, nó có thể được sử dụng cho .net – Smith

0

Bạn đã xem API mới của mình chưa? http://developers.facebook.com/docs/authentication/

Bạn có thể gọi một URL đơn giản để có được quyền truy cập OAuth2.0 token và đính kèm mà trên phần còn lại của các yêu cầu của bạn ...

https://graph.facebook.com/oauth/authorize? 
    client_id=...& 
    redirect_uri=http://www.example.com/oauth_redirect 

Thay đổi redirect_uri để bất cứ điều gì URL mà bạn muốn, và nó sẽ được gọi lại với tham số có tên là "access_token" trên đó. Nhận điều đó và thực hiện bất kỳ lệnh gọi SDK tự động nào bạn muốn.

+0

Cảm ơn câu trả lời. Tôi chỉ đang thực hiện một nghiên cứu riêng trong việc vẽ đồ thị xã hội và chỉ cần tự động duyệt web của riêng mình thông qua bạn bè của riêng tôi, thay vì lưu các trang thủ công. Có thể mất ít thời gian hơn để thực hiện nó theo cách thủ công, nhưng sẽ thú vị hơn khi tự động hóa :) Tôi không cần hoặc muốn ứng dụng facebook thực sự. Ngoài ra, API không thể thấy tất cả những gì tôi có thể thấy mình là người dùng đã đăng nhập và dù sao nhiệm vụ hiện tại của tôi bị kẹt là học cách sử dụng BrowserSession đúng cách. –

2

Bạn có thể muốn sử dụng WatiN (Web Application Testing In .Net) Hoặc Selenium để thúc đẩy trình duyệt của mình. Điều này sẽ giúp đảm bảo bạn không phải fiddle với các cookie và làm bất kỳ công việc tùy chỉnh để làm cho các yêu cầu tiếp theo làm việc kể từ khi bạn đang mô phỏng người dùng thực tế.

+0

Cảm ơn câu trả lời, tôi đã tìm được giải pháp của tôi nhưng có thể quay lại ví dụ sau :) –

1

Hôm nay tôi đã đối mặt với cùng một vấn đề. Tôi cũng đã làm việc với lớp học BrowserSession của Rohit Agarwal cùng với HtmlAgilityPack. Sau khi thử nghiệm và lập trình lỗi cả ngày, tôi đã phát hiện ra rằng vấn đề là do sự cố không đặt đúng cookie trong các yêu cầu tiếp theo. Tôi không thay đổi mã BrowserSession ban đầu để hoạt động chính xác nhưng tôi đã thêm các chức năng sau và sửa đổi một chút chức năng SameCookieFrom. Cuối cùng nó làm việc rất tốt cho tôi.

Các thêm/sửa đổi các chức năng như sau:

class BrowserSession{ 
    private bool _isPost; 
    private HtmlDocument _htmlDoc; 
    public CookieContainer cookiePot; //<- This is the new CookieContainer 

... 

    public string Get2(string url) 
    { 
     HtmlWeb web = new HtmlWeb(); 
     web.UseCookies = true; 
     web.PreRequest = new HtmlWeb.PreRequestHandler(OnPreRequest2); 
     web.PostResponse = new HtmlWeb.PostResponseHandler(OnAfterResponse2); 
     HtmlDocument doc = web.Load(url); 
     return doc.DocumentNode.InnerHtml; 
    } 
    public bool OnPreRequest2(HttpWebRequest request) 
    { 
     request.CookieContainer = cookiePot; 
     return true; 
    } 
    protected void OnAfterResponse2(HttpWebRequest request, HttpWebResponse response) 
    { 
     //do nothing 
    } 
    private void SaveCookiesFrom(HttpWebResponse response) 
    { 
     if ((response.Cookies.Count > 0)) 
     { 
      if (Cookies == null) 
      { 
       Cookies = new CookieCollection(); 
      }  
      Cookies.Add(response.Cookies); 
      cookiePot.Add(Cookies);  //-> add the Cookies to the cookiePot 
     } 
    } 

Những gì nó: Về cơ bản nó sẽ lưu các tập tin cookie từ ban đầu "Post-Response" và thêm các CookieContainer cùng với yêu cầu gọi sau. Tôi không hoàn toàn hiểu tại sao nó không hoạt động trong phiên bản đầu tiên bởi vì nó bằng cách nào đó làm tương tự trong hàm AddCookiesTo. (if (Cookies! = null & & Cookies.Count> 0) request.CookieContainer.Add (Cookies);) Nhưng dù sao, với những chức năng được thêm vào, nó sẽ hoạt động tốt ngay bây giờ.

Nó có thể được sử dụng như thế này:

//initial "Login-procedure" 
BrowserSession b = new BrowserSession(); 
b.Get("http://www.blablubb/login.php"); 
b.FormElements["username"] = "yourusername"; 
b.FormElements["password"] = "yourpass"; 
string response = b.Post("http://www.blablubb/login.php"); 

tất cả các cuộc gọi tiếp theo nên sử dụng:

response = b.Get2("http://www.blablubb/secondpageyouwannabrowseto"); 
response = b.Get2("http://www.blablubb/thirdpageyouwannabrowseto"); 
... 

Tôi hy vọng nó sẽ giúp nhiều người phải đối mặt với cùng một vấn đề!

12

Tôi đã khắc phục nguyên nhân gốc rễ của việc này nếu có ai quan tâm. Hóa ra các cookie đã được lưu trong CookieContainer của đối tượng REQUEST và không phải là đối tượng phản hồi. Tôi cũng đã thêm khả năng tải xuống tệp (miễn là tệp dựa trên chuỗi). Mã chắc chắn không phải là chủ đề an toàn, nhưng đối tượng không an toàn với chủ đề để bắt đầu bằng:

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

Tôi có các triệu chứng tương tự - đăng nhập đã hoạt động nhưng cookie xác thực không có trong ngăn chứa cookie và vì vậy nó không được gửi về các yêu cầu tiếp theo. Tôi phát hiện ra điều này là do yêu cầu web đã xử lý tiêu đề Vị trí: nội bộ, chuyển hướng hậu trường sang một trang mới, mất cookie trong quá trình này. Tôi đã sửa lỗi này bằng cách thêm:

request.AllowAutoRedirect = false; // Location header messing up cookie handling! 

... vào hàm OnPreRequest(). Nó giống như sau:

protected bool OnPreRequest(HttpWebRequest request) 
    { 
     request.AllowAutoRedirect = false; // Location header messing up cookie handling! 

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

Tôi hy vọng điều này có thể giúp ai đó gặp phải vấn đề tương tự.

Các vấn đề liên quan