一次爬虫实践学习(C#)

我们经常浏览网页,有时候看到一些精美的图片,想下载下来保存,但是太多,如果一张一张的下载,那太费时了;如果你喜欢看书,看小说,那么浏览小说网站是常有的事,但是有时候我们不能联网,比如农村老家,如果还想看,我们有没有想过一次性保存到手机里。网站上的小说都是一章一个页面,难道要我们一次一个章节复制粘贴保存?这个事我真的干了,为了一本小说,我连续花了三个小时,目不转睛地盯着电脑复制粘贴。还有抓取别人的网站一些有用数据,为我们服务,比如说找工作的事,买彩票的事等等。

现在我要来实现一个下载他趣网站上性感漂亮女性图片,发现这个网站也是通过别人博客得知的。那些图片很不错,养眼,所以对这个网站感兴趣了,也就有动力了。

今天写这个博客就是为了记录一次学习爬虫过程,分享自己遇到一些问题,以及解决问题的方法经验。

常规思路是,首先打开我们要爬取的网站,然后点击审查网页元素,分析HTML结构如下图

然后就开始写代码,初看这肯定要从HTML元素中获取图片链接了。于是我还下载了Jumony 一个提取网页元素的帮助类。

 url = "http://www.taqu.cn/community/";
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                // 请求成功的状态码:200
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            string html = reader.ReadToEnd();

                            var parser = new JumonyParser();
                            var doc = parser.Parse(html);
                            var lists = doc.Find("#subject_list > ul > li");
                            foreach (var li in lists)
                            {
                                var img = li.FindFirst("img");
                                var src =img.Attribute("src").Value();
                                DownloadImage(src);
                            }
                            //Console.WriteLine(html);
                        }
                    }
                }
                else
                {
                    Console.WriteLine("服务器返回错误:{0}", response.StatusCode);
                }
            }
        

没有想到string html = reader.ReadToEnd(); 获取的是一张算作空白页,根本就没有我审查元素看到的内容,没有我想要的元素,那要怎么办?难道错了?不行呀,于是我又通过这个观察如下图,有图片的请求,也有内容。——原来是ajax请求呀!

再仔细看,不是有请求么,而且有返回数据

点击他趣girl

返回数据

在Headers这一栏里我们可以发现请求头部一些信息,可以请求之后响应一些信息。主要看请求的。

分析得知,在代码里我们要发送请求需要填写哪些内容并注意事项了。代码如下,参考别人的,后面会说明。

  public static string GetContent(string method, string url, string postData = "", CookieContainer cookie = null)
        {
            HttpWebResponse response = null;
            HttpWebRequest request = null;
            if (cookie == null)
                cookie = new CookieContainer();
            // 准备请求...
            try
            {
                // 设置参数
                request = WebRequest.Create(url) as HttpWebRequest;
                request.CookieContainer = cookie;
                request.AllowAutoRedirect = true;
                request.Method = method.ToUpper();
                request.ContentType = "application/json, text/javascript, */*; q=0.01";
                string userAgent = string.Format("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36)");//这个根据电脑不同而改变
                request.UserAgent = userAgent;

                if (method.ToUpper() == "POST")
                {
                    request.ContentLength = postData.Length;
                    if (!string.IsNullOrEmpty(postData))
                    {
                        byte[] data = Encoding.Default.GetBytes(postData);
                        request.ContentLength = data.Length;
                        using (Stream outstream = request.GetRequestStream())
                        {
                            outstream.Write(data, 0, data.Length);
                        }
                    }
                }
                //发送请求并获取相应回应数据
                response = request.GetResponse() as HttpWebResponse;
                //直到request.GetResponse()程序才开始向目标网页发送Post请求
                using (Stream instream = response.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(instream, Encoding.Default))
                    {

                        //返回结果网页(html)代码
                        string content = sr.ReadToEnd();
                        return UnicodeToGB(content);
                    }
                }
            }
            catch (Exception ex)
            {
                string err = ex.Message;
                return err;
            }
            finally
            {
                if (response != null)
                    response.Close();
            }
        }

参考:http://www.cnblogs.com/shoufengwei/p/5654491.html

请求方法是有了,但是返回的数据好像不是汉字哦,是Unicode编码过的数据,于是有了转码这一步

 public static string UnicodeToGB(string text)
        {
            System.Text.RegularExpressions.MatchCollection mc = System.Text.RegularExpressions.Regex.Matches(text, "\\\\u([\\w]{4})");
            if (mc != null && mc.Count > 0)
            {
                foreach (System.Text.RegularExpressions.Match m2 in mc)
                {
                    string v = m2.Value;
                    string word = v.Substring(2);
                    byte[] codes = new byte[2];
                    int code = Convert.ToInt32(word.Substring(0, 2), 16);
                    int code2 = Convert.ToInt32(word.Substring(2), 16);
                    codes[0] = (byte)code2;
                    codes[1] = (byte)code;
                    text = text.Replace(v, Encoding.Unicode.GetString(codes));
                }
            }
            else
            {

            }
            return text;
        }

