自己动手写美女图片下载器

前言:看到标题可能会有人觉得似曾相识,没错,这篇博文的来源正是根据杨中科老师的《百度美女图片下载器开发教程.Net版》。因为我也观看了该教程,觉得很好玩,于是乎想自己独立完成一次,作为对之前基础学习内容的回顾和运用。以博文的形式和大家分享整个开发过程主要是想借此机会来重新整理下思路、锻炼下自己的表达能力。您如果对下面要用到知识点很熟悉,可忽略此文。

一.主要技术

  • Winform常用控件的基本使用
  • HttpWebRequest请求其他网站内容
  • Newtonsoft.Json.dll组件解析JSON数据
  • IO文件流的读写操作
  • 多线程和跨线程更新UI控件
  • IrisSkin4.dll组件美化皮肤

二.需求分析

  (1)首先,我们打开百度图片,输入搜索关键字“美女”,这时大片大片的美女扑面而来。想要批量下载这些“美女”就必须想办法获取每张图片的请求地址,但是百度服务器不会给我们提供具体的下载链接,我们只能尝试去分析、去测试。在拖动下拉框的时候我们会发现一个现象,那就是图片是实时加载显示的,并非是当我们打开该网页一次性加载完的。我们也就大致明白图片是通过异步请求来加载的,这也就是通常说的AJAX请求。 透过现象看本质,我们按下"F12"进入到开发者模式,来看看它的异步请求到底是怎么回事,另外它又会响应给浏览器什么东西呢。

  执行操作:按下“F12”—>重新加载页面—>查看XHR请求—>拖动滚动条。出现下图界面:

  (2)上图中的两个异步请求就是在拖动滚动条时产生的,也就是用来异步加载下一页图片的请求地址。接下来我们具体查看其中一个请求的响应内容:

  观察得知,响应内容为JSON格式的数据,其中我们在 imgs数组节点中找到了我们想要的东西。imgs数组包含索引为0-59共60个JSON对象,经过测试对比,每个对象对应一张图片信息,其中objURL为每张图片真实下载地址。60个对象,也就意味着每次请求会加载60张图片,每次请求算一页。

  (3)接下来我们对比分析上面两个向百度服务器异步请求URL:

1.http://image.baidu.com/search/avatarjson?tn=resultjsonavatarnew&ie=utf-8&word=%E7%BE%8E%E5%A5%B3&cg=girl&pn=60&rn=60&itg=0&z=0&fr=&width=&height=&lm=-1&ic=0&s=0&st=-1&gsm=350700003c
2.http://image.baidu.com/search/avatarjson?tn=resultjsonavatarnew&ie=utf-8&word=%E7%BE%8E%E5%A5%B3&cg=girl&pn=120&rn=60&itg=0&z=0&fr=&width=&height=&lm=-1&ic=0&s=0&st=-1&gsm=7107000078

首先,它们都是GET请求并且请求地址相同。不过跟在地址后面的个别参数值有差异:其中一个是“pn”,另外一个是“msg”。

“pn”参数:其值分别为60、120,根据第(2)条叙述, 故推测"pn"为图片的加载数量;

"gsm"参数:没太搞明白,根据百度结果,推测"gsm"为一通信标准,为随机值,不影响请求过程;

“word”参数:"%E7%BE%8E%E5%A5%B3" 通过UrlDecode解码后,内容为“美女”,推测"word"为搜索关键字。

分析到此,我们就可以去向百度服务器发送一个请求,咱们自己来定义搜索关键字和请求图片数。这也是实现了这个开发过程中最关键一步。

  小结:通过前面三个部分的叙述,我们分析了美女图片的请求过程和响应内容。如此以来,我们开发百度美女图片下载器就有了大致方向:模拟浏览器向百度服务器发送请求,解析响应任容获取图片地址,下载保存到本地。

三.解决方案

3.1界面搭建

  根据分析结果,我们从请求地址中得到了两个可控制元素:搜索关键字和每次加载的图片数量。紧接着我们就可以搭建好软件界面:

  这里大致简述下软件的工作流程:当用户输入搜索关键字和获取页数,后台开启一个新线程请求百度服务器,请求成功接着解析响应JSON中的imgs数组中的每个图片真实下载地址objURL,然后再依次请求每个objURL,最终以文件流的形式保存到本地文件夹中。

