目录

充电学习中...

X

神器CefSharp在爬虫方面的应用

神器CefSharp在爬虫方面的应用

一、前言概述

提起Web端的自动化,模拟渲染,相信虫子们都能想到一些比较流行的框架,例如Selenium,Puppeteer等。其中Selenium利用的是WebDriver,支持Python,Java,.Net等多种语言,而Puppeteer出自谷歌之手,基于nodejs。两者的底层都是通过DevTools Protocol来操控Chrome的。诚然,这两个框架在Web自动化领域的应用非常强大,其中Puppeteer更是可以对网络请求进行拦截,但是用在爬虫上,hhhhhh,先放张图撑撑场面:

timg.jpg

你可能不服,别急,继续往下看就知道了。

自动化测试框架为开发者(这里只针对爬虫而言)带来了很多方便,例如处理ajax请求的动态加载,前端JS混淆加密,人机验证,请求拦截等,但是却有个致命的缺点,就是拥有很多和正常浏览器不同的特征,而这些特征一旦被侦测到,爬虫就无法再进行下去,而目前的大厂们,几乎都或多或少的会进行检测,例如瑞数加密的JS,淘宝Web端页面等,均有检测代码。下面给出几个检测及绕过检测点的例子:

反爬虫中chrome无头浏览器的几种检测与绕过方式

简单来说,目前比较流行的两种方式绕过检测。1.对于部分能够直接修改的特征值,可以通过JS hook的方式进行修改。2.对于其他修改困难的特征值,可以通过操控请求,对目标网站响应的JS文件进行篡改,删除或者替换其用于检测的代码,以达到绕过的目的。

但是,上面两种方法的弊端也很明显,方法1所修改的特征值,可能在刷新页面后失效,而方法2,首先Selenium无法直接做到,需要外挂代理进行实现,而Puppeteer有原生支持。其次,随着反爬虫技术的不断提升,特征检测的位置可能会越来越多,用于检测的JS代码也可能经过了层层的混淆加密,可读性极差,从中找到检测逻辑可能是个极为繁琐的工作。

二、CefSharp介绍

开源项目地址
我们可以将CefSharp理解为一个浏览器核心,类似WebKit东西,然后可以嵌入程序中使用,跟微软的WebBrowser控件有点像,不同的是WebBrowser是IE内核的,像ifram等标签都无法支持,而CefSharp基于Chrome内核,可以做到完美兼容。
换句话说,上面提到的特征检测和反检测的斗争,我不陪你玩了,我们直接拿Chrome内核写个浏览器,随你怎么检测。

三、CefSharp爬虫方面的应用及配置

1、强制当前页打开链接

由于我们使用的是基于Chrome内核的真实浏览器,那么为了防止采集过程中,多个窗口弹出影响采集,需要强制CefSharp使用当前页来打开链接,默认是新窗口。
实现方法很简单,实现ILifeSpanHandler接口的OnBeforePopup方法即可代码如下:

        public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
        {
            newBrowser = null;
            var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;
            chromiumWebBrowser.Load(targetUrl);
            return true;
        }

2、UA,代理等配置

            var settings = new CefSettings()
            {
                //设置UA
                UserAgent = "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Mobile Safari/537.36",
            };
            settings.CefCommandLineArgs.Add("proxy-server", "127.0.0.1:8888");
            //加载Flash
            //settings.CefCommandLineArgs.Add("ppapi-flash-path", AppDomain.CurrentDomain.BaseDirectory + "\\Plugins\\pepflash\\pepflashplayer.dll");
            Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);

3、Cookie操作

            ICookieManager cookieManager = CefSharp.Cef.GetGlobalCookieManager();

            //读取Cookie
            var Cookies = cookieManager.VisitAllCookiesAsync().Result;
            //设置Cookie
            var domain = "blog.nb-ray.com";
            var name = "CookieName";
            var value = "CookieValue";
	    //设置Cookie
            cookieManager.SetCookie($"http://{domain}", new CefSharp.Cookie
            {
                Domain = domain,
                Name = name,
                Value = value,
                Expires = DateTime.MinValue
            });

4、执行JS

 browser.EvaluateScriptAsync("alert(navigator.webdriver)").Result.Result;

这边我们输出navigator.webdriver特征值看看:
image.png
因为使用的是货真价实的浏览器,navigator.webdriver字段的值意料之中是undefined

5、请求拦截

这一部分稍微麻烦点,需要重写RequestHandler,ResourceRequestHandler两个类,并实现IResponseFilter接口,为了方便使用,这部分我已经进行了封装,项目代码放在文章末尾,下面先给出拦截部分的几个应用

  RequestHandler = new TrackRequestHandler(new TrackConfig
                {
		//这里请求进行一系列操作,传入Action或者Func类型的委托
		}

5.1、请求阻断

例如 可以阻断所有请求地址以png结尾的请求,这里的请求会直接取消,而不会到达服务端

       BlockingRequest = req =>
                    {
                        return req.Url.EndsWith(".png");
                    }

5.2、修改请求

在请求发出前进行修改,例如修改请求的地址,Head,body等,下面的示例代码把请求地址替换为百度

                    EditRequest = req =>
                    {
                        if (req.Url.Contains("http://blog.nb-ray.com/articles/2020/07/02/1593671843093.html"))
                        {
                            req.Url = "https://www.baidu.com/";
                        }
                        return req;
                    },

5.3、修改响应

修改来自服务端的响应,下面的代码将地址"http://blog.nb-ray.com/articles/2020/07/02/1593671843093.html"
响应的内容修改为字符串"xxynb"

                    ReplaceFunc = (row, bts) =>
                    {
                        //响应内容转字符串 可能是片段
                        var encode = Encoding.UTF8;
                        try
                        {
                            encode = Encoding.GetEncoding(row.charset);
                        }
                        catch (Exception)
                        {

                        }
                        var str = encode.GetString(bts);
                        if (row.url.Contains("http://blog.nb-ray.com/articles/2020/07/02/1593671843093.html"))
                        {
                            //同一请求分为多个片段传输的情况下 全文替换只生效一次
                            if (reptime == 0)
                            {
                                //例如 将响应内容替换为 "xxynb" 
                                bts = Encoding.GetEncoding(row.charset).GetBytes("xxynb");
                                reptime++;
                            }
                            else
                            {
                                bts = Encoding.GetEncoding(row.charset).GetBytes(" ");
                            }
                        }
                        else
                        {
                            reptime = 0;
                        }
                        return bts;
                    }

5.3、请求收集

此处可以得到所有请求的详细数据,可以在这里进行爬虫数据清洗等操作,例如下文代码将响应内容进行输出:

                    RowFunc = row =>
                    {
                        if (row.url.Contains("articles"))
                        {
                            Console.WriteLine(row.responsedatastring);
                        }
                    }

四、项目地址

DEMO项目地址


标题:神器CefSharp在爬虫方面的应用
作者:Cubeeeee
地址:http://blog.nps.fuguicun.com/articles/2020/07/06/1594006931150.html