2

背景
ETrade 身份验证系统让我创建一个 RequestToken,然后执行一个授权 URL,这将打开一个 ETrade 页面。用户登录以授权其帐户上的活动。他们会收到一个密码,然后将其输入我的应用程序。我用 RequestToken 和 Pin 调用 ExchangeRequestTokenForAccessToken。然后我们就出发了。

问题
问题是我正在创建一个在后台连续运行的服务。不会有任何用户登录。相反,我不会进行任何交易。只是处理数字,寻找符合某些标准的股票。我不知道如何让它在无人看管的情况下工作。

谢谢,布拉德。

4

2 回答 2

1

Brad Melton 代码的当前版本。WatiN 已更改,不再包含 IE.AttachToIE 函数。因此,IEStaticInstanceHelper 现在被称为 StaticBrowserInstanceHelper,但该代码很难找到,所以我将其包含在此处。

class StaticBrowserInstanceHelper<T> where T : Browser {
    private Browser _browser;
    private int _browserThread;
    private string _browserHwnd;

    public Browser Browser {
        get {
            int currentThreadId = GetCurrentThreadId();
            if (currentThreadId != _browserThread) {
                _browser = Browser.AttachTo<T>(Find.By("hwnd", _browserHwnd));
                _browserThread = currentThreadId;
            }
            return _browser;
        }
        set {
            _browser = value;
            _browserHwnd = _browser.hWnd.ToString();
            _browserThread = GetCurrentThreadId();
        }
    }

    private int GetCurrentThreadId() {
        return Thread.CurrentThread.GetHashCode();
    }
}

ETrade 的登录页面也发生了变化。他们有几个。我检查的所有登录页面都始终有一个 USER 字段和一个 PASSWORD 字段,但是登录按钮有各种看起来很脆弱的名称。因此,如果这不起作用,那是我要检查的第一件事。其次,如果我直接进入认证页面,它会提示登录,但是它经常不会带你进入认证页面。通过转到主页登录,然后转到身份验证页面,我得到了更一致的结果。

    static public string GetPin(string username, string password, string logonLink, string authLink) {
        // Settings.Instance.MakeNewIeInstanceVisible = false;

        var StaticInstanceHelper = new StaticBrowserInstanceHelper<IE>();
        Settings.AutoStartDialogWatcher = false;

        // This code doesn't always handle it well when IE is already running, but it won't be in my case. You may need to attach to existing, depending on your context. 
        using (StaticInstanceHelper.Browser = new IE(logonLink)) {
            string authCode = "";

            // Browser reference was failing because IE hadn't started up yet.
            // I'm in the background, so I don't care how long it takes.
            // You may want to do a WaitFor to make it snappier.
            Thread.Sleep(5000);
            if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
                throw new ApplicationException("eTrade down for maintenance.");
            }

            TextField user = StaticInstanceHelper.Browser.TextField(Find.ByName("USER"));

            TextField pass2 = StaticInstanceHelper.Browser.TextField(Find.ByName("PASSWORD"));

            // Class names of the Logon and Logoff buttons vary by page, so I find by text. Seems likely to be more stable.
            Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ByText("Log On"));
            Element btnLogOff = StaticInstanceHelper.Browser.Element(Find.ByText("Log Off"));
            Button btnAccept = StaticInstanceHelper.Browser.Button(Find.ByValue("Accept"));

            TextField authCodeBox = StaticInstanceHelper.Browser.TextField(Find.First());

            if (user != null && btnLogOn != null &&
                user.Exists && pass2.Exists && btnLogOn.Exists) {
                user.Value = username;
                pass2.Value = password;
                btnLogOn.Click();
            }

            Thread.Sleep(1000);
            if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
                Element btnContinue = StaticInstanceHelper.Browser.Element(Find.ByName("continueButton"));
                if (btnContinue.Exists)
                    btnContinue.Click();
            }
            btnLogOff.WaitUntilExists(30);

            // Here we go, finally.
            StaticInstanceHelper.Browser.GoTo(authLink);
            btnAccept.WaitUntilExists(30);
            btnAccept.Click();

            authCodeBox.WaitUntilExists(30);
            authCode = authCodeBox.Value;
            StaticInstanceHelper.Browser.Close();

            return authCode;
        }
    }

能够像这样自动化它意味着我不再关心令牌的有效期。谢谢布拉德姆!

于 2017-01-08T06:30:23.817 回答
1

之前,我使用了一系列的 WebRequest 并手动添加了 headers 来模拟授权页面。这一直有效,直到大约一年前,ETrade 用一些似乎是跟踪信息的东西来复杂化他们的标题。我现在使用http://watin.org/登录,并剥离 Auth 代码。

乱七八糟的代码是这样的:

            using WatiN.Core; // IE Automation
...
            // verify current thread in STA.

            Settings.Instance.MakeNewIeInstanceVisible = false;

            var ieStaticInstanceHelper = new IEStaticInstanceHelper();
            Settings.AutoStartDialogWatcher = false;

            using (ieStaticInstanceHelper.IE = new IE())
            {
                string authCode = "";
                ieStaticInstanceHelper.IE.GoTo(GetAuthorizationLink());

                if (ieStaticInstanceHelper.IE.ContainsText("Scheduled System Maintenance"))
                {
                    throw new ApplicationException("eTrade down for maintenance.");
                }

                TextField user = ieStaticInstanceHelper.IE.TextField(Find.ByName("USER"));

                TextField pass = ieStaticInstanceHelper.IE.TextField(Find.ById("txtPassword"));

                TextField pass2 = ieStaticInstanceHelper.IE.TextField(Find.ByName("PASSWORD"));

                Button btn = ieStaticInstanceHelper.IE.Button(Find.ByClass("log-on-btn"));
                Button btnAccept = ieStaticInstanceHelper.IE.Button(Find.ByValue("Accept"));


                TextField authCodeBox = ieStaticInstanceHelper.IE.TextField(Find.First());

                if (user != null && pass != null && btn != null &&
                    user.Exists && pass2.Exists && btn.Exists)
                {
                    user.Value = username;
                    pass2.Value = password;
                    btn.Click();
                }

                btnAccept.WaitUntilExists(30);
                btnAccept.Click();

                authCodeBox.WaitUntilExists(30);
                authCode = authCodeBox.Value;

                SavePin(authCode);
            }
于 2016-12-06T21:26:43.213 回答