.NET实现网络爬虫_hyunbar的博客-程序员资料

技术标签: Spiders  其他  NET  

爬虫的特征和运行方式

User-Agent:主要用来将我们的爬虫伪装成浏览器。

Cookie:主要用来保存爬虫的登录状态。

连接数:主要用来限制单台机器与服务端的连接数量。

代理IP:主要用来伪装请求地址,提高单机并发数量。

爬虫工作的方式可以归纳为两种:深度优先、广度优先。

深度优先就是一个连接一个连接的向内爬,处理完成后再换一下一个连接,这种方式对于我们来说缺点很明显。
广度优先就是一层一层的处理,非常适合利用多线程并发技术来高效处理,因此我们也用广度优先的抓取方式。

首先我们用Visual Studio 2015创建一个控制台程序,定义一个简单的SimpleCrawler类,里面只包含几个简单的事件:

 public class SimpleCrawler
    {
        public SimpleCrawler() { }
        /// <summary>
        /// 爬虫启动事件
        /// </summary>
        public event EventHandler<OnStartEventArgs> OnStart;
        /// <summary>
        /// 爬虫完成事件
        /// </summary>
        public event EventHandler<OnCompletedEventArgs> OnCompleted;

        /// <summary>
        /// 爬虫出错事件
        /// </summary>
        public event EventHandler<Exception> OnError;
        /// <summary>
        /// 定义cookie容器
        /// </summary>
        public CookieContainer CookieContainer { get; set; }

    }

接着我们创建一个OnStart的事件对象:

这里写图片描述

然后我们创建一个OnCompleted事件对象:

这里写图片描述

最后我们再给它增加一个异步方法,通过User-Agent将爬虫伪装成了Chrome浏览器

 /// <summary>
        /// 异步创建爬虫
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="proxy"></param>
        /// <returns></returns>
        public async Task<string> Start(Uri uri, WebProxy proxy = null)
        {
            return await Task.Run(() =>
            {
                var pageSource = string.Empty;
                try
                {
                    if (this.OnStart != null)
                        this.OnStart(this, new OnStartEventArgs(uri));
                    Stopwatch watch = new Stopwatch();
                    watch.Start();
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
                    request.Accept = "*/*";
                    //定义文档类型及编码
                    request.ContentType = "application/x-www-form-urlencoded";
                    request.AllowAutoRedirect = false;//禁止自动跳转
                    request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36";
                    //定义请求超时事件为5s
                    request.Timeout = 5000;
                    //长连接
                    request.KeepAlive = true;
                    request.Method = "GET";
                    //设置代理服务器IP,伪装请求地址
                    if (proxy != null)
                        request.Proxy = proxy;
                    //附加Cookie容器
                    request.CookieContainer = this.CookieContainer;
                    //定义最大链接数
                    request.ServicePoint.ConnectionLimit = int.MaxValue;
                    //获取请求响应
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    //将Cookie加入容器,保持登录状态
                    foreach (Cookie cookie in response.Cookies)
                        this.CookieContainer.Add(cookie);
                    //获取响应流
                    Stream stream = response.GetResponseStream();
                    //以UTF8的方式读取流
                    StreamReader reader = new StreamReader(stream,Encoding.UTF8);
                    //获取网站资源
                    pageSource = reader.ReadToEnd();
                    watch.Stop();
                    //获取当前任务线程ID
                    var threadID = Thread.CurrentThread.ManagedThreadId;
                    //获取请求执行时间
                    var milliseconds = watch.ElapsedMilliseconds;
                    reader.Close();
                    stream.Close();
                    request.Abort();
                    response.Close();
                    if (this.OnCompleted != null)
                        this.OnCompleted(this, new OnCompletedEventArgs(uri, threadID, milliseconds, pageSource));
                }
                catch (Exception ex)
                {
                    if (this.OnError != null)
                        this.OnError(this, ex);

                }
                return pageSource;
            });
        }

在控制台里写下爬虫的抓取代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace TestPa
{
    class Program
    {
        static void Main(string[] args)
        {
            //定义入口URl
            var cityUrl = "http://hotels.ctrip.com/citylist";
            //定义泛型列表存放城市名称及对应的酒店
            var cityList = new List<City>();
            //调用自己写的爬虫程序
            var cityCrawler = new SimpleCrawler();
            cityCrawler.OnStart += (s, e) =>
              {
                  Console.WriteLine("爬虫开始抓取的地址:" + e.Uri.ToString());
              };
            cityCrawler.OnError += (s, e) =>
            {
                Console.WriteLine("爬虫抓取出现错误:" + e.Message);
            };
            cityCrawler.OnCompleted += (s, e) =>
              {
                  var links = Regex.Matches(e.PageSource, @"<a[^>]+href=""*(?<href>/hotel/[^>\s]+)""\s*[^>]*>(?<text>(?!.*img).*?)</a>", RegexOptions.IgnoreCase);
                  foreach(Match match in links)
                  {
                      var city = new City
                      {
                          CityName = match.Groups["text"].Value,
                          Uri = new Uri("http://hotels.ctrip.com" + match.Groups["href"].Value)
                      };
                      if (!cityList.Contains(city))
                          cityList.Add(city);
                      Console.WriteLine(city.CityName + "||" + city.Uri);
                  }

                  Console.WriteLine(e.PageSource);
                  Console.WriteLine("**********************************");
                  Console.WriteLine("爬虫抓取完成");
                  Console.WriteLine("耗时:" + e.Milliseconds + " 毫秒");
                  Console.WriteLine("线程:" + e.ThreadID);
                  Console.WriteLine("地址:" + e.Uri.ToString());
              };
            cityCrawler.Start(new Uri(cityUrl)).Wait();
            Console.ReadKey();
        }
    }

    public class City
    {
        public string CityName { get; set; }
        public Uri Uri { get; set; }
    }
}