3.2核心代码解读

  3.2.1 开启一个新线程(防止界面假死)请求自定义的地址

            string keyWord = Uri.EscapeDataString(txtKeyWord.Text);    //获取UrlEncode编码后的关键字
            int pageCount = Convert.ToInt32(numPage.Value);        //获取请求页的数量
            string url = string.Format(@"http://image.baidu.com/search/avatarjson?tn=resultjsonavatarnew&ie=utf-8&word={0}&cg=girl&pn={1}&rn=60&itg=1&z=0&fr=ala&lm=-1&ic=0&s=0&st=-1&gsm=aa0a0000b4", keyWord, pageCount * 60);
            thread = new Thread(() =>
            {
                ProcessDownload(keyWord,url, pageCount);          //处理请求
            });
            thread.Start();

  3.2.2 HttpWebRequest向百度服务器发送请求的处理

        private void ProcessDownload(string keyWord, string reqUrl, int pageCount)
        {
            for (int i = 0; i < pageCount; i++)
            {
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(reqUrl);
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        #region 响应成功
                        using (Stream stream = response.GetResponseStream())
                        {
                            using (StreamReader reader = new StreamReader(stream))
                            {
                                string responseBody = reader.ReadToEnd();
                                try
                                {
                                    DownloadPage(responseBody);   //下载当前页的所有图片
                                }
                                catch (Exception ex)
                                {
                                    AppendLog(ex.Message);        //打印异常日志
                                }
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        ShowMsg("获取第" + (1 + i) + "页失败,错误信息:" + response.StatusCode);
                    }
                }
            }
        }

  3.2.3 用第三方组件Newtonsoft.Json.dll解析JSON响应内容

        private void DownloadPage(string responseBody)
        {
            JObject bodyRoot = (JObject)JsonConvert.DeserializeObject(responseBody);
            JArray imgsRoot = (JArray)bodyRoot["imgs"];
            for (int i = 0; i < imgsRoot.Count; i++)
            {
                JObject img = (JObject)imgsRoot[i];
                string url = Convert.ToString(img["objURL"]);
                try
                {
                    DownloadImage(url);     //根据url下载图片
                }
                catch (Exception ex)
                {
                    AppendLog(ex.Message);
                }
            }
        }

   3.2.4 下载图片保存到本地

        private void DownloadImage(string imgUrl)
        {
            string savePath = Path.Combine(txtSaveDir.Text, (Path.GetFileName(imgUrl)));   //设置图片的保存目录
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(imgUrl);
            request.Referer = "http://www.baidu.com/";                                     //伪造该请求是百度自己的请求,欺骗服务器
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        using (FileStream fsWrite = new FileStream(savePath, FileMode.Create))
                        {
                            stream.CopyTo(fsWrite);
                        }
                    }
                    proDownload.BeginInvoke(new Action(() =>
                    {
                        proDownload.Value += 1;    //异步更新进度条,解决跨线程更新控件的问题
                    }));
                }
                else
                {
                    ShowMsg("图片下载失败,错误信息:" + response.StatusCode);
                }
            }

        }

  这里只列举了部分核心代码,初学且有兴趣的朋友请参考完整代码:点击下载

3.3皮肤美化

  关于Winform窗体美化,目前大致了解是有两种方式:第一种是重写Winform本身的控件,不过这需要非常熟悉控件的各个属性和事件并且要求具有很高的GDI绘图技术。第二种是借助第三方Winform皮肤组件。我果断选择了第二种方式,毕竟修为不够。这里,就是借助目前还很流行的“IrisSkin4.dll”组件实现皮肤美化效果。

  “IrisSkin4.dll”怎样使用呢?由于我们的下载器程序只有一个窗体,所以可以使用下面的简单方式:

1、将IrisSkin4.dll程序集放到项目文件Debug文件夹下,然后在工程中添加“引用”;

2、在窗体后置类中添加命名空间:using Sunisoft.IrisSkin;

3、在窗体类的构造函数中调用InitializeSkin方法,初始化组件。

        private void InitializeSkin()
        {
            SkinEngine skinObj = new SkinEngine();      //初始化组件对象
            skinObj.SkinFile = "skin/DiamondRed.ssk";   //加载皮肤文件
            skinObj.SkinAllForm = true;                 //所有窗体都应用该皮肤
            skinObj.Active = true;
        }

InitializeSkin方法

  这样,美化工作就很简单地完成。这里提供IrisSkin4.dll的下载地址给需要的朋友:点击下载

3.4效果演示

四.反思总结

  先说说这次写的东西,功能虽然已经实现,不过存在很多可扩展性,这里我们只是抓取关键字为“美女”的AJAX请求地址,我们可以分析其他关键字的请求找到一些规律,或许可以结合正则表达式实现更多类型图片的批量下载。这次最大的收获是了解了HttpWebRequest类的使用,以后用来从网上抓取数据或许很好用。

  看过一遍杨老师的教程,但是在自己敲代码的过程中还是停停顿顿,顺着思路写却总有些细节顾及不到。看来只顾着往前学是不可行的,以后要抽出时间多回顾回顾以前学的基础。

时间: 2024-12-28 22:03:49

自己动手写美女图片下载器的相关文章

Python实战:美女图片下载器,海量图片任你下载

Python应用现在如火如荼,应用范围很广.因其效率高开发迅速的优势,快速进入编程语言排行榜前几名.本系列文章致力于可以全面系统的介绍Python语言开发知识和相关知识总结.希望大家能够快速入门并学习Python这门语言. 本文是在前一部分Python基础之上Python实战:Python爬虫学习教程,获取电影排行榜,再次升级的Python网页爬虫实战课程. 1.项目概述. 利用XPath和requests模块进行网页抓取与分析,达到网页图片下载的效果. 抓爬图片地址:http://www.2c

