0

資格情報を自動的に提供した後、Web ページから情報を取得する必要があるアプリを開発しています。ページの自動ログインとリダイレクトをどのように実行したか。これが私のコードです:

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://abcd.com.au/categories/A_dfn/sdf");
    HttpWebResponse res = req.GetResponse() as HttpWebResponse;

    StringBuilder sb = new StringBuilder();
    byte[] buf = new byte[10000];
    Stream resStream = res.GetResponseStream();
    string s = null;
    int c = 0;
    do
    {
        c = resStream.Read(buf, 0, buf.Length);
        if (c != 0) {
            s = ASCIIEncoding.ASCII.GetString(buf, 0, c);
            sb.Append(s);
        }
    } while (c > 0);
    string oldhead = "class=\"login_button\">";
    string newhead = "class=\"login_button\">   <script type=\"text/javascript\">document.getElementById('btn').click()</script>";
    sb.Replace(oldhead, newhead);

    string oldbtn = "value=\"Submit\"";
    string newbtn = "value=\"Submit\" id=\"btn\" ";
    sb.Replace(oldbtn, newbtn);

    string oldAction = "<form action=\"/login\" method=\"post\">";
    string newAction = "<form action=\"https://abcd.com.au/login?orig_req_url=%2Fcategories/A_dfn/sdf\" method=\"post\">";
    sb.Replace(oldAction, newAction);

    string oldUsername = "<input id=\"login_email\" type=\"text\" name=\"user[email_address]\" class=\"textBox\" value=\"\">";
    string newUserName = "<input id=\"login_email\" type=\"text\" name=\"user[email_address]\" class=\"textBox\" value=\"abc@xyz.com.au\">";
    sb.Replace(oldUsername, newUserName);

    string oldPass = "<input id=\"login_password\" type=\"password\" name=\"user[password]\" class=\"textBox\" value=\"\">";
    string newPass = "<input id=\"login_password\" type=\"password\" name=\"user[password]\" class=\"textBox\" value=\"abc\">";
    sb.Replace(oldPass,newPass);
    Response.Write(sb);

これは、ページ (Response.write(sb)) をレンダリングすることによって、期待される出力を示しています。しかし、今は「https://abcd.com.au/login?orig_req_url=%2Fcategories/A_dfn/sdf」にリダイレクトせずに同じことをしたいと思っており、これについてもっとやりたいと思っています。何らかのバッファで Response.Write(sb) の出力を取得することを期待しています。することは可能ですか?

ここに例を示します。これは、私がやりたいことを正確に説明しています。私は製品の数量と言う名前を探しています:スクリュー15mm、これはページhttps://abcd.com.au/%2Fcategories/A_dfn/sdfにあります。したがって、最初にこの URL を要求していますが、そのページにアクセスするにはログインが必要なため、ログイン ページにリダイレクトされ、ユーザー名とパスワードを入力し、javascript を使用してログイン ボタンを押してから、最初に要求されたページにリダイレクトされます。そして、このページでその製品を検索し、Web アプリに情報を返します。

これはすべて、ユーザーに見せずにやりたいことです。取得した情報を表示したいだけです。

ありがとう。

4

1 に答える 1

0

あなたが探しているのは永続的なセッションです。この問題に対するあなたのアプローチは正しくありません。クライアント側で送信をトリガーしています。達成しようとしていることは、サーバー側で行う必要があります。

シナリオの鍵は、ログイン ページで設定されたセッションと Cookie を永続化 (保存) することです。次に、製品情報の次のリクエストの前に、クレデンシャルをリクエスト元の webRequest に挿入します。

  1. WebRequest オブジェクトを使用して、ログイン ページを読み込みます。
  2. ログイン ページの応答ヘッダーから送信された情報 (Cookie) を保存します。
  3. 提供された応答ヘッダーを使用して新しい WebRequest オブジェクトを作成し、ユーザー ID/パスワードを挿入します。
  4. Response によって返された資格情報を保存します。
  5. 見積もり情報のリクエストに進みます。

スクリーン スクラップを作成しようとしている Web サイトを知らずにこれを行う一般的な方法はありません。しかし、一般的な手順は上記のとおりです。基本的に、これにはカスタム クラスを作成する必要があります。

また、 HTML ノードを解析するには、HTMLAgilityPackが必要です。正しい方法です。


編集:私のコードを追加しました。たまたま、以前にこのクラスを作成したことがあります。だから、あなたは運がいいです。ただし、使用するには HTMLAgilityPack をインストールして参照する必要があります。HAPは次の場所からダウンロードできます 。

