Fork me on GitHub
爬虫的框架

[开源 .NET 跨平台 数据采集 爬虫框架: DotnetSpider] [二] 最基本,最自由的使用方式

上一篇大至 介绍了一下爬虫的框架设计,从这一篇开始着重介绍如何使用这个爬虫。

数据抽取定义

之前也有人反应说用Attribute+模型来定义抽取规则太花哨,实用性不强。实际上可能他没有仔细看到我的设计,我的核心抽取不是Attrbiute+模型,而是采用类似JSON的定义格式,可以实现各种嵌套,各种能想像到的复杂情况。参考最早一版定义(最新版有修改,设计思路没有变化)

复制代码
"entities": [
        {
            "targeturls": [
                {
                    "sourceregion": "",
                    "values": [
                        "http://shu.taobao.com/top/[\\d]+/search"
                    ]
                }
            ],
            "expression": "//div[contains(@class,'mod')]",
            "multi": true,
            "selector": "xpath",
            "schema": {
                "databasename": "alibaba",
                "tablename": "industry_rank"
            },
            "identity": "industry_rank",
            "fields": [
                {
                    "datatype": "string(100)",
                    "expression": "./h3[1]",
                    "name": "category",
                    "selector": "xpath",
                    "multi": false
                },
                {
                    "datatype": {
                        "fields": [
                            {
                                "datatype": "string(100)",
                                "expression": ".//a[1]",
                                "name": "keyword",
                                "selector": "xpath",
                                "multi": false
                            },
                            {
                                "datatype": "string(100)",
                                "expression": "./@data-rise",
                                "name": "rise",
                                "selector": "xpath",
                                "multi": false
                            }
                        ]
                    },
                    "expression": ".//ol/li",
                    "multi": true,
                    "name": "results",
                    "selector": "xpath"
                }
            ]
        }
    ]
复制代码
  1. Entities 是数组,表示一个页面可以抽取出多个数据对象
  2. 第一个Entity的第一个Field是string(100)的数据类型(常用数据类型)
  3. 第一个Entity的第二个Field是一个数据对象

因此,爬虫的解析是非常自由化的。而Attrbiute+模型的抽取是先转换成了以上定义再传给解析类的,我设计这个解析类的原因也是考虑到跨语言的可能性的,只要你能传正确的JSON过来,我就能解析成一个正确的爬虫。所以只要有兴趣的人写上他们自己语言的Provider, 其实就是写几个Class序列化成JSON传过来就好了。

是否够灵活?

也有人反应说Attrbiute+模型的抽取不够灵活,不能满足大部分情况。其实最灵活的就是使用核心库即Core这个DLL,在这个项目里,实现了爬虫的基本逻辑,URL调度、去重,Html的Selector,基本的下载器,多线程控制等等。就是说,你要自由、灵活我也给你的呀。

如何使用核心库

我们在上一篇也说过,实现一个完整的业务爬虫需要4大模块:下载器(已有实现),URL调度(已有实现),数据抽取(需要自己实现),数据存储(需要自己实现),因此,你只需要实现2个模块就可以完成一个爬虫了

定义数据对象

    public class YoukuVideo
    {
        public string Name { get; set; }
        public string Volume { get; set; }
    }

 

数据抽取的实现

只需要实现 IPageProcessor 这个接口就可以了

复制代码
    public class MyPageProcessor : IPageProcessor
    {
        public Site Site
        {
            get; set;
        }

        public void Process(Page page)
        {
            var totalVideoElements = page.Selectable.SelectList(Selectors.XPath("//li[@class='yk-col4 mr1']")).Nodes();
            List<YoukuVideo> results = new List<YoukuVideo>();
            foreach (var videoElement in totalVideoElements)
            {
                var video = new YoukuVideo();
                video.Name = videoElement.Select(Selectors.XPath(".//li[@class='title']/a[1]")).GetValue();
                video.Volume = videoElement.Select(Selectors.XPath(".//ul[@class='info-list']/li[3]")).GetValue();
                video.Volume = video.Volume.Replace("\r", "");
                results.Add(video);
            }
            page.AddResultItem("VideoResult", results);
        }
    }
复制代码

 

这里就需要注意4点

  1. public Site Site { get; set; } 是必需的,并且不需要市值,会在Spider类的初始化时设值
  2. Page 对象传过来的时候,已经是加载好下载的HTML到Selectable属性中了,所以你只需要调用Seletable的接口并传入合适的查询XPATH,CSS, JSONPATH,REGEX就可以查询到你想到值,并且Selectable是可以循环调用
  3. Selectable的GetValue传入true时会把结果去HTML标签
  4. 把你在此处组装好的对象,如上面的 YoukuVideo这个List, 存到page的ResultITem中,并指定一个KEY