美女图片采集器 (源码+解析)

前言: 有一段时间没写博客了, "持之以恒"徽章都暗了, 实在不该. 前一段确实比较忙, ...小小地给自己的懒找个借口吧. 大二即将结束, 学习iOS也有一段时间了.今天抽点时间, 开源一个前几天刚上传的App里面的一个功能, RT, 美女图片采集器.   美女.. 相信没有人不喜欢吧, 基于此, 这个小Demo应运而生. 效果演示: 看到这里, 如果还有兴趣学习的话, 可以先到我的git中下载源码, 然后配合着源码看我下面的解析.相信, 会让你有所收获的. git下载链接: Bea

自己动手写android图片异步加载库(二)

在<自己动手写android图片异步加载库>系列的第一篇文章中,主要是学习了使用ReferenceQueue来实现一个内存缓存.在这篇文章中主要是介绍在下载很多图片是怎么控制线程和队列.在这版代码里,加入信号量和队列,可以控制下载任务的顺序.可以控制暂停和结束. 代码A:ImageLoader.java /** * 图片加载工具类 * * @author qingtian * @blog http://blog.csdn.net/bingoSpunky */ @SuppressLint(&qu

Bing图片下载器(Python实现)

分享一个Python实现的Bing图片下载器.下载首页图片并保存到到当前目录.其中用到了正则库re以及Request库. 大致流程如下: 1.Request抓取首页数据 2.re正则匹配首页图片URL 3.再次使用Request下载图片数据 源码: # --*-- encoding: UTF-8 --*-- """bingloader.py下载Bing.com首页图片""" import reimport sysimport osimport r

自己动手写编译器、链接器作者自序

<自己动手写编译器.链接器> 纸上得来终觉浅,绝知此事要躬行. --陆游 编译原理与技术的一整套理论在整个计算机科学领域占有相当重要的地位,学习它对程序设计人员有很大的帮助.我们考究历史会发现那些人人称颂的程序设计大师都是编译领域的高手,像写出BASIC语言的比尔·盖茨,Sun公司的Java之父等,在编译领域都有很深的造诣.曾经在世界首富宝座上稳坐多年的比尔·盖茨也是从给微机编写BASIC语言编译器起家的,也正是这个BASIC编译器为比尔·盖茨和保罗·艾伦的微软帝国奠定了基础.这个编写BASI

自己动手写编译器、链接器章节划分

<自己动手写编译器.链接器>预计将于12月由清华大学出版社出版,敬请期待,前一篇博客所提内容绝无虚言.这里向大家提前公开一下本书的章节划分,另外公布一下作者邮箱:[email protected]. 章节划分: 第 1 章 引言 第 2 章 文法知识第 3 章 SC 语言定义 第 4 章 SC 语言词法分析第 5 章 SC 语言语法分析 第 6 章 符号表第 7 章 生成 COFF 目标文件第 8 章 X86 机器语言第 9 章 SCC 语义分析第 10 章 链接器第 11 章 SC 语言程序

PyQt写的图片播放器简约版.

用PyQt写的图片播放器. 用线程控制. 线程之间的信号传递. 1 # -*- coding:utf-8 -*- 2 3 from PyQt4 import QtGui, QtCore 4 import sys, os, time 5 6 # from datetime import date, time, datetime, timedelta 7 QtCore.QTextCodec.setCodecForTr(QtCore.QTextCodec.codecForName("utf8"

自己动手写编译器、链接器一书作者自序

<自己动手写编译器.链接器>  购买网址 纸上得来终觉浅,绝知此事要躬行. --陆游 编译原理与技术的一整套理论在整个计算机科学领域占有相当重要的地位,学习它对程序设计人员有很大的帮助.我们考究历史会发现那些人人称颂的程序设计大师都是编译领域的高手,像写出BASIC语言的比尔·盖茨,Sun公司的Java之父等,在编译领域都有很深的造诣.曾经在世界首富宝座上稳坐多年的比尔·盖茨也是从给微机编写BASIC语言编译器起家的,也正是这个BASIC编译器为比尔·盖茨和保罗·艾伦的微软帝国奠定了基础.这个

自己动手写编译器、链接器致谢

<自己动手写编译器.链接器> 本书投稿后,有幸请CSDN暨<程序员>杂志总编.刘江老师阅读了本书的初稿,并为本书作序,在此向刘老师表示最衷心的感谢.本书临近出版之际,承蒙清华大学王生原老师阅读了本书终稿,并对书稿做了中肯评价: “本书特色鲜明,内容有深度,文笔也很不错,很值得出版.本书最大的特色是所选的目标平台,即x86处理器以及微软系统的COFF目标文件格式,这在教材中很少见到,可为国内的编译教学实践提供别具一格的素材.”同时,王老师还对本书提出了宝贵建议.在这里,向王老师表示由