参考:http://www.cnblogs.com/guardianf/archive/2012/08/21/2649147.html

但是,但是数据是json,我要怎么获取其中的图片请求链接呢?我首先想到的事json转datatable 然后循环依次获得。想法没有错,但是怎么写,我又不会。只有网上找了,一搜真的不少,满怀喜悦之情,复制粘贴,运行,试了不少,不是这里报错,就是转化不成功,一下子失望透顶了。怎么办,怎么办。想到网上一些现成方法,应该只适合作者的项目,没有通用性,一下子就轻松多了。再找找看,说不定有的。

这里举一个报错的转化方法

/// <summary>
        /// json转换为DataTable
        /// </summary>
        /// <param name="json">需要转化的json格式字符串</param>
        /// <returns></returns>
        public DataTable updateInfo(string json)
        {
            System.Web.Script.Serialization.JavaScriptSerializer jss =new System.Web.Script.Serialization.JavaScriptSerializer();
            object[] obj = (object[])jss.DeserializeObject(json);
            Dictionary<string, object> dic;
            DataRow dr;
            DataTable dt = getDataTable();
            foreach (object _obj in obj)
            {
                dr = dt.NewRow();
                dt.Rows.Add(dr);
                dic = (Dictionary<string, object>)_obj;
                dr["id"] = dic["id"];
                dr["img_url"] = dic["img_url"];
                dr["title"] = dic["title"];

            }
            return dt;
        }

经历大半天寻找,实践运行总算有一个转化成功的了。

  #region 将json转换为DataTable
        /// <summary>
        /// 将json转换为DataTable 参考 http://www.jb51.net/article/61990.htm
        /// </summary>
        /// <param name="strJson">得到的json</param>
        /// <returns></returns>
        private DataTable JsonToDataTable(string strJson)
        {
            //转换json格式
            strJson = strJson.Replace(",\"", "*\"").Replace("\":", "\"#").ToString();
            //取出表名
            var rg = new Regex(@"(?<={)[^:]+(?=:\[)", RegexOptions.IgnoreCase);
            string strName = rg.Match(strJson).Value;
            DataTable tb = null;
            //去除表名
            strJson = strJson.Substring(strJson.IndexOf("[") + 1);
            strJson = strJson.Substring(0, strJson.IndexOf("]"));
            //获取数据
            rg = new Regex(@"(?<={)[^}]+(?=})");
            MatchCollection mc = rg.Matches(strJson);
            for (int i = 0; i < mc.Count; i++)
            {
                string strRow = mc[i].Value;
                string[] strRows = strRow.Split(‘*‘);
                //创建表
                if (tb == null)
                {
                    tb = new DataTable();
                    tb.TableName = strName;
                    foreach (string str in strRows)
                    {
                        var dc = new DataColumn();
                        string[] strCell = str.Split(‘#‘);
                        if (strCell[0].Substring(0, 1) == "\"")
                        {
                            int a = strCell[0].Length;
                            dc.ColumnName = strCell[0].Substring(1, a - 2);
                        }
                        else
                        {
                            dc.ColumnName = strCell[0];
                        }
                        tb.Columns.Add(dc);
                    }
                    tb.AcceptChanges();
                }
                //增加内容
                DataRow dr = tb.NewRow();
                for (int r = 0; r < strRows.Length; r++)
                {
                    dr[r] = strRows[r].Split(‘#‘)[1].Trim().Replace(",", ",").Replace(":", ":").Replace("\"", "");
                }
                tb.Rows.Add(dr);
                tb.AcceptChanges();
            }
            return tb;
        }
        #endregion

现在连接也有了,最后一般是下载了,我们来分析单个链接。

没有了cookie,我们只要一个一个链接请求下载就可以了。于是我有了两个方法,肯定不止这两个了。

方法一,模仿上面那个有cookie的

 public void  GetContent2(string url, string destFileName, string method)
        {
            HttpWebResponse response = null;
            HttpWebRequest request = null;
            try
            {

                // 设置参数
                //request.Host = "183.60.137.147:80";
              //  request.Address[""] = new Uri("");

                request = WebRequest.Create(url) as HttpWebRequest;
                request.AllowAutoRedirect = true;
                request.Host = "forumimg01.touchcdn.com";
                request.Method = method.ToUpper();
                request.Headers["Accept-Encoding"] = "gzip, deflate, sdch";
                request.ContentType = "image/webp,image/*,*/*;q=0.8";
                string userAgent = string.Format("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36)");//这个根据电脑不同而改变
                request.UserAgent = userAgent;

                //发送请求并获取相应回应数据
                response = request.GetResponse() as HttpWebResponse;
                //直到request.GetResponse()程序才开始向目标网页发送Post请求
                using (Stream instream = response.GetResponseStream())
                {

                        //返回结果网页(html)代码
                        using (FileStream fileStream = new FileStream(destFileName, FileMode.Create))
                        {
                            instream.CopyTo(fileStream);//stream 写入到fileStream中
                        }

                }
            }
            catch (Exception ex)
            {
                string err = ex.Message;
            }
            finally
            {
                if (response != null)
                    response.Close();
            }
        }

方法二,请求整个网页的

 private void DownloadImage2(string objUrl, string destFileName)
        {
           // string destFileName = Path.Combine(destDir, Path.GetFileName(objUrl));
            HttpWebRequest request =
                (HttpWebRequest)HttpWebRequest.Create(objUrl);
            // 欺骗服务器判断URLReferer
            // request.Referer = "http://image.baidu.com";
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        using (FileStream fileStream = new FileStream(destFileName, FileMode.Create))
                        {
                            stream.CopyTo(fileStream);//stream 写入到fileStream中
                        }
                    }
                }
                else
                {
                    throw new Exception("下载" + objUrl + "失败,错误码:" + response.StatusCode);
                }
            }
        }

