xml地图|网站地图|网站标签 [设为首页] [加入收藏]
中的闭包,信息采集ajax数据
分类:编程

1、 闭包的含义

首先闭包并不是针对某一特定语言的概念,而是一个通用的概念。除了在各个支持函数式编程的语言中,我们会接触到它。一些不支持函数式编程的语言中也能支持闭包(如java8之前的匿名内部类)。

在看过的对于闭包的定义中,个人觉得比较清晰的是在《JavaScript高级程序设计》这本书中看到的。具体定义如下:

闭包是指有权访问另一个函数作用域中的变量的函数

注意,闭包这个词本身指的是一种函数。而创建这种特殊函数的一种常见方式是在一个函数中创建另一个函数。

代码:

.net 信息采集ajax数据

2、 在C# 中使用闭包(例子选取自《C#函数式程序设计》)

下面我们通过一个简单的例子来理解C#闭包

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetClosureFunction()(30));
    }

    static Func<int, int> GetClosureFunction()
    {
        int val = 10;
        Func<int, int> internalAdd = x => x + val;

        Console.WriteLine(internalAdd(10));

        val = 30;
        Console.WriteLine(internalAdd(10));

        return internalAdd;
    }
}

上述代码的执行流程是Main函数调用GetClosureFunction函数,GetClosureFunction返回了委托internalAdd并被立即执行了。

输出结果依次为20、40、60

对应到一开始提出的闭包的概念。这个委托internalAdd就是一个闭包,引用了外部函数GetClosureFunction作用域中的变量val。

注意:internalAdd有没有被当做返回值和闭包的定义无关。就算它没有被返回到外部,它依旧是个闭包。

图片 1图片 2

关于.net信息采集的资料很多,但是如果采集的网站是ajax异步加载数据的模式,又如何采集呢?今天就把自己做信息采集时,所遇到的一些问题和心得跟大家分享一下。

3、 理解闭包的实现原理

我们来分析一下这段代码的执行过程。在一开始,函数GetClosureFunction内定义了一个局部变量val和一个利用lamdba语法糖创建的委托internalAdd。

第一次执行委托internalAdd 10 + 10 输出20

接着改变了被internalAdd引用的局部变量值val,再次以相同的参数执行委托,输出40。显然局部变量的改变影响到了委托的执行结果。

GetClosureFunction将internalAdd返回至外部,以30作为参数,去执行得到的结果是60,和val局部变量最后的值30是一致的。

val 作为一个局部变量。它的生命周期本应该在GetClosureFunction执行完毕后就结束了。为什么还会对之后的结果产生影响呢?

我们可以通过反编译来看下编译器为我们做的事情。

为了增加可读性,下面的代码对编译器生成的名字进行修改,并对代码进行了适当的整理。

class Program
{
    sealed class DisplayClass
    {
        public int val;

        public int AnonymousFunction(int x)
        {
            return x + this.val;
        }
    }

    static void Main(string[] args)
    {
        Console.WriteLine(GetClosureFunction()(30));
    }

    static Func<int, int> GetClosureFunction()
    {
        DisplayClass displayClass = new DisplayClass();
        displayClass.val = 10;
        Func<int, int> internalAdd = displayClass.AnonymousFunction;

        Console.WriteLine(internalAdd(10));

        displayClass.val = 30;
        Console.WriteLine(internalAdd(10));

        return internalAdd;
    }
}

编译器创建了一个匿名类(如果不需要创建闭包,匿名函数只会是与GetClosureFunction生存在同一个类中,并且委托实例会被缓存,参见clr via C# 第四版362页),并在GetClosureFunction中创建了它实例。局部变量实际上是作为匿名类中的字段存在的。

private static ImageCodecInfo GetImageCodecInfo(ImageFormat imageFormat)
{
    ImageCodecInfo[] imageCodecInfoArr = ImageCodecInfo.GetImageDecoders();
    foreach (ImageCodecInfo imageCodecInfo in imageCodecInfoArr)
    {
        if (imageCodecInfo.FormatID == imageFormat.Guid)
        {
            return imageCodecInfo;
        }
    }
    return null;
}

采集网站的几种方式与利弊:

4、 C#7对于不作为返回值的闭包的优化

如果在vs2017中编写第二节的代码。会得到一个提示,询问是否把lambda表达式(匿名函数)托转为本地函数。本地函数是c#7提供的一个新语法。那么使用本地函数实现闭包又会有什么区别呢?

如果还是第二节那样的代码,改成本地函数,查看IL代码。实际上不会发生任何变化。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetClosureFunction()(30));
    }

    static Func<int, int> GetClosureFunction()
    {
        int val = 10;
        int InternalAdd(int x) => x + val;

        Console.WriteLine(InternalAdd(10));

        val = 30;
        Console.WriteLine(InternalAdd(10));

        return InternalAdd;
    }
}

