博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
网络爬虫(专门抓取图片)
阅读量:6689 次
发布时间:2019-06-25

本文共 11153 字,大约阅读时间需要 37 分钟。

xmfdsh我真是兴趣多多,怎么老是静不下心来搞定一方面的技术,再学点其他的东西,循序渐进,好吧,我又研究网络爬虫去了,这是一个简单版的,参考了网上很多资料,C#来编写,专门抓取图片,能够抓取一些需要cookie的网站,所以功能上还是挺完善的,xmfdsh只研究了三天,因此还有大把需要改进的地方,日后再 慢慢改进,在本文后面附上整个项目,在此献给喜欢研究C#的朋友们,让我慢慢地道来:

#region 访问数据 + Request(int index)        ///         /// 访问数据        ///         private void Request(int index)        {            try            {                int depth;                string url = "";                //lock锁住Dictionary,因为Dictionary多线程会出错                lock (_locker)                {                    //查看是否还存在未下载的链接                    if (UnDownLoad.Count <= 0)                    {                        _workingSignals.FinishWorking(index);                        return;                    }                    //否则的话,把该虫子标记为在工作                    _reqsBusy[index] = true;                    depth = UnDownLoad.First().Value;                    url = UnDownLoad.First().Key;                    IsDownLoad.Add(url, depth);//将URL 加入到已经下载集合里                    UnDownLoad.Remove(url);                }                //网络协议的东西,不懂网上搜一下,(HttpWebRequest)的使用                //这个需要一点日子理解,xmfdsh不是一下子就弄懂                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);                request.Method = "GET";                request.Accept = "text/html";                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)";                request.CookieContainer = cookies;//cookie 尝试                RequestState rs = new RequestState(request, url, depth, index);                //回调函数,如果接受到数据的处理方法                var result = request.BeginGetResponse(new AsyncCallback(Received), rs);                //也是回调函数,超时的回调函数                ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeOut, rs, MAXTIME, true);            }            catch (Exception ex)            {                _reqsBusy[index] = false;                DispatchWork();            }        }         #endregion

首先上来的是请求某个连接的方法,里面有两个回调函数,说明这里的编程时异步的,这个异步编程个人觉得需要花点时间去了解和学习,这个不是一朝一夕的,个人觉得C#比较难的地方就在一些接口,工具类,还有异步事件等等。

#region 超时的方法 + TimeOut(object state, bool timedOut)        ///         /// 超时的方法        ///         ///         ///         private void TimeOut(object state, bool timedOut)        {            //判断是否超时            if (timedOut)            {                RequestState rs = state as RequestState;                if (rs != null)                {                    //撤销internet请求                    rs.Request.Abort();                }                DispatchWork();            }        } #endregion

这个是超时的方法,当超时的时候,默认撤销internet的请求,并回滚,所以这个链接下的东西就没有了,当然超时后,继续请求下一个链接的资源

#region 获取数据 异步 + Received(IAsyncResult ar)        ///         /// 获取数据 异步        ///         ///         private void Received(IAsyncResult ar)        {            try            {                //得到请求进来的参数                RequestState rs = (RequestState)ar.AsyncState;                HttpWebRequest request = rs.Request;                string url = rs.Url;                //获取响应                 HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(ar);                if (response != null && response.StatusCode == HttpStatusCode.OK)//成功获取响应                {                    //得到资源流                    Stream responseStream = response.GetResponseStream();                    rs.ResponseStream = responseStream;                    //处理读取数据的异步方法 ReceivedData                    var result = responseStream.BeginRead(rs.Data, 0, rs.BufferSize, new AsyncCallback(ReceivedData), rs);                }                //响应失败                else                {                    response.Close();                    rs.Request.Abort();                    _reqsBusy[rs.Index] = false;                    DispatchWork();                }            }            catch (Exception ex)            {                RequestState rs = (RequestState)ar.AsyncState;                _reqsBusy[rs.Index] = false;                DispatchWork();                            }        }         #endregion

 