下载保存路径方法

   private void DownloadImage(string id, string objUrl)
        {
            destDir = txtSavePath.Text.Trim();
            if (string.IsNullOrEmpty(destDir))
            {
                MessageBox.Show("请选择要保存的文件夹!");
                return;
            }
            if (!destDir.EndsWith("\\"))
            {
                destDir += "\\";
            }

            string destFileName = Path.Combine(destDir, Path.GetFileName(objUrl));

          //  string urlimg = objUrl + "?imageView/2/w/380/interface/1";这个下载下来是小图,所以不用它才是大图
            DownloadImage2(objUrl, destFileName);
           // GetContent2(objUrl, destFileName, "GET");

        }

点击开始执行

   private void btnStart_Click(object sender, EventArgs e)
        {
          //1到20页
            btnStart.Enabled = false;
            for(int i=0 ;i<20;i++){
                getbasedata(i + 1);
            }
        }

调用请求方法

  public void getbasedata(int num)//num是页码
        {
            string url = string.Format("http://www.taqu.cn/ajax/Forum/getFindListByTab?tab_id=119&limit=10&score=1468547984&page={0}", num);
            string method = "GET";

            DataTable dt = JsonToDataTable(GetContent(method, url));
            if (dt.Rows.Count > 0)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    //string urlimg = dr["img_url"].ToString()+ "?imageView/2/w/380/interface/1";
                    DownloadImage(dr["id"].ToString(), dr["img_url"].ToString().Replace(@"\", ""));//这里@符号阻止转义
                    //因为json转化为datatable连接变成这样的了——http:\/\/forumimg01.touchcdn.com\/index\/3e637fe0142008158574c24a278d7804.jpg
                    //正确的是——http://forumimg01.touchcdn.com/index/3e637fe0142008158574c24a278d7804.jpg 所以需要替换
                }
            }
        }

忘了 这个全局的变量 private string destDir; // 目标文件夹

运行的效果

主要和关键代码都放上来了。

窗体代码去这里下载 博客最底端  http://www.cnblogs.com/edisonchou/p/4175190.html

能想到的,都写上了,其实还可以更完善,比如加上IP代理,,一些数据保存到数据库,不过这个工程太大了。这也不是我要写这篇文章的目的,我只要这些图片,这些乐趣。

原来认真做一件事,真的可以获得意想不到的的乐趣,也打开了自己兴趣之门。

原来我也可以做到,不要临渊羡鱼,自己动手一干!

参考:

http://www.cnblogs.com/edisonchou/p/4175190.html

http://www.cnblogs.com/haigege/p/5492177.html

时间: 2024-10-21 12:21:27