但是当internalAdd不需要被返回时,结果就不一样了。

下面分别来看下匿名函数和本地函数创建不作为返回值的闭包的时候演示代码及经整理的反编译代码。

匿名函数

static void GetClosureFunction()
{
    int val = 10;
    Func<int, int> internalAdd = x => x + val;

    Console.WriteLine(internalAdd(10));

    val = 30;
    Console.WriteLine(internalAdd(10));
}

经整理的反编译代码

sealed class DisplayClass
{
    public int val;

    public int AnonymousFunction(int x)
    {
        return x + this.val;
    }
}

static void GetClosureFunction()
{
    DisplayClass displayClass = new DisplayClass();
    displayClass.val = 10;
    Func<int, int> internalAdd = displayClass.AnonymousFunction;

    Console.WriteLine(internalAdd(10));

    displayClass.val = 30;
    Console.WriteLine(internalAdd(10));
}

本地函数

class Program
{
    static void Main(string[] args)
    {
    }

    static void GetClosureFunction()
    {
        int val = 10;
        int InternalAdd(int x) => x + val;

        Console.WriteLine(InternalAdd(10));

        val = 30;
        Console.WriteLine(InternalAdd(10));
    }
}

经整理的反编译代码

// 变化点1:由原来的class改为了struct
struct DisplayClass
{
    public int val;

    public int AnonymousFunction(int x)
    {
        return x + this.val;
    }
}

static void GetClosureFunction()
{
    DisplayClass displayClass = new DisplayClass();
    displayClass.val = 10;

    // 变化点2:不再构建委托实例,直接调用值类型的实例方法
    Console.WriteLine(displayClass.AnonymousFunction(10));

    displayClass.val = 30;
    Console.WriteLine(displayClass.AnonymousFunction(10));
}

上述这两点变化在一定程度上能够带来性能的提升,所以在官方的推荐中,如果委托的使用不是必要的,更推荐使用本地函数而非匿名函数。

如果本博客描述的内容存在问题,希望大家能够提出宝贵的意见。坚持写博客,从这一篇开始。

View Code

  1. HttpWebRequest

代码:

利用系统自带HttpWebRequest对象,采集网站内容,优点是采集效率快,但是如果网站是ajax异步加载数据的方式,是采集不到网页内容的,并且网站没有采用ajax的方式,在网页中用到了javascript,比如说:网页内容用document.write的方式输出到网页中的,这种情况也是获取不到内容的。其次还需要知道对方网站的编码格式(就是网页头部中<meta charset="utf-8"/>),如果采集时网站编码格式错误的话,会导致采集的内容是乱码。但这个是小问题,我自己当时查阅资料时找到了别人封装好的方法,但是很惭愧因为不知道作者是谁了,我会把相应的代码下载链接提供给大家。以上的问题是因为js和ajax是需要浏览器去解析的,所以导致了获取不到网页内容。

图片 3图片 4

Help.HttpHelp.HttpRequest("采集的网址");
MemoryStream ms = HttpUtil.HttpDownloadFile(url);
Bitmap bmp = new Bitmap(ms);

EncoderParameters encoderParameters = new EncoderParameters(1);
EncoderParameter encoderParameter = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L);
encoderParameters.Param[0] = encoderParameter;

MemoryStream msCompress = new MemoryStream();
bmp.Save(msCompress, GetImageCodecInfo(ImageFormat.Jpeg), encoderParameters);
Bitmap bmpCompress = new Bitmap(msCompress);
bmpCompress.Save(path);
bmp.Save(path2);

msCompress.Close();
ms.Close();

源码下载地址

View Code

          2.浏览器控件

 

因为当时我开发的时候,用的是cs模式,相信大家同样也会用cs的模式去开发这个功能。既然是cs模式(不考虑美观)的情况下肯定是WinForm,WinForm中有自带的浏览器控件,这个是不好用的,我当时用的是Geckofx,基于火狐内核的一款浏览器控件,但是这方面的资料很少,当时遇到了一些问题都找不到解决方法,但后来还是都解决了。用了该控件就可以获取到ajax异步加载的数据,在网页加载完成之后,延迟几秒钟获取网页内容,就可以很方便的获取到网页内容,缺点是相对第一种方案来说的话会慢一些,因为它是一个浏览器控件,需要渲染html和解析js等操作。

Geckofx下载

本文由澳门新葡亰手机版发布于编程,转载请注明出处:中的闭包,信息采集ajax数据

上一篇:学习路线图,回调函数 下一篇:没有了
猜你喜欢
热门排行
精彩图文