这个是收到了网站响应的时候做的事情,当然用到了异步来请求的话,数据也只能异步的读取,因此有了ReceivedData方法来接受数据,并处理,如果出错或者获取相应失败,相应的,回滚,并把该爬虫虫子的工作状态重新置为准备状态,为下一个链接做好准备。

#region 异步操作读写 + ReceivedData(IAsyncResult ar)        ///         /// 异步操作读写        ///         /// 异步操作状态        private void ReceivedData(IAsyncResult ar)        {            //获取异步状态参数            RequestState rs = (RequestState)ar.AsyncState;            HttpWebRequest request = rs.Request;            System.Net.HttpWebResponse responseImg = request.GetResponse() as System.Net.HttpWebResponse;            Stream responseStream = rs.ResponseStream;                        string url = rs.Url;            int depth = rs.Depth;            string html = "";            int index = rs.Index;            int read = 1;            try            {                //如果改链接为图片来的,需要保存此图片                if (url.Contains(".jpg") || url.Contains(".png"))                {                    read = responseStream.EndRead(ar);                    if (read > 0)                    {                        MemoryStream ms = new System.IO.MemoryStream(rs.Data, 0, read);                        BinaryReader reader = new BinaryReader(ms);                        byte[] buffer = new byte[32 * 1024];                        while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)                        {                            rs.memoryStream.Write(buffer, 0, read);                        }                        //递归 再次请求数据                        var result = responseStream.BeginRead(rs.Data, 0, rs.BufferSize, new AsyncCallback(ReceivedData), rs);                        return;                    }                }                else                {                    read = responseStream.EndRead(ar);                    if (read > 0)                    {                        //创建内存流                        MemoryStream ms = new MemoryStream(rs.Data, 0, read);                        StreamReader reader = new StreamReader(ms, Encoding.GetEncoding("gb2312"));                        string str = reader.ReadToEnd();                        //添加到末尾                        rs.Html.Append(str);                        //递归 再次请求数据                        var result = responseStream.BeginRead(rs.Data, 0, rs.BufferSize, new AsyncCallback(ReceivedData), rs);                        return;                    }                }                if (url.Contains(".jpg") || url.Contains(".png"))                {                    //images = rs.Images;                    SaveContents(rs.memoryStream.GetBuffer(), url);                }                else                {                    html = rs.Html.ToString();                    //保存                    SaveContents(html, url);                    //获取页面的链接                }            }            catch (Exception ex)            {                                _reqsBusy[rs.Index] = false;                DispatchWork();            }            List
links = GetLinks(html,url); //得到过滤后的链接,并保存到未下载集合 AddUrls(links, depth + 1); _reqsBusy[index] = false; DispatchWork(); } #endregion

这个便是对数据的处理,这里就是重点的,其实也不难,判断是否为图片,如果为图片,保存此图片,因为目前网络爬虫做的还不够高级的时候爬图片是比较实际也比较好玩的(还不赶紧找找哪些网站有好多妹子图片),如果不是图片,我们认为它为普通html页面,便读取其中html代码,如果有发现有链接http或者href便加入到未下载链接中。当然读到的链接我们对一些js或者一些css等做了限制(不去读取这类东西)。

