• 模型配置方式实现爬虫


    爬虫框架: DotnetSpider] [三] 模型配置方式实现爬虫

     

    上一篇介绍了最基础的使用方法,也是最自由最灵活的方式,但是其实大多数情况下是可以用模型配置的方式实现一个爬虫的。我们做框架,肯定要尽可能的把一些重复性的工作帮你们做掉,当然灵活性等也会打折扣,但肯定有存在的意义。

    另,爬虫已经对dotnet core rc2 做了适配,可以在dotnet core rc2环境下运行啦

    定义数据对象类

    复制代码
            [Schema("test", "sku", TableSuffix.Today)]
            [TypeExtractBy(Expression = "//li[@class='gl-item']/div[contains(@class,'j-sku-item')]", Multi = true)]
            [Indexes(Index = new[] { "category" }, Unique = new[] { "category,sku", "sku" })]
            public class Product : ISpiderEntity
            {
                [StoredAs("category", DataType.String, 20)]
                [PropertyExtractBy(Expression = "name", Type = ExtractType.Enviroment)]
                public string CategoryName { get; set; }
    
                [StoredAs("cat3", DataType.String, 20)]
                [PropertyExtractBy(Expression = "cat3", Type = ExtractType.Enviroment)]
                public int CategoryId { get; set; }
    
                [StoredAs("url", DataType.Text)]
                [PropertyExtractBy(Expression = "./div[1]/a/@href")]
                public string Url { get; set; }
    
                [StoredAs("sku", DataType.String, 25)]
                [PropertyExtractBy(Expression = "./@data-sku")]
                public string Sku { get; set; }
    
                [StoredAs("commentscount", DataType.String, 32)]
                [PropertyExtractBy(Expression = "./div[5]/strong/a")]
                public long CommentsCount { get; set; }
    
                [StoredAs("shopname", DataType.String, 100)]
                [PropertyExtractBy(Expression = ".//div[@class='p-shop']/@data-shop_name")]
                public string ShopName { get; set; }
    
                [StoredAs("name", DataType.String, 50)]
                [PropertyExtractBy(Expression = ".//div[@class='p-name']/a/em")]
                public string Name { get; set; }
    
                [StoredAs("venderid", DataType.String, 25)]
                [PropertyExtractBy(Expression = "./@venderid")]
                public string VenderId { get; set; }
    
                [StoredAs("jdzy_shop_id", DataType.String, 25)]
                [PropertyExtractBy(Expression = "./@jdzy_shop_id")]
                public string JdzyShopId { get; set; }
    
                [StoredAs("run_id", DataType.Date)]
                [PropertyExtractBy(Expression = "Monday", Type = ExtractType.Enviroment)]
                public DateTime RunId { get; set; }
    
                [PropertyExtractBy(Expression = "Now", Type = ExtractType.Enviroment)]
                [StoredAs("cdate", DataType.Time)]
                public DateTime CDate { get; set; }
            }
    复制代码
    1. 数据对象必须继承ISpiderEntity
    2. Schema定义数据库及表名,可以定义表名后缀
    3. Indexes定义:主键、唯一索引、索引,如果是由多个列构成索引用,号隔开
    4. TypeExtractBy定义这个数据对象的抽取规则, 如上面的数据对象是用于抓取京东的商品信息,我们打开一个页面看一下:

    每个商品为一个li, 并且我们需要存为一行数据,因此TypeExtractBy我们可以指定为XPATH, 并且填上能够把所有元素查询出来表达式://li[@class='gl-item'], 同时把Multi设置为True以告诉爬虫这个表达式查询结果为List. 当你熟练度够了之后,这个表达式还可以改成我上面的

    //li[@class='gl-item']/div[contains(@class,'j-sku-item')]

    原因是京东有一个DIV藏了好几个套装的情况,每个套装又是一个SKU,以上XPATH可以尽可能多的取到数据

      5. 定义数据列:首先定义一个public的属性,在属性上添加StoreAs用于定义在数据库中的存储类型,同时定义该属性的抽取规则PropertyExtractBy,这时候要注意的是,属性的抽取是要计算相对行上面数据行的PATH,而不是绝对的。如下,

    我们行首先已经定位到了gl-i-warp j-sku-item这个DIV,假设你想从这个DIV的attribute中取得sku数据,这时你应该这么定义(.表示当前节点,具体可以去w3school学一下XPATH)

    [StoredAs("sku", DataType.String, 25)]
    [PropertyExtractBy(Expression = "./@data-sku")]
    public string Sku { get; set; }

      6. 环境变量:我们经常用到的一个场景是,抓取SKU的链接是从category过来的,我们想保留category的信息到SKU表中,这时候如何把category的信息传递到数据对象中呢? 需要做两步:

    第一是在初始化的StartUrl时要添加信息

    context.AddStartUrl("http://list.jd.com/list.html?cat=9987,653,655&page=2&JL=6_0_0&ms=5#J_main", new Dictionary<string, object> { { "name", "手机" }, { "cat3", "655" } });

    第二是定义环境变量属性抽取规则:

    [StoredAs("category", DataType.String, 20)]
    [PropertyExtractBy(Expression = "name", Type = ExtractType.Enviroment)]
    public string CategoryName { get; set; }

      7. 特殊数据:大多会用到当天时间,或者星期一等特殊时间,我们也可以使用环境变量来实现

    [StoredAs("run_id", DataType.Date)]
    [PropertyExtractBy(Expression = "Monday", Type = ExtractType.Enviroment)]
    public DateTime RunId { get; set; }

    [PropertyExtractBy(Expression = "Now", Type = ExtractType.Enviroment)]
    [StoredAs("cdate", DataType.Time)]
    public DateTime CDate { get; set; }

    定义爬虫

    定义一个类,继承SpiderBuilder

    复制代码
            protected override SpiderContext GetSpiderContext()
            {
                SpiderContext context = new SpiderContext();
                context.SetSpiderName("JD sku/store test " + DateTime.Now.ToString("yyyy-MM-dd HHmmss"));
                context.AddTargetUrlExtractor(new Extension.Configuration.TargetUrlExtractor
                {
                    Region = new Extension.Configuration.Selector { Type = ExtractType.XPath, Expression = "//span[@class="p-num"]" },
                    Patterns = new List<string> { @"&page=[0-9]+&" }
                });
                context.AddPipeline(new MysqlPipeline
                {
                    ConnectString = "Database='test';Data Source=;User ID=root;Password=;Port=4306"
                });
                context.AddStartUrl("http://list.jd.com/list.html?cat=9987,653,655&page=2&JL=6_0_0&ms=5#J_main", new Dictionary<string, object> { { "name", "手机" }, { "cat3", "655" } });
                context.AddEntityType(typeof(Product));
                return context;
            }
    复制代码
    1. 使用SetSpiderName设置爬虫名称,不合规范的名字会抛异常
    2. 使用AddTargetUrlExtractor配置目标页的解析器(在定题爬虫中一般是翻页), Region表式你需要在哪一个元素下面的url, 正则的作用是把找到的所有url用正则作匹配,符合规则的就是你想要的合法翻页链接。如下图,我们把翻页的区域定位到class为p-nm这个span下面,同时用正则&page=[0-9]+&来做筛选

      3. 配置URL调度,如果不配置则默认使用内存Queue, 可以使用已经实现的Queue或者RedisScheduler(用于分布式)

                context.SetScheduler(new RedisScheduler
                {
                    Host = "",
                    Password = "",
                    Port = 6379
                });

      4. 我们添加一个MySql的数据管道,只需要配置好你的连接字符串即可

      5. 添加StartUrl, 这里可以添加额外数据用于传递到目标数据对象

      6. 添加目标数据对象:

    context.AddEntityType(typeof(Product));

      7. 运行爬虫:

                JdSkuSampleSpider spiderBuilder = new JdSkuSampleSpider();
                spiderBuilder.Run("rerun");

      8. 额外配置:大家可以自己查看API,可以配置线程数SetThreadNum, 在广义搜索爬取时控制深度(SetDeep), 设置Pipeline的数据提交频次SetCachedSize等

    完整代码参见:

    https://raw.githubusercontent.com/zlzforever/DotnetSpider/master/src/Java2Dotnet.Spider.Test/Example/JdSkuSample.cs

    可以看到,只需区区几行代码就可以实现一个爬虫,帮你建表、分表,抓取、抽取数据,还是比较简单的

    下一篇

    介绍如何抓取一些API,JSONPATH的数据

  • 相关阅读:
    luoguP4113 [HEOI2012]采花
    JDOJ1100: Fix
    luoguP1972 [SDOI2009]HH的项链
    luoguP3431 [POI2005]AUT-The Bus
    luoguP5094 [USACO04OPEN]MooFest 狂欢节
    【转】主定理
    LeetCode 392. Is Subsequence 详解
    花式求解 LeetCode 279题-Perfect Squares
    利用遗传算法求解TSP问题
    DAPP开发初探——永存的留言
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5597132.html
Copyright © 2020-2023  润新知