一次爬虫实践学习(C#)的相关文章

开始我的Python爬虫学习之路

因为工作需要经常收集一些数据,我就想通过学爬虫来实现自动化完成比较重复的任务. 目前我Python的状况,跟着敲了几个教程,也算是懂点基础,具体比较深入的知识,是打算从做项目中慢慢去了解学习. 我是觉得如果一开始就钻细节的话,是很容易受到打击而放弃的,做点小项目让自己获得点成就感路才更容易更有信心走下去. 反正遇到不懂的就多查多问就对了. 知乎上看了很多关于入门Python爬虫的问答,给自己总结出了大概的学习方向. 基础: HTML&CSS,JOSN,HTTP协议(这些要了解,不太需要精通) R

爬虫难点分析

难点分析 1.网站采取反爬策略 2.网站模板定期变动 3.网站url抓取失败 4.网站频繁抓取ip被封 1.网站采取反爬策略 >网站默认对方正常访问的方式是浏览器访问而不是代码访问,为了防止对方使用大规模服务器进行爬虫从而导致自身服务器承受过大的压力,通常网站会采取反爬策略 根据这一特性,我们用代码模拟实现浏览器访问 2.网站模板定期变动-解决方案 >标签变动,比如<div>变动,那么我们不能把代码给写死了 (1)不同配置文件配置不同网站的模板规则 (2)数据库存储不同网站的模板规

爬虫——模拟点击动态页面

动态页面的模拟点击: 以斗鱼直播为例:http://www.douyu.com/directory/all 爬取每页的房间名.直播类型.主播名称.在线人数等数据,然后模拟点击下一页,继续爬取 #!/usr/bin/python3 # -*- conding:utf-8 -*- __author__ = 'mayi' """ 动态页面的模拟点击: 模拟点击斗鱼直播:http://www.douyu.com/directory/all 爬取每页房间名.直播类型.主播名称.在线人数

第三百二十三节,web爬虫,scrapy模块以及相关依赖模块安装

第三百二十三节,web爬虫,scrapy模块以及相关依赖模块安装 当前环境python3.5 ,windows10系统 Linux系统安装 在线安装,会自动安装scrapy模块以及相关依赖模块 pip install Scrapy 手动源码安装,比较麻烦要自己手动安装scrapy模块以及依赖模块 安装以下模块 1.lxml-3.8.0.tar.gz (XML处理库) 2.Twisted-17.5.0.tar.bz2 (用Python编写的异步网络框架) 3.Scrapy-1.4.0.tar.gz

Python有了asyncio和aiohttp在爬虫这类型IO任务中多线程/多进程还有存在的必要吗?

最近正在学习Python中的异步编程,看了一些博客后做了一些小测验:对比asyncio+aiohttp的爬虫和asyncio+aiohttp+concurrent.futures(线程池/进程池)在效率中的差异,注释:在爬虫中我几乎没有使用任何计算性任务,为了探测异步的性能,全部都只是做了网络IO请求,就是说aiohttp把网页get完就程序就done了. 结果发现前者的效率比后者还要高.我询问了另外一位博主,(提供代码的博主没回我信息),他说使用concurrent.futures的话因为我全

Python爬虫从入门到放弃(十一)之 Scrapy框架整体的一个了解

这里是通过爬取伯乐在线的全部文章为例子,让自己先对scrapy进行一个整理的理解 该例子中的详细代码会放到我的github地址:https://github.com/pythonsite/spider/tree/master/jobboleSpider 注:这个文章并不会对详细的用法进行讲解,是为了让对scrapy各个功能有个了解,建立整体的印象. 在学习Scrapy框架之前,我们先通过一个实际的爬虫例子来理解,后面我们会对每个功能进行详细的理解.这里的例子是爬取http://blog.jobb

简谈-网络爬虫的几种常见类型

众所周知,网络爬虫(或称为网络爬虫.网络蜘蛛.机器人)是搜索引擎最上游的一个模块,是负责搜索引擎内容索引的第一关. 很多人为了提高自己网站的索引量,都是去网上随便找一些爬虫工具来使用.但是很多人不知道,这些抓取网站的小爬虫是有各种各样的不同性格的. 常见的优秀网络爬虫有以下几种类型: 1.批量型网络爬虫:限制抓取的属性,包括抓取范围.特定目标.限制抓取时间.限制数据量以及限制抓取页面,总之明显的特征就是受限: 2.增量型网络爬虫(通用爬虫):与前者相反,没有固定的限制,无休无止直到抓完所有数据.

python爬虫 模拟登陆校园网-初级

最近跟同学学习爬虫的时候看到网上有个帖子,好像是山大校园网不稳定,用py做了个模拟登陆很有趣,于是我走上了一条不归路..... 先上一张校园网截图 首先弄清一下模拟登陆的原理: 1:服务器判定浏览器登录使用浏览器标识,需要模拟登陆 2: 需要post账号,密码,以及学校id python走起,我用的2.7版本,用notepad++写的,绑定python可以直接运行 由于是模拟网页登陆,需要导入urllib urllib2 cookielib库,前两个有与网页直接的接口,cookielib就是用来

爬虫的本质

w机器化的人,超越人. [初码干货]关于.NET玩爬虫这些事 - 初码 - 博客园 http://www.cnblogs.com/printhelloworld/p/6354085.htm "爬虫的本质是对目标WebServer页面行为和业务流程的精准分析,是对HTTP的深刻理解,是对正则.多线程等周边技术以及软件工程的灵活运用"