在ASP.NET MVC应用程序中,Web浏览器托管问题

6
在我的Asp.Net MVC网络应用程序中,我需要通过网站获取数据。为了爬取数据,我需要登录到该网站,然后通过点击菜单选项卡到达一个页面,从那个页面上爬取数据。要爬取的网站包含2个框架。我以前在一个Windows项目中使用Windows Web Browser控件实现了Web爬虫。
我按照以下链接将Web浏览器控件集成到我的Web应用程序中:link。在Visual Studio环境下一切正常,我通过Web浏览器爬取了网站上的数据。但是当我尝试托管该应用程序时,它并没有像预期的那样工作。Web浏览器控件无法加载。我搜索了解决方案,但还没有找到可行的解决方案。我尝试使用iframe实现相同的功能,并使用Silverlight Webbrowser控件和使用JavaScript来激活登录事件,但由于跨域访问限制而失败。
那么有没有办法在Asp.Net Web应用程序中登录到网站并爬取数据?是否可以将我的Windows应用程序作为ActiveX控件使用?ActiveX会有跨浏览器问题吗?
非常感谢任何帮助!
提前致谢。

你是否考虑过使用 System.Net.Http.HttpClientSystem.Net.WebRequest 来获取所需的内容? - Daniel Schilling
是的,我尝试使用相同的方法,但失败了。我怀疑是因为要抓取的网站包含多个框架? - BonDaviD
有没有可能你可以创建一个单独的应用程序来处理爬取?然后将结果保存在数据库或文本文件中,以便你的网站可以从中读取。 - NoLifeKing
我的猜测是Windows Server的安全限制阻止了IIS启动浏览器,或者阻止浏览器正常运行。你怎么知道浏览器控件没有被加载? - Daniel Schilling
你不需要浏览器控制来进行网页抓取。使用System.Net.WebRequest。当你获取到包含框架的页面时,解析它以获取每个框架的URL。然后获取你感兴趣的框架。或者如果已知URL,则直接进入该框架。 - yu_sha
1个回答

0

根据评论中的建议,使用Web浏览器的方法似乎过于繁重,并且会受到其他环境限制。您最好的方法是创建一个单独的可测试的存储库来爬取数据 - 如果您确实需要(并且目标数据不会更改),则可以按需或提前使用蜘蛛方法。

是的,如果您尝试将其设置为ActiveX,则不同的浏览器将遇到问题。安全性可能不允许它。这里确实有很多因素;如果您的环境没有受到控制,那么这不是一个很好的选择。

假设您采用按需方法,我强烈建议创建一个可以引用的WebService或类。然后,您可以在服务器端使用开源解析器,例如:

  1. CsQuery(如果文档格式不正确),或者
  2. Fizzler(如果您可以信任文档完整性)。

基本上,您需要进行身份验证,存储您的身份验证cookie,最后通过第二个请求加载目标文档,并填充您的身份验证cookie。将页面馈送到您的解析器(CsQuery或Fizzler)中。

执行登录的示例如下:

private HttpWebRequest PerformLoginRequest(CookieContainer container)
{
    var request = (HttpWebRequest) WebRequest.Create(YOUR_POST_URL);
    request.Method = "POST";
    request.CookieContainer = container;

    _logger.DebugFormat("Attempting login for '{0}'", _username);
    var encoding = new ASCIIEncoding();

    // assumes the un/pw is stored in a field
    var credentials = string.Format("username={0}&password={1}", _username, _password);
    byte[] data = encoding.GetBytes(credentials);

    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = data.Length;

    using (var requestStream = request.GetRequestStream())
    {
        try
        {
            requestStream.Write(data, 0, data.Length);
        }
        catch (Exception e)
        {
            _logger.Error("Error in login attempt.", e);
        }
        finally
        {
            requestStream.Close();
        }
    }
    return request;
}

返回的 cookies 容器将有一个 set-cookie,您需要解析它,以便后续请求正确呈现身份验证位。我不得不这样做,并根据我在 SO 上找到的代码进行了工作,但现在找不到参考了。它可能看起来像这样(解释在 Set-Cookie 这里):

private static CookieContainer ProcessCookieContainer(HttpWebRequest request, CookieContainer container)
{
    var response = (HttpWebResponse) request.GetResponse();
    var cookierReader = new StreamReader(response.GetResponseStream());
    string htmldoc = cookierReader.ReadToEnd();

    var cookieHeader = response.GetResponseHeader("Set-Cookie");
    response.Close();

    container = new CookieContainer();
    foreach (var cookie in cookieHeader.Split(','))
    {
        // these are ; seperated name/value pairs
        var split = cookie.Split(';');
        string name = split[0].Split('=')[0];
        string value = split[0].Split('=')[1];

        // create the cookie with the domain
        var c = new Cookie(name, value) {Domain = "YourCookieDomain.com"};

        container.Add(c);
    }
    return container;
}

而要加载一个文档进行解析,你可能会这样操作:

public string GetValueFromSomePage(int first, string second)
{
    var container = new CookieContainer();

    // do login
    var request = PerformLoginRequest(container);

    // chew on cookies
    container = ProcessCookieContainer(request, container);

    var result = string.Empty;
    var requestUrl = string.Format("http://YourUrlWithParams.com/?first={0}&second={1}", 123, "abc");
    var request = (HttpWebRequest)WebRequest.Create(requestUrl);
    request.CookieContainer = container;

    using (var serverResponse = (HttpWebResponse)request.GetResponse())
    {
        try
        {
            var reader = new StreamReader(serverResponse.GetResponseStream());
            var responseDoc = new CQ(reader);

            // do something with CSS selectors...
            result = responseDoc["input[name=name]"].FirstElement().Value;

        }
        catch (Exception e)
        {
            _logger.Error("Error fetching data.", e);
        }
        finally
        {
            serverResponse.Close();
        }
    }

    return result;
}

希望这能有所帮助。这里有一些需要注意的地方,但考虑到你的任务性质,你可能已经预料到了。

祝好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接