Public Class clsBrowserSession
    '=================================================================================================================================
    'This is a special Browser Post class
    ' Instead of just POST to a URL as per the clsWeb.fnsPostResponse()
    ' clsBrowserSession allows us to LOAD a page first, persist all the cookies and variables, and then only POST to the target URL.
    ' The reason is that some program will drop (lets say) a SessionID as an input when you first load the page.
    ' and when you post, without the SessionID (variable), it will reject the POST. Thus clsBrowserSession can solve this problem.
    '=================================================================================================================================
    ' USAGE:
    '   Dim voBrowserSession As New clsBrowserSession
    '   voBrowserSession.sbLoadPage("https://xxx.yyy.net.my/publicncdenq/index.htm")
    '   voBrowserSession.proFormElements("UserID") = "myID"
    '   voBrowserSession.proFormElements("Password") = "myPassword"
    '   Dim vsResponseHTML As String = voBrowserSession.Post("https://xxx.yyy.net.my/publicncdenq/index.htm")
    Private vbIsPostingInProgress As Boolean
    Public voCookies As System.Net.CookieCollection
    Public proHTMLDoc As HtmlAgilityPack.HtmlDocument
    Public proFormElements As clsFormElementCollection

    Public Sub sbLoadPage(pvsURL As String)
        vbIsPostingInProgress = False
        fnoCreateWebRequestObject().Load(pvsURL)
    End Sub
    Public Function Post(pvsURL As String) As String
        vbIsPostingInProgress = True
        fnoCreateWebRequestObject().Load(pvsURL, "POST")

        Return proHTMLDoc.DocumentNode.InnerHtml
    End Function

    Private Function fnoCreateWebRequestObject() As HtmlAgilityPack.HtmlWeb
        Dim voWeb As New HtmlAgilityPack.HtmlWeb
        voWeb.UseCookies = True
        voWeb.PreRequest = New HtmlAgilityPack.HtmlWeb.PreRequestHandler(AddressOf event_OnPreRequest)
        voWeb.PostResponse = New HtmlAgilityPack.HtmlWeb.PostResponseHandler(AddressOf event_OnAfterResponse)
        voWeb.PreHandleDocument = New HtmlAgilityPack.HtmlWeb.PreHandleDocumentHandler(AddressOf event_OnPreHandleDocument)
        Return voWeb
    End Function
    Private Sub sbAddPostDataTo(pvoRequest As Net.HttpWebRequest)
        Dim vsPayload As String = proFormElements.fnsAssemblePostPayload()
        Dim vabyteBuffer As Byte() = Text.Encoding.UTF8.GetBytes(vsPayload.ToCharArray())
        pvoRequest.ContentLength = vabyteBuffer.Length
        pvoRequest.ContentType = "application/x-www-form-urlencoded"
        pvoRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11"
        pvoRequest.GetRequestStream().Write(vabyteBuffer, 0, vabyteBuffer.Length)
    End Sub
    Private Sub sbAddvoCookiesTo(pvoRequest As Net.HttpWebRequest)
        If (Not IsNothing(voCookies)) Then
            If voCookies.Count > 0 Then pvoRequest.CookieContainer.Add(voCookies)
        End If
    End Sub
    Private Sub sbSaveCookiesFrom(pvoResponse As Net.HttpWebResponse)
        If pvoResponse.Cookies.Count > 0 Then
            If IsNothing(voCookies) Then voCookies = New Net.CookieCollection
            voCookies.Add(pvoResponse.Cookies)
        End If
    End Sub
    Private Sub sbSaveHtmlDocument(pvoHTMLDocument As HtmlAgilityPack.HtmlDocument)
        proHTMLDoc = pvoHTMLDocument
        proFormElements = New clsFormElementCollection(proHTMLDoc)
    End Sub

    Protected Function event_OnPreRequest(pvoRequest As Net.HttpWebRequest) As Boolean
        sbAddvoCookiesTo(pvoRequest)
        If vbIsPostingInProgress Then sbAddPostDataTo(pvoRequest)
        Return True
    End Function
    Protected Sub event_OnAfterResponse(pvoRequest As System.Net.HttpWebRequest, pvoResponse As Net.HttpWebResponse)
        sbSaveCookiesFrom(pvoResponse)
    End Sub
    Protected Sub event_OnPreHandleDocument(pvoHTMLDocument As HtmlAgilityPack.HtmlDocument)
        sbSaveHtmlDocument(pvoHTMLDocument)
    End Sub

    '-----------------------------------------------------------------------------------------------------
    'Form Elements class
    '  Note: This element class will only capture (any) INPUT elements only, which should be enough
    '  for most cases. It can be easily modified to add other SELECT, TEXTAREA, etc voInputs
    '-----------------------------------------------------------------------------------------------------
    Public Class clsFormElementCollection
        Inherits Dictionary(Of String, String)
        Public Sub New(htmlDoc As HtmlAgilityPack.HtmlDocument)
            Dim voInputs As Collections.Generic.IEnumerable(Of HtmlAgilityPack.HtmlNode) = htmlDoc.DocumentNode.Descendants("input")
            For Each voInput As HtmlAgilityPack.HtmlNode In voInputs
                Dim vsName = voInput.GetAttributeValue("name", "undefined")
                Dim vsValue = voInput.GetAttributeValue("value", "")
                If vsName <> "undefined" Then Add(vsName, vsValue)
            Next
        End Sub
        Public Function fnsAssemblePostPayload() As String
            Dim sb As New Text.StringBuilder
            For Each voKeyValuePair In Me
                Dim vsValue = System.Web.HttpUtility.UrlEncode(voKeyValuePair.Value)
                sb.Append("&" & voKeyValuePair.Key & "=" & vsValue)
            Next
            Return sb.ToString.Substring(1)
        End Function
    End Class
End Class

上記をクラスオブジェクトにしてインスタンス化するだけです。使用例はコメントにあります。vsResponseHTML文字列が必要です。

于 2013-03-04T04:08:13.010 に答える