private void SaveContents(byte[] images, string url)        {                        if (images.Count() < 1024*30)                return;            if (url.Contains(".jpg"))            {                File.WriteAllBytes(@"d:\网络爬虫图片\" + _index++ + ".jpg", images);                Console.WriteLine("图片保存成功" + url);            }            else            {                File.WriteAllBytes(@"d:\网络爬虫图片\" + _index++ + ".png", images);                Console.WriteLine("图片保存成功" + url);            }        }
#region 提取页面链接 + List
GetLinks(string html) ///
/// 提取页面链接 /// ///
///
private List
GetLinks(string html,string url) { //匹配http链接 const string pattern2 = @"http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"; Regex r2 = new Regex(pattern2, RegexOptions.IgnoreCase); //获得匹配结果 MatchCollection m2 = r2.Matches(html); List
links = new List
(); for (int i = 0; i < m2.Count; i++) { //这个原因是w3school的网址,但里面的东西明显不是我们想要的 if (m2[i].ToString().Contains("www.w3.org")) continue; links.Add(m2[i].ToString()); } //匹配href里面的链接,并加到主网址上(学网站的你懂的) const string pattern = @"href=([""'])?(?
[^'""]+)\1[^>]*"; Regex r = new Regex(pattern, RegexOptions.IgnoreCase); //获得匹配结果 MatchCollection m = r.Matches(html); // List
links = new List
(); for (int i = 0; i < m.Count; i++) { string href1 = m[i].ToString().Replace("href=", ""); href1 = href1.Replace("\"", ""); //找到符合的,添加到主网址(一开始输入的网址)里面去 string href = RootUrl + href1; if (m[i].ToString().Contains("www.w3.org")) continue; links.Add(href); } return links; } #endregion

提取页面链接的方法,当读到发现这个为html代码的时候,继续解读里面的代码,找到里面的网址链接,也正是这样才有网络爬虫的功能(不然只能提取本页面就没意思了),其中http链接要提取就理所当然,href里面的话是因为。。。。(学网站你们懂的,不好解释)里面放的几乎都是图片,文章,因此才有了上面要处理href这类代码。

#region 添加url到 UnDownLoad 集合 + AddUrls(List
urls, int depth) ///
/// 添加url到 UnDownLoad 集合 /// ///
///
private void AddUrls(List
urls, int depth) { lock (_locker) { if (depth >= MAXDEPTH) { //深度过大 return; } foreach (string url in urls) { string cleanUrl = url.Trim(); int end = cleanUrl.IndexOf(' '); if (end > 0) { cleanUrl = cleanUrl.Substring(0, end); } if (UrlAvailable(cleanUrl)) { UnDownLoad.Add(cleanUrl, depth); } } } } #endregion
#region 开始捕获 + DispatchWork()        ///         /// 开始捕获        ///         private void DispatchWork()        {            for (int i = 0; i < _reqCount; i++)            {                if (!_reqsBusy[i])                {                    Request(i);                    Thread.Sleep(1000);                 }            }        }         #endregion

这个函数就是让那些虫子工作的,其中_reqCount的值是一开始弄上去的,其实形象理解就是你放出虫子的个数,这个程序里面我默认放的20,随时可以修改。说到某些网站需要cookie的话是通过一开始先访问输入的网址,当然也是用HttpWebRequest帮助类,cookies = request.CookieContainer; //保存cookies,在后面访问后续网址的时候就加上去就行了request.CookieContainer = cookies;//cookie 尝试。应用了cookie才能访问的网站的话,根网页是不需要的,也就好比百度图片的网址是不需要的,但如果很唐突的访问里面的图片的话就要附上cookie了,所以这个问题也解决,xmfdsh发现这个程序还是有一些网站不能去抓图,抓到一定数量就停了,具体原因不知道,后面再慢慢改进

附上源码:

 

转载于:https://www.cnblogs.com/xmfdsh/p/3697047.html

你可能感兴趣的文章
redis info信息详解A
查看>>
看到想笑!
查看>>
实现按钮点击事件
查看>>
第一次作业
查看>>
PHP算法:等宽等像素值截取字符串
查看>>
Lync Server与Outlook Web App集成
查看>>
visudo精确用户赋权与sudo日志跟踪
查看>>
LVM系列4
查看>>
MYSQL 基础记录(1)
查看>>
windows查看进程路径
查看>>
Data persistence overview
查看>>
The final mile: Upgrade to Grails 2.4.3 and use Sp
查看>>
springBoot(7):web开发-错误处理
查看>>
linux中top命令详解
查看>>
MODIS批量处理软件MRT的安装说明
查看>>
MySQL数据库索引
查看>>
keyCode 大全
查看>>
一个经典编程面试题的“隐退”
查看>>
【java基础知识】使用javap对代码进行反汇编
查看>>
iOS中AFNetworking的简单使用
查看>>