数据存取

只需要实现 IPipeline,在这里,我们需要用到在 PageProcessor存入数据的KEY,通过这个KEY把数据对象取出来,之后你想把这个数据存文件或是MYSQL,MSSQL,MONGODB就由你自己实现了

复制代码
    public class MyPipeline : IPipeline
    {
        private string _path;


        public MyPipeline(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new Exception("XXXX");
            }

            _path = path;

            if (!File.Exists(_path))
            {
                File.Create(_path);
            }
        }

        public void Process(ResultItems resultItems, ISpider spider)
        {
            foreach (YoukuVideo entry in resultItems.Results["VideoResult"])
            {
                File.AppendAllText(_path, JsonConvert.SerializeObject(entry));
            }
        }

        public void Dispose()
        {
        }
    }
复制代码

 

运行爬虫

写好上面两个必需的模块之后,我们就可以运行这个爬虫了。首先需要下载最新的DotnetSpider的代码并编译,编译成功后会把DLL移动到solution文件夹下的output文件夹。我们建立一个空的Console程序,引用必要的DLL如下图

再把上面3个类添加到Project中

运行代码如下,需要注意必须要添加StartUrl, 这是爬虫的第一个起始URL,如果你可以初始计算出所有的翻页URL,也可以在这里一定完全初始化。

复制代码
        public static void Main()
        {
            HttpClientDownloader downloader = new HttpClientDownloader();
            var site = new Site() { EncodingName = "UTF-8" };
            site.AddStartUrl("http://www.youku.com/v_olist/c_97_g__a__sg__mt__lg__q__s_1_r_0_u_0_pt_0_av_0_ag_0_sg__pr__h__d_1_p_1.html");
            Spider spider = Spider.Create(site, new MyPageProcessor(), new QueueDuplicateRemovedScheduler()).AddPipeline(new MyPipeline("test.json")).SetThreadNum(1);
            spider.Run();
        }
复制代码

F5运行项目,结果如下

 

如何翻页、抽取目标页

上面的例子只爬取了一个页面,那么如何从页面中抽取翻页的URL或者其它的目标页呢?我们只需要在PageProccessor中解析你的目标页,并加入到Page对象的TargetRequests这个List中即可。我们做如下改动:

复制代码
    public class MyPageProcessor : IPageProcessor
    {
        public Site Site
        {
            get; set;
        }

        public void Process(Page page)
        {
            var totalVideoElements = page.Selectable.SelectList(Selectors.XPath("//li[@class='yk-col4 mr1']")).Nodes();
            List<YoukuVideo> results = new List<YoukuVideo>();
            foreach (var videoElement in totalVideoElements)
            {
                var video = new YoukuVideo();
                video.Name = videoElement.Select(Selectors.XPath(".//li[@class='title']/a[1]")).GetValue();
                video.Volume = videoElement.Select(Selectors.XPath(".//ul[@class='info-list']/li[3]")).GetValue();
                video.Volume = video.Volume.Replace("\r", "");
                results.Add(video);
            }
            page.AddResultItem("VideoResult", results);

            foreach (var url in page.Selectable.SelectList(Selectors.XPath("//ul[@class='yk-pages']")).Links().Nodes())
            {
                page.AddTargetRequest(new Request(url.GetValue(), 0, null));
            }
        }
    }
复制代码

重新运行程序,我们可以看到,爬虫不停的开始翻页往下爬取了

总结

到这里最基本,最灵活的使用方法就结束了。是不是很简单?至于有朋友提到的要用到大量的if else, replace什么的你都可以在PageProcessor里做这个组装,抽取的工作。然后我个人感觉世上没有十全十美的东西 ,灵活就可能要更多的代码,而Attrbibute+模型的死板也不是一无是处,至少我用下来70%-80%都可以应付,更何况在Attribute上还可以配置各种各样的Formatter, 当然跟我的抓取对象大多结构化比较规范有关吧。大至缕一下后面要写的章节吧

  • HTTP Header、Cookie的设置,POST的使用
  • JSON 数据的解析
  • 基于配置的使用(Extension项目)
  • WebDriverDownloader的使用,包含基本登录,手动登录
  • 分布式的使用
posted on 2016-05-25 09:29  HackerVirus  阅读(346)  评论(0编辑  收藏  举报