CefSharp使用教程二

时间:2021-07-19 13:30:04

1.读取网页源代码

在页面加载完成后处理, 依赖最低环境 4.5.2

async void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)  
{
Log.WriteLog("browser_FrameLoadEnd:" + e.Url);

var result = await browser.GetSourceAsync();
}

如果想在4.0下环境操作需要使用。

 var task = browser.GetSourceAsync();  
task.Wait();
string content = task.Result;

2.获取页面中的指定文件内容(.jpg,.js等)

A.首先需要对ChromiumWebBrowser 的 IRequestHandler RequestHandler进行实现。
B.需要对 IRequestHandler 的IResponseFilter IRequestHandler.GetResourceResponseFilter 方法进行重写。
C.需要写一个类实现 IResponseFilter 接口。
D.然后就可用在IResponseFilter 的
FilterStatus Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten) 方法中,读取指定内容的流了。

3.过滤某些页面内容,例如图片或某些文字

具体实现,参考 2 ,在最后的Filter方法中,对返回的dataOut不进行赋值或者,取到值,然后replace处理,返回其他数据,即可。

4.文件进一步获取,获取完整内容

说明:由于很多文件无法获取到完整内容,再者具体文件内容在Filter里面进行了控制,而Fileter的内容依赖于IRequestHandler所以,外部只能操作Handler得到数据。 所以需要在,Filter和Hanlder类中,使用事件来传递具体的内容。代码如下。 Filter类如下:

public class TestImageFilter : IResponseFilter  
{
public event Action<byte[]> NotifyData;
private int contentLength = 0;
private List<byte> dataAll = new List<byte>();

public void SetContentLength(int contentLength)
{
this.contentLength = contentLength;
}

public FilterStatus Filter(System.IO.Stream dataIn, out long dataInRead, System.IO.Stream dataOut, out long dataOutWritten)
{
try
{
if (dataIn == null)
{
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);
dataAll.AddRange(bs);

if (dataAll.Count == this.contentLength)
{
// 通过这里进行通知
NotifyData(dataAll.ToArray());

return FilterStatus.Done;
}
else if (dataAll.Count < this.contentLength)
{
dataInRead = dataIn.Length;
dataOutWritten = dataIn.Length;

return FilterStatus.NeedMoreData;
}
else
{
return FilterStatus.Error;
}
}
catch (Exception ex)
{
dataInRead = dataIn.Length;
dataOutWritten = dataIn.Length;

return FilterStatus.Done;
}
}

public bool InitFilter()
{
return true;
}
}

Filter类有了,那我们如何知道数据流的具体长度呢?这就需要在Handler的实现的其他方法里面寻找了。

bool IRequestHandler.OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)  
{
//NOTE: You cannot modify the response, only the request
// You can now access the headers
//var headers = response.ResponseHeaders;
try
{
var content_length = int.Parse(response.ResponseHeaders["Content-Length"]);
if (this.filter != null)
{
this.filter.SetContentLength(content_length);
}
}
catch { }
return false;
}

private TestImageFilter filter = null;
public event Action<byte[]> NotifyData;

IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
{
var url = new Uri(request.Url);
if (url.AbsoluteUri.Contains("http://test.test.com/somehead?"))
{
this.filter = new TestImageFilter();
filter.NotifyData += filter_NotifyData;

return filter;
}

return null;
}

void filter_NotifyData(byte[] data)
{
if (NotifyData != null)
{
NotifyData(data);
}
}

此方法位IRequestHandler的一部分实现,通过实现函数:IRequestHandler.GetResourceResponseFilter得到资源文件的长度,然后长度传入Filter,在Filter中控制从而得到整个数据的真正长度。

5.文件进一步获取,获取完整内容(优化,Content-Length不一致)

由于,部分站点,返回数据是分片了的,即:不能通过,Content-Length的长度来判断,程序的流是否完成。
所以需要其他方式处理,即:单个http请求完成的时候,会调用Complete方法,所以可以在这里处理。
下面是测试代码: Filter管理类

public class FilterManager
{
private static Dictionary<string, IResponseFilter> dataList = new Dictionary<string, IResponseFilter>();

public static IResponseFilter CreateFilter(string guid)
{
lock (dataList)
{
var filter = new TestImageFilter();
dataList.Add(guid, filter);

return filter;
}
}

public static IResponseFilter GetFileter(string guid)
{
lock (dataList)
{
return dataList[guid];
}
}
}

TestFilter类,对流进行合并

public class TestImageFilter : IResponseFilter
{
public List<byte> dataAll = new List<byte>();

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);
dataAll.AddRange(bs);

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;
}
}

最后是部分的。IRequestHandler实现代码

public class RequestHandler : IRequestHandler
{
// 略去代码 ...

public event Action<byte[]> NotifyMsg;

IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response)
{
var url = new Uri(request.Url);
if (url.AbsoluteUri.Contains("https://res.wx.qq.com/zh_CN/htmledition/v2/css/base/base2e4e03.css"))
{
var filter = FilterManager.CreateFilter(request.Identifier.ToString());

return filter;
}

return null;
}

void filter_NotifyData(byte[] data)
{
if (NotifyMsg != null)
{
NotifyMsg(data);
}
}

void IRequestHandler.OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
{
if (request.Url.Contains("https://res.wx.qq.com/zh_CN/htmledition/v2/css/base/base2e4e03.css"))
{
var filter = FilterManager.GetFileter(request.Identifier.ToString()) as TestImageFilter;

filter_NotifyData(filter.dataAll.ToArray());
}
}
}