zoukankan      html  css  js  c++  java
  • CefSharp.ChromiumWebBrowser浏览器的一些功能使用

    CefSharp.ChromiumWebBrowser浏览器的一些功能使用

    1.配置支持AnyCpu编译模式

    2.使用Http代理服务

    3.Cookie隔离,每个IWebBrowser实例的数据不共享

    4.使用IResponseFilter获取响应数据

    1.配置支持AnyCpu编译模式

      CefSharp从51版本以后开始支持AnyCpu编译模式,首先需要在当前项目的csproj文件的PropertyGroup节点下第一行增加一个配置项

    <CefSharpAnyCpuSupport>true</CefSharpAnyCpuSupport>

    然后在程序的启动入口配置动态加载目标平台x86/x64的程序集:

    复制代码
       [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
    
                AppDomain.CurrentDomain.AssemblyResolve += Resolver;
                Application.Run(new Form1());
            }
    
            private static Assembly Resolver(object sender, ResolveEventArgs args)
            {
                if (args.Name.StartsWith("CefSharp"))
                {
                    string assemblyName = args.Name.Split(new[] { ',' }, 2)[0] + ".dll";
                    string archSpecificPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                                                           Environment.Is64BitProcess ? "x64" : "x86",
                                                           assemblyName);
                    return File.Exists(archSpecificPath)
                               ? Assembly.LoadFile(archSpecificPath)
                               : null;
                }
                return null;
            }
    复制代码

      这种方法是根据运行的目标平台动态去加载对应的程序集,如果我们能明确运行平台则可以不用加上面的代码逻辑,在当前项目App.config文件的根节点下加入以下配置即可:

    <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="x64"/><!--如果是32位系统,则换成x86-->
      </assemblyBinding>
    </runtime>

    2.使用Http代理服务

    网上一些文章介绍的通过添加命令行参数CefCommandLineArgs的方式,我试了一下不管用,通过 CefSharpSettings.Proxy =  new ProxyOptions("ipadress", "prot", "username",  "password");  这句是可以配置成功的,但这个是全局配置,不能满足独立我要控制每个browser实例各自使用自己的代理服务器。通过在初始化ChromiumWebBrowser的地方加入以下代码可实现动态设置代理。

    复制代码
    var browser = new ChromiumWebBrowser("url", context); 
    browser.RequestHandler = new DefaultRequestHandler(); Cef.UIThreadTaskFactory.StartNew(delegate { var rc = browser.GetBrowser().GetHost().RequestContext; rc.GetAllPreferences(true); var dict = new Dictionary<string, object>(); dict.Add("mode", "fixed_servers"); dict.Add("server", "ipaddress:prot"); //此处替换成实际 ip地址:端口 string error; bool success = rc.SetPreference("proxy", dict, out error); if (!success) { Console.WriteLine("something happen with the prerence set up" + error); } });
    复制代码

    如果代理服务有用户名密码的话,则需要在DefaultRequestHandler类里重写GetAuthCredentials方法,如下:

    复制代码
     protected override bool GetAuthCredentials(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
     {
          if (isProxy)
          {
               callback.Continue("username", "passwrod");
               return true;
          }
          return false;
    }
    复制代码

    这样就可以实现每个ChromiumWebBrowser运行实例独立连接自己的代理服务器了。

    3.Cookie隔离,每个IWebBrowser实例的数据不共享

      要保证多个browser实例之间cookie不共享,就不要在全局设置CefSettings中设置CachePath值,应该在实例的RequestContextSettings中设置,可以设置成每个browser拥有独立的缓存目录。在RequestContext内添加的cookie只有当前browser才能访问,从而实现cookie隔离。

    复制代码
     var setting = new RequestContextSettings()
                    {
                        CachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CefSharp\Cache_" + name),
                        PersistSessionCookies = true,
                        PersistUserPreferences = true
                    };
                    var context = new RequestContext(setting);
                    var cookieManager = context.GetCookieManager(null);
              //这样设置的cookie不是全局的,只有当前browser才能访问
                    cookieManager.SetCookie("domain", new Cookie
                    {
                        Name = "cookiename",
                        Value = "cookievalue",
                        Path = "/"
                    });
                    var browser = new ChromiumWebBrowser("url", context);
    复制代码

    4.使用IResponseFilter获取响应数据

       要获取ChromiumWebBrowser中每个请求的响应body内容并不是那么方便,拿到Frame的html内容倒是很简单,使用IFrame的GetSourceAsync()方法就行,但有时候我们需要单个请求的响应结果,这就需要自定义实现IResponseFilter接口来实现响应数据的拦截。

    复制代码
        
      //DefaultResourceHandler的构造可以放在IRequestHandler的实现类的GetResourceRequestHandler方法内
      class DefaultResourceHandler : ResourceRequestHandler
        {
            protected override IResponseFilter GetResourceResponseFilter(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response)
            {
                if (response.MimeType.Equals("application/json", StringComparison.OrdinalIgnoreCase))
                {
                    return JsonResponseFilter.CreateFilter(request.Identifier.ToString());
                }
                return null;
            }
    
            protected override void OnResourceLoadComplete(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
            {
                var filter = JsonResponseFilter.GetFileter(request.Identifier.ToString()) as JsonResponseFilter;
                if (filter != null)
                {
                    var encode = !string.IsNullOrEmpty(response.Charset)
                        ? Encoding.GetEncoding(response.Charset)
                        : Encoding.UTF8;
                    using (var read = new StreamReader(filter.GetStream(), encode))
                    {
                        var text = read.ReadToEnd();
                        Debug.WriteLine(text);
                    }
                }
            }
        }
    
        public class JsonResponseFilter : IResponseFilter
        {
            private MemoryStream Stream;
    
            public JsonResponseFilter()
            {
                Stream = new MemoryStream();
            }
    
            public FilterStatus Filter(System.IO.Stream dataIn, out long dataInRead, System.IO.Stream dataOut, out long dataOutWritten)
            {
                try
                {
                    if (dataIn == null || dataIn.Length == 0)
                    {
                        dataInRead = 0;
                        dataOutWritten = 0;
    
                        return FilterStatus.Done;
                    }
    
                    dataInRead = dataIn.Length;
                    dataOutWritten = Math.Min(dataInRead, dataOut.Length);
    
                    dataIn.CopyTo(dataOut);
                    dataIn.Seek(0, SeekOrigin.Begin);
                    byte[] bs = new byte[dataIn.Length];
                    dataIn.Read(bs, 0, bs.Length);
    
                    Stream.Write(bs, 0, bs.Length);
    
                    dataInRead = dataIn.Length;
                    dataOutWritten = dataIn.Length;
    
                    return FilterStatus.NeedMoreData;
                }
                catch (Exception ex)
                {
                    dataInRead = dataIn.Length;
                    dataOutWritten = dataIn.Length;
    
                    return FilterStatus.Done;
                }
            }
    
            public bool InitFilter()
            {
                return true;
            }
    
            public Stream GetStream()
            {
                Stream.Seek(0, SeekOrigin.Begin);
                return Stream;
            }
    
            public void Dispose()
            {
            }
    
            private static Dictionary<string, IResponseFilter> _dictionary = new Dictionary<string, IResponseFilter>();
    
            public static IResponseFilter CreateFilter(string id)
            {
                var filter = new JsonResponseFilter();
                _dictionary[id] = filter;
                return filter;
            }
    
            public static IResponseFilter GetFileter(string id)
            {
                if (_dictionary.ContainsKey(id))
                {
                    var filter = _dictionary[id];
                    _dictionary.Remove(id);
                    return filter;
                }
                return null;
            }
        }
    复制代码

      为了截获响应数据绕了这么一大圈有点费劲,不过人家这种设计也是为了方便外部扩展,可以针对不同响应类型的response来实现IResponseFilter过滤器。

  • 相关阅读:
    Nutch的配置(使用MySQL作为数据存储)
    MySQL简单实现多字段模糊查询
    nutch的一些基础整理
    Java分布式爬虫Nutch教程——导入Nutch工程,执行完整爬取
    Nutch2 WebPage写入数据库的过程分析
    Nutch2 WebPage 字段解释
    nutch如何修改regex-urlfilter.txt爬取符合条件的链接
    Run Nutch In Eclipse on Linux and Windows nutch version 0.9
    Linux Mint 17.1 安装全配置
    Ubuntu(Linux Mint):sudo apt-get upgrade升级失败
  • 原文地址:https://www.cnblogs.com/guanshan/p/guan2021-4-15.html
Copyright © 2011-2022 走看看