笨笨图片批量下载器 V0.3 beta[C# | WinForm | 正则表达式 | HttpWebRequest | Async异步编程] new

前言

     从【笨笨图片批量抓取下载 V0.2 beta】到【笨笨图片批量下载器 V0.3 beta】时间将近2个月,不是说这个升级版本开发了这么久,实在是懒,呵呵: )再加有时候工作忙、学习,多的时间就不愿意动了,现在都感觉辜负了上一版N多朋友的支持了,不过这将近一个星期时间我按计划完成了这个小软件版的升级开发,并且依然和上两个版本一样保持源代码开源,文章最后有下载地址,以下是这个版本相比上个版本的特点:

     1.     加入图片是否重命名。

     2.     加入异步线程池控制(? 随后解释)。

     3.     加入图片大小限制。

     4.     加入支持指定网址内CSS文件内的图片下载。

     5.     加入了正则表达式即时配置更改(应变正则表达式缺陷)。

     6.     优化部分代码。

     7.     修改部分统计错误。

 

感谢

     1.     感谢热心回帖并提供建议的部分网友:Stoneq、liyundong、寻梦E.net、Caspar Jiong 、laoda、lexus 等

     2.     感谢Google Code Seach,在我找不到任何我能看懂的中英文资料时(尤其是异步线程池控制),她提供了我参考代码!!

     3.     感谢女朋友在精神上的莫大鼓舞!

 

正文

     1.     和以往一样,先来一张图,然后看图说话:)

             

          说明:界面上V0.3与V0.2差不多,剔除了图片类型复选框,如果你需要下载指定类型的图片的话,可以从 配置->设置正则表达式图片链接分析 里面直接修改匹配图片的正则表达式,最末端就是图片的文件类型了,如:(jpg|jpeg|png|ico|bmp|gif);状态栏增加了一个实际下载图片数量,可以实时的显示当前下载图片的张数;多了一个[重命名]和[下载页面包含CSS文件内的图片],后者就不用说明了,但是前者需要说明一下:这个地方是费了我不少时间了,我原先的重命名方案是DateTime.Now.ToString("yyyyMMddHHmmssfff"),后来又加上了new Random().Next(100)和lock,都不对,显示下载图片的数量和文件夹里面的图片数量不符合,并且选择重命名和不选择重命名文件夹实际图片数相差较大,几张到几十张不等,最后改用了System.Guid.NewGuid()至此基本正确符合,所有猜想,DateTime.Now和Random在遇到异步多线程应该会出现脏读吧?!

          栏目里面的配置,开始想的时候觉得有那么点画蛇添足的味道,但是觉得有促进和各位朋友正则表达式交流的作用而保留下来了,所以期待牛人给予友情提示:)

 

     2.     接下来贴部分关键代码和讲解,这里只讲异步的代码,源码注释也比较多:


/// <summary>
        /// 开始异步分析下载
        /// </summary>
        /// <param name="url"></param>
        /// <param name="savePath"></param>
        private void AsyncAnalyzeAndDownload(string url, string savePath)
        {
            this.uriString = url;
            this.savePath = savePath;

            初始化全局变量

            分析计时开始

            using (WebClient wClient = new WebClient())
            {
                AutoResetEvent waiter = new AutoResetEvent(false);
                //为异步结果返回传参
                wClient.QueryString.Add("url", uriString);
                wClient.QueryString.Add("IsInclueCssImages", _IsInclueCssImages.ToString());
                wClient.Credentials = CredentialCache.DefaultCredentials;
                wClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(AsyncURIAnalyze);
                wClient.DownloadDataAsync(new Uri(uriString), waiter);
                //waiter.WaitOne();     //阻止当前线程,直到收到信号
            }
        }

          说明:这里是异步分析和下载的入口,传入网址、保存路径、是否分析在CSS文件内的图片参数。

 ----------------------------------------------------------------------------------------------------------