运行结果:
这里写图片描述

请看大神原文地址
基于.NET更高端智能化的网络爬虫

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/hyunbar/article/details/79302065

智能推荐

mysql 5.7配置多线程复制,MySQL5.7复制功能实战,基于事务的复制,多源复制和多线程复制配置..._weixin_39737757的博客-程序员资料

这篇是幕课网-MySQL5.7复制功能实战视频教程的学习笔记。http://www.imooc.com/learn/589第1章 MySQL复制基础MySQL是异步复制采取针对特定用户的读写分离,可以实现假无延迟。MySQL复制是基于binlog日志进行的。存在三种日志格式:statement格式 存储SQL语句,存储日志量最小row格式 ...

无聊的时候……STM32做个拼图小游戏。。_weixin_30614109的博客-程序员资料

===========================华丽的main.c分界线====================================#include "stm32f10x.h"#include "LCD_driver.h"#include"chinese.h"#include"picture.h"#define LCD_cs(x) ...

adxl345的STM32驱动程序和硬件设计_weixin_34393428的博客-程序员资料

一、硬件电路接口图片 1.ADXL345硬件接口图片使用的是SPI端口进行通信,这样读取数据比较快且后续也可以转化为IIC通信接口。在网上找一些发现IIC接口的比较多,所以本人就DIY做SPI的通信。2.STM32F103T系列单片机作为MCU 资源比较丰富、本人比较熟悉开发速度较快硬件电路首先是为了实现功能,所以设计比较简单。后续小编想做无线蓝牙的数据传输,所以硬件上...

微信推广带id二维码生成详细解读_修炼到救赎的博客-程序员资料_微信id转二维码代码

欢迎加入ifast 交流群,群聊号码:746358408 ,关注的朋友请加群,博客留言信息不带回哈需求:登录用户产生一个二维码,下一个人扫描这个二维码之后关注公众号,登录之后自动成为第一个用户的粉丝吐槽一把,这个需求很操蛋。。。。但是,操不操蛋那是你程序猿说的不?微信开发本来就相当操蛋了。。。都懂得,文档又少,限制又多,傻逼的很。。。。首先看下微信官方的文档,我们先了解...

QT中通过QProcess调用adb命令完成PC端文件传输到安卓_MY9712_的博客-程序员资料

最近工作中遇到 一个需求,是需要通过QT的界面去调用adb命令完成将PC端的文件传输到安卓手机上。到网上查询资料后发现QT中的QProcess类可以完成这个需求,简单用法如下:QProcess *process = new QProcess(this);process-&gt;start("ls");ui-&gt;textEdit-&gt;append(process-&gt;readAl...

python中plot是什么意思_python中Matplotlib用法_weixin_39995439的博客-程序员资料

原文地址https://blog.csdn.net/Notzuonotdied/article/details/77876080简单演示import matplotlib.pyplot as pltimport numpy as np# 从[-1,1]中等距去50个数作为x的取值x = np.linspace(-1, 1, 50)print(x)y = 2*x + 1# 第一个是横坐标的值,第二个...

随便推点

在vue中使用echarts以绘出好看的横向柱状图[email protected]上帝的博客-程序员资料_vue 横向柱状图

echarts使用入门导入echarts库import * as echarts from "echarts";ui&lt;div id="manuChart" class="chart-div"&gt;&lt;/div&gt;vue中的代码操作dom的代码尽量放在window.onload中。该横向柱状图可以实现鼠标上下滑动操作,并且禁止了放大缩小的功能。createdManuChart() { window.onload = () =&gt; { const m

platform设备驱动全透析_loongembedded的博客-程序员资料

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://21cnbao.blog.51cto.com/109393/3376091.1 platform总线、设备与驱动在Linux 2.6的设备驱动模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,

Android 并发二三事之 Handler 机制的妙用 HandlerThread_MyLero的博客-程序员资料

Android 并发第五篇本篇会讲解如何利用 HandlerThread 实现异步操作。HandlerThread 本身其实就是一个 Thread ,但是其内部还利用 Handler 机制。 对于提交的任务(或者说是信息 Message)依次处理。 所以在介绍 HandlerThread 原理以及如果使用之前,会首先说一个 Handler 异步机制。当然 Handler, Looper, Mes

JEB 3.7.0 Merry Xmas Edition by DimitarSerg_MarkDDi的博客-程序员资料

1. Maximum license type (copying, scripts, etc. work now).2. Fixed integrity check #1.3. Fixed integrity check #2.4. All the telemetry has been cut out.5. Removed the update checks and other shit ...

MySql 导出建表语句_海内存知己天涯若比邻的博客-程序员资料_mysql导出建表语句

基本用法show create table (目标表格) 结果为:+-----------------+---------------------------------------------------------------------------------------------------------------------------------------------------...

推荐文章

热门文章

相关标签