/// <summary>
        /// 异步分析指定网址返回的数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void AsyncURIAnalyze(Object sender, DownloadDataCompletedEventArgs e)
        {
            AutoResetEvent waiter = (AutoResetEvent)e.UserState;
            WebClient nWC = sender as WebClient;
            bool IsMatchCss = true;
            bool IsInclueCssImages = Convert.ToBoolean(nWC.QueryString["IsInclueCssImages"]);
            try
            {
                if (!e.Cancelled && e.Error == null)
                {
                    string dnDir = string.Empty;
                    string domainName = string.Empty;
                    string uri = nWC.QueryString["url"];

                    if (!uri.StartsWith("http://") && !uri.StartsWith("https://"))
                        uri = string.Concat("http://", uri);

                    //获得域名 http://www.sina.com
                    domainName = GetDomain(uri);

                    //获得域名最深层目录 http://www.sina.com/mail/
                    dnDir = GetFullPath(domainName, uri);

                    //获取数据
                    string pageData = Encoding.UTF8.GetString(e.Result);

                    //匹配全路径
                    AnalyzeContent(Regex.Matches(pageData, ImagePattern), domainName, dnDir);

                    //是否下载页面包含CSS文件内的图片
                    if (IsInclueCssImages)
                    {
                        //匹配CSS文件 //[\w=/]*((\.css){1})
                        MatchCollection mc = Regex.Matches(pageData, CssPattern, RegexOptions.IgnoreCase);
                        for (int i = 0, j = mc.Count; i < j; i++)
                        {
                            string item = mc[i].Value;

                            //短路径处理
                            if (!item.StartsWith("http://") && !item.StartsWith("https://"))
                                item = (item[0] == '/' ? domainName : dnDir) + item;

                            using (WebClient wClient = new WebClient())
                            {
                                AutoResetEvent are = new AutoResetEvent(false);
                                wClient.QueryString.Add("url", item);
                                wClient.QueryString.Add("IsOver", i == j - 1 ? "1" : "0");
                                wClient.QueryString.Add("IsInclueCssImages", "false");
                                wClient.Credentials = CredentialCache.DefaultCredentials;
                                wClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(AsyncURIAnalyze);
                                wClient.DownloadDataAsync(new Uri(item), are);
                            }
                        }
                        if (mc.Count == 0)
                            IsMatchCss = false;
                    }
                }
            }
            finally
            {
                //waiter.Set();

                if ((IsInclueCssImages && !IsMatchCss) || (!IsInclueCssImages && string.IsNullOrEmpty(nWC.QueryString["IsOver"])) || (!string.IsNullOrEmpty(nWC.QueryString["IsOver"]) && "1" == nWC.QueryString["IsOver"]))
                {
                    lock (slock)
                    {
                        #region 分析计时结束

                        QueryPerformanceCounter(ref count1);
                        count = count1 - count;
                        
                        toolStripStatusLabel1.Text = "分析完毕!";
                        toolStripStatusLabel2.Text = string.Format(" | 分析耗时:{0:#.####}秒", (double)(count) / (double)freq);

                        #endregion

                        //分析完毕
                        isAnalyzeComplete = true;
                    }
                    Application.DoEvents();
                }
            }
        }

          说明:这部分代码是程序的主体部分之一,如果需要分析CSS文件内的图片,则采用递归调用本方法,AnalyzeContent方法为分析图片链接核心代码,这里发现比较有意思的是可以为下次接受的数据传参,即自己传给自己,这样对于判断是否分析完毕有很大便利性,代码如:wClient.QueryString.Add("url", item);

------------------------------------------------------------------------------------------------------


/// <summary>
        /// 分析链接
        /// </summary>
        /// <param name="mc"></param>
        /// <param name="domainName"></param>
        /// <param name="dnDir"></param>
        private void AnalyzeContent(MatchCollection mc, string domainName, string dnDir)
        {
            List<string> urlList = new List<string>();
            foreach (Match mt in mc)
            {
                string item = mt.Value;

                /*  处理图片正则表达式的缺陷,即图片必须带域名,如:
                 *      当前正则表达式匹配http://www.icoxxx.com
                 *      匹配结果为http://www.ico
                 */
                if (item.Length > 8 && item.IndexOf('/', 9) == -1)
                    continue;

                //短路径处理
                if (!item.StartsWith("http://") && !item.StartsWith("https://"))
                    item = (item[0] == '/' ? domainName : dnDir) + item;

                //处理../
                if (item.IndexOf("../") != -1)
                {
                    List<string> urls = new List<string>();
                    urls.AddRange(item.Split('/'));
                    for (int i = 0; i < urls.Count; i++)
                        if ("..".Equals(urls[i]))
                        {
                            urls.RemoveRange(i - 1, 2);
                            i -= 2;
                        }
                    item = Join("/", urls);
                }

                if (!urlList.Contains(item))
                {
                    urlList.Add(item);

                    lock (slock)
                    {
                        imgUrlList.Add(item);
                    }

                    //实时显示分析结果
                    AddlbShowItem(item);

                    //边分析边下载
                    HttpWebRequest hwr = WebRequest.Create(item) as HttpWebRequest;
                    hwr.AllowWriteStreamBuffering = false;
                    //hwr.ReadWriteTimeout = 5 * 1000; //默认超时30秒
                    IAsyncResult res = hwr.BeginGetResponse(new AsyncCallback(AsyncDownLoad), hwr);
                    //加入线程池控制
                    ThreadPool.RegisterWaitForSingleObject(res.AsyncWaitHandle, new WaitOrTimerCallback(timeoutCallback), hwr, Timeout, true);
                }
            }
        }

          说明:这块代码是分析图片链接的代码,由于正则表达式缺陷,加入了一些处理;加入了线程池控制,说到这里,关于异步线程池的控制几乎没找到中文资料,就在Google Code Seach里面搜索到了以下代码片段:

IAsyncResult res = hwr.BeginGetResponse(new AsyncCallback(AsyncDownLoad), hwr);
                    //加入线程池控制
                    ThreadPool.RegisterWaitForSingleObject(res.AsyncWaitHandle, new WaitOrTimerCallback(timeoutCallback), hwr, Timeout, true);

          具体如何测试是否加入连接池也没有深入的研究了,所以前面提到新加功能时后面打了一个问号,望资深人士帮忙分析下:)

-------------------------------------------------------------------------------------------------------------


/// <summary>
        /// 异步接受数据
        /// </summary>
        /// <param name="asyncResult"></param>
        public void AsyncDownLoad(IAsyncResult asyncResult)
        {
            #region 下载计时开始

            lock (slock)
            {
                if (cfreq == 0L)
                {
                    ccount = 0L; 
                    QueryPerformanceFrequency(ref cfreq);
                    QueryPerformanceCounter(ref ccount);
                }
            }

            #endregion
            
            WebRequest request = (WebRequest)asyncResult.AsyncState;
            string url = request.RequestUri.ToString();
            int indexItem = this.lbShow.Items.IndexOf(url);

            //从未下载的列表中删除已经下载的图片
            lock (slock)
            {
                imgUrlList.Remove(url);
            }
            
            try
            {
                WebResponse response = request.EndGetResponse(asyncResult);
                long cLength = response.ContentLength;

                if (cLength > 0 && cLength <= FileSize)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        Image img = Image.FromStream(stream);
                        img.Save(string.Concat(savePath, "/", GetFileName(url)));
                        img.Dispose();
                        stream.Close();
                    }
                    //allDone.Set();

                    //成功下载
                    if (indexItem >= 0 && indexItem <= this.lbShow.Items.Count)
                        SetlbShowItem(indexItem, "√  ");
                }
                else if (cLength == 0L && indexItem >= 0)
                    SetlbShowItem(indexItem, "×  ");
                else if (indexItem >= 0)
                    SetlbShowItem(indexItem, "!  ");    //表示图片过大或过小
            }
            catch (Exception)
            {
                if (indexItem >= 0)
                    SetlbShowItem(indexItem, "×  ");
            }

        }

          说明:这部分代码是异步下载的代码,加入了人性化了多符号标示文件的状态。

          代码注释后可讲解的就不多了,比较难的就是异步中同步数据控制,得控制好了并且尽量少,否则对性能能较大影响,大伙有兴趣的话看代码吧:)

 

结束

          感谢能阅读到此处的网友,希望能多多交流关于异步、同步、正则表达式方面的经验,不吝赐教!在下载中可能出现其他意外情况,导致图片已经下载完毕但是下载图片的按钮不可用,状态栏显示正在下载...,持续时间超过1分钟,那么可能是哪里出了BUG,呵呵!可以强制关闭然后重新启动程序下载,或者确认已经下载完毕,状态没法恢复可以从工具->初始化来重置一下。

 

补充

     感谢 Jon.Hong 的批评,确实下载回来执行有问题,原因是因为写文章的时候临时改了几个参数忘了编译测试了,已经更新了下载文件,不过查看源代码直接调试也行!

     做这个小软件只有两个目的:

          1.     学习交流

          2.     希望能帮得到大家

     希望和大家多多交流使用到的相关技术,以期能积累更多这方面的经验,谢谢大家支持!!

转载:http://www.cnblogs.com/over140/archive/2008/10/31/1322786.html

时间: 2024-10-07 09:50:24

笨笨图片批量下载器 V0.3 beta[C# | WinForm | 正则表达式 | HttpWebRequest | Async异步编程] new的相关文章

笨笨图片批量抓取下载 V0.2 beta[C# | WinForm | 正则表达式 | HttpWebRequest | Async异步编程]

前言      首次在博客园首页发布文章,一直把自己的文章定位在新手区,也一直这样认为自己的.这段时间看博客园首页挺多的,说实在的有些文章很一般但是得到更多朋友以及高手的指点,所以鼓起勇气把自己花了几天时间改版的图片下载器贴上来,希望能在程序的性能上以及适用上得到更多的宝贵的建议!先谢谢每位看客赏光了 : )   感谢      MSDN     MSDN上异步网络编程的例子是我0.2版的核心所在   运行环境      .NET Framework2.0   开发工具      Microso

百度图片批量下载怎么样

  百度图片下载器是一款可以批量下载百度图片的软件,而且还能帮你删减重复图片.是一款能够根据关键字而下载图片的软件,非常简单实用.下面小编介绍一下它的特点和界面. 百度图片下载器特点介绍: 1.最好用的百度图片下载器,一键快速批量下载百度亿万精美图片; 2.快速高效的图片检索和多任务下载,下载进度实时显示,包括下载速度.耗时.已下载.下载状态等; 3.支持图片即时预览.分享到微博等社交网站.设为桌面壁纸.图片另存为等快捷实用功能; 4.支持批量任务管理,可批量添加关键字下载任务到队列,方便任务批

360浏览器图片批量下载怎么设置

  1. 打开360浏览器,单击菜单栏中的"扩展",在弹出的下拉菜单中选择"扩展首页",打开扩展首页. 2.在搜索框中输入"小乐图客",单击搜索,出现小乐图客应用程序安装页. 3.单击安装,弹出"要添加小乐图客吗"对话框,选择"添加". 4.现在单击菜单栏右上角"更多"按钮,选择小乐图客,打开取图页面. 5.在打开的取图页面,下面图片地址栏框输入现在图片网页地址,如图所示,单击"

360浏览器图片批量下载的设置方法图解

1. 我们在打开的360浏览器的机部点击 菜单栏中的"扩展",然后点击进入到"扩展首页",打开扩展首页. 2.之后我们搜索"小乐图客",然后找到并打开安装程序页面. 3.然后我们点击页面中的"要添加小乐图客吗"之后点击"添加".按钮 4.然后我们再点击"更多"按钮,选择小乐图客,打开取图页面. 5.在打开的取图页面,下面图片地址栏框输入现在图片网页地址,如图所示,单击"Go&q

网易手机图片自由下载器(hta)_hta

复制代码 代码如下: <TITLE>网易文件摄取</TITLE> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <HTA:APPLICATION   ID="MyhyliApp"   APPLICATIONNAME="设置程序"   VERSION="1.0"   SCROL

新浪Flash下载器 [HttpWebRequest | 新浪视频]

前言      哈哈,好久不写小工具了,去年写的笨笨图片批量下载器 V0.3 beta平时还是帮了一些忙的,今天的小工具是 新浪Flash下载器 .可以直接下载swf结尾的文件,如果下载新浪视频文件,你需要右键源代码找到这个flash的伪链接,如http://music.sina.com.cn/yueku/flashPlayer.swf?vid=6851105,然后把这里连接复制到软件输入框点下载就可以了.   运行环境      1.     Microsoft .NET Framework

猎豹浏览器如何批量下载图片

  1.打开猎豹浏览器点击左上角头像→猎豹应用市场(http://store.liebao.cn/) 2.搜索图片批量 3.点击"Fatkun图片批量下载"应用安装 4.打开所需要批量下载的图片页面 5.点击"Fatkun图片批量下载"图片进行图片批量查看(支持所有页面以及当前页面) 6.点击"保存图片"进行图片批量下载 7.弹出提示,需要关闭下载前询问每个文件的保存位置(点击猎豹浏览器点击左上角头像→设置→更多设置里关闭[下载前询问每个文件的保

猎豹浏览器怎么批量下载网页图片

  1.打开猎豹浏览器点击左上角头像→猎豹应用市场(http://store.liebao.cn/) 2.搜索图片批量 3.点击"Fatkun图片批量下载"应用安装 4.打开所需要批量下载的图片页面 5.点击"Fatkun图片批量下载"图片进行图片批量查看(支持所有页面以及当前页面) 6.点击"保存图片"进行图片批量下载 7.弹出提示,需要关闭下载前询问每个文件的保存位置(点击猎豹浏览器点击左上角头像→设置→更多设置里关闭[下载前询问每个文件的保

Python制作CSDN免积分下载器_python

CSDN免积分下载 你懂的. 1.输入资源地址如:http://download.csdn.net/download/gengqkun/4127808 2.输入验证码 3.点击下载,会弹出浏览器下载. 注:成功率在70-80% ,界面很丑,请将就着用. 复制代码 代码如下: #-*-coding:utf-8-*- #python3.3.5 import urllib.parse,urllib.request,http.cookiejar,io,webbrowser import tkinter