`
hnicypb
  • 浏览: 65066 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

多线程带智能采集策略的采集系统

阅读更多

    去年年底的时候曾经发过一个数据采集器《网页数据采集器》,那是专门针对某一个网站来进行采集的,如果需要采集新的网站内容,就需要修改代码并重新编译。

    昨晚完成了一个带智能策略的采集系统。其实,这个策略的方案三年前就想好了,那时候打算用VB做,做了一半就搁置了。现在用C#才终于把这个方案实现了。

    整个方案大概是这样的:

    需要建立一个AC数据库,MSSQL也行,有四个表:PageType用于记录页面的种类,比如列表页和详细页两类;Url表用于记录要采集的网址,另外还有一个字段TypeID标明该网址属于哪一种页面类型,比如是列表页还是详细页;Rule表记录着各种规则,主要有三个字段,FromTypeID源页类型,ToTypeID目的页类型,Pattern规则;CjPage用于存储采集到的网页内容,还包含网址和页面种类。

    采集策略的核心就在于规则库Rule。

    工作过程大概这样:
1,采集线程从Url表抽取一个网址,并马上在表中将其删除,为了防止冲突,这个过程需要用多线程同步解决;
2,用WebClient请求该网址的页面内容;
3,取得内容后,给线程池的线程来分析处理,本线程回到1,继续去Url表取下一个网址;
4,线程池在有空闲线程时,会调用分析函数ParsePage去处理上次获得的页面内容;
5,先到Rule中取所有FromTypeID为当前网址TypeID;
6,如果没有取到任何规则Rule,则将本页内容写入到CjPage中;
7,如果取到规则,那么遍历规则,为每条规则执行ParseUrl方法;
8,ParseUrl根据规则的Pattern匹配到页面内容中的所有网址,并记录到Url中,规则的ToTypeID就是Url的TypeID。

    至此,整个流程就完成了。下面举一个实际例子来说明一下:
    我要截取动网开发者网络的所有ASP文章http://www.cndw.com/tech/asp/
    首先,在页面类型库中加入列表页和详细页两行,再把http://www.cndw.com/tech/asp/写入到Url中,页面类型是列表页;
    其次,在Rule中加入两条规则:
        一,从列表页取得详细页的网址FromTypeID=1  ToTypeID=2,Pattern是· <a href="([^>]*)" target=_blank>,这条规则将会识别列表页上的所有详细页的链接,并记入到Url中,TypeID是详细页;
        二,从列表页取得列表页的网址FromTypeID=1  ToTypeID=1,Pattern是<a href='([^>]*)'>下一页<\/a>,这条规则将会取得当前列表页上的下一页的链接,并记入到Url中,TypeID还是列表页。
    采集器工作时,如果采集的是详细页的内容,将会直接写入到CjPage中,因为没有FromTypeID=2的规则;而采集的是列表页的内容时,就要做两件事了,因为有两条FromTypeID=1的规则,一件事是识别当前列表页中所有文章的链接并存入Url,另一件事是识别下一列表页链接并存入Url。
    由于规则具有递归性,使得采集器能递归采集到所有的文章。

    下面是一些核心源码(没有公开的都是一些数据层的添删改查的代码):

以下是代码片段:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;
using CJData;
using System.Text.RegularExpressions;
using NLog;

namespace CJ
{
    /// <summary>
    /// 写日志委托
    /// </summary>
    /// <param name="log"></param>
    public delegate void WriteLogCallBack(String log);
    /// <summary>
    /// 采集
    /// </summary>
    public class CaiJi
    {
        private WebClient _wc;

        public WebClient Wc
        {
            get
            {
                if (_wc == null) _wc = new WebClient();
                return _wc;
            }
        }
        private Thread thread;

        public String Name = "";
        public event WriteLogCallBack OnWriteLog;

        /// <summary>
        /// 开始工作
        /// </summary>
        public void Start()
        {
            if (thread != null) return;
            thread = new Thread(new ThreadStart(Work));
            thread.Start();
        }
        /// <summary>
        /// 停止工作
        /// </summary>
        public void Stop()
        {
            if (thread != null) thread.Abort();
            thread = null;
        }

        private void Work()
        {
            int times = 0;
            while (times < 100)
            {
                Url url = Url.SelectOne();
                try
                {
                    if (url != null)
                    {
                        String page = Wc.DownloadString(url.UrlAddress);
                        if (!String.IsNullOrEmpty(page))
                        {
                            OnWriteLog(Name + " 成功抓取:" + url.UrlAddress);
                            times = 0;
                            ThreadPool.QueueUserWorkItem(new WaitCallback(ParsePage), new Object[] { url, page });
                        }
                    }
                    else
                    {
                        //OnWriteLog(Name + " 没有工作,休息半秒");
                        times++;
                        //没有工作,休息半秒
                        Thread.Sleep(500);
                    }
                }
                catch (ThreadAbortException e)
                {
                    OnWriteLog(Name + " 外部终止");
                    break;
                }
                catch (Exception e)
                {
                    times++;
                    OnWriteLog(Name + " 赚取" + url.UrlAddress + "出错,休息半秒。" + e.Message);
                    Trace.WriteLine(url.UrlAddress);
                    //出错,休息半秒
                    Thread.Sleep(500);
                }
            }
            OnWriteLog(Name + " 完成!");
        }

        private void ParsePage(Object state)
        {
            Object[] objs = (Object[])state;
            Url url = objs[0] as Url;
            String page = (String)objs[1];
            IList<Rule> rs = Rule.SelectAll(Rule._.FromTypeID, url.TypeID);
            //if (url.PageType.TypeName == "详细页")
            if (rs == null || rs.Count < 1)
            {
                CjPage cp = new CjPage();
                cp.CjTime = DateTime.Now;
                cp.Content = page;
                cp.Url = url.UrlAddress;
                cp.TypeID = url.TypeID;
                cp.Insert();
            }
            else
            {
                foreach (Rule r in rs)
                {
                    ParseUrl(url, r, page);
                }
            }
        }
        private void ParseUrl(Url u, Rule r, String page)
        {
            Regex reg = new Regex(r.Pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
            MatchCollection ms = reg.Matches(page);
            foreach (Match m in ms)
            {
                Url url = new Url();
                url.TypeID = r.ToTypeID;
                url.UrlAddress = m.Groups[1].Value;
                if (!url.UrlAddress.StartsWith("http://"))
                {
                    if (url.UrlAddress.Substring(0, 1) == "/")
                    {
                        url.UrlAddress = u.UrlAddress.Substring(0, u.UrlAddress.IndexOf("/", 8)) + url.UrlAddress;
                    }
                    else
                    {
                        if (u.UrlAddress.Substring(u.UrlAddress.Length - 1) == "/")
                            url.UrlAddress = u.UrlAddress + url.UrlAddress;
                        else
                            if (u.UrlAddress.LastIndexOf("/") < u.UrlAddress.LastIndexOf("."))
                                url.UrlAddress = u.UrlAddress.Substring(0, u.UrlAddress.LastIndexOf("/") + 1) + url.UrlAddress;
                            else
                                url.UrlAddress = u.UrlAddress + "/" + url.UrlAddress;
                    }
                }
                url.Insert();
            }
        }
    }
}

以下是代码片段:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;

namespace CJ
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        CaiJi[] cjs;
        private void button1_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            if (btn.Text == "停止")
            {
                foreach (CaiJi cj in cjs)
                {
                    if (cj != null) cj.Stop();
                }
                cjs = null;
                btn.Text = "开始";
                return;
            }

            richTextBox1.Text = "";
            btn.Text = "停止";

            int k = 100;
            if (!int.TryParse(textBox1.Text, out k)) k = 100;
            cjs = new CaiJi[k];
            for (int i = 0; i < cjs.Length; i++)
            {
                cjs[i] = new CaiJi();
                cjs[i].Name = "线程" + i.ToString("00");
                cjs[i].OnWriteLog += new WriteLogCallBack(cj_OnWriteLog);
            }
            foreach (CaiJi cj in cjs)
            {
                cj.Start();
            }
        }

        void cj_OnWriteLog(string log)
        {
            if (richTextBox1.InvokeRequired)
            {
                richTextBox1.Invoke(new WriteLogCallBack(cj_OnWriteLog), new object[] { log });
            }
            else
            {
                if (richTextBox1.Lines.Length > 3000) richTextBox1.Text = "";
                richTextBox1.Text = log + Environment.NewLine + richTextBox1.Text;
            }
        }
    }
}

分享到:
评论
1 楼 lwqjz 2010-10-04  
没有看到你所说的智能方面的东西。还是把标题改下算了,把智能去掉。

相关推荐

    多串口多线程技术在实时数据采集与控制中的应用.rar(PDF)

    在实时数据采集与控制中,多线程能够实现数据处理和控制指令的并发执行,确保系统的响应速度和实时性。例如,一个线程负责接收串口数据,另一个线程则负责解析数据并执行控制命令。 三、实时数据采集 实时数据采集...

    C# 数据采集系统 智能采集 数据分析 整套源码

    《C# 数据采集系统:智能采集与数据分析的全方位解析》 C# 数据采集系统是一种高效、灵活的工具,用于从互联网、数据库、文件系统等不同数据源获取信息,并进行智能化处理和分析。在这个整套源码中,我们将深入探讨...

    网络信息智能采集系统.zip

    在网络信息智能采集系统中,除了以上基础功能外,还可能集成一些高级特性,如分布式爬取、多线程处理、云存储对接、大数据分析等,以满足不同场景的需求。随着AI技术的发展,未来的智能采集系统将更加智能化,能够...

    基于Java多线程的智能图片爬虫系统的研究与实现.pdf

    "基于Java多线程的智能图片爬虫系统的研究与实现" 本文研究了一种基于Java多线程的智能图片爬虫系统,旨在解决传统爬虫系统中存在的问题,如爬取大量无效信息、重复图片等。该系统利用HttpClient、JSoup、WebMagic...

    QT版采集工具 ,产品级别,多线程采集数据,存储数据,快速导出数据,任务分配调度清晰,界面美观大方!

    同时,用户还可以自定义任务优先级,灵活调整采集策略。 7. 界面设计:界面设计遵循用户友好原则,美观大方,布局清晰,使得用户能够轻松上手,无需复杂操作就能完成数据采集工作。 8. 教程与支持:提及在QTCN上有...

    基于http协议的Web网络信息智能采集系统设计与实现

    1. **爬虫架构**:通常,一个智能采集系统会采用多线程或分布式爬虫架构,以提高数据抓取效率。多线程允许系统同时处理多个请求,而分布式爬虫则可以将任务分配到多个节点上,进一步提升抓取速度,同时减轻单个节点...

    自动新闻采集系统

    7. **系统架构**:自动新闻采集系统通常采用多线程或多进程模型,以提高抓取效率。同时,为了避免对目标网站造成过大压力,系统会包含反爬策略识别和自我调整机制,如设置延迟、限制请求频率等。 8. **合法性与道德...

    蓝天采集器 云端数据采集发布系统 SkyCaiji

    3. **多线程/分布式**:支持多线程或分布式爬取,以提高数据采集速度,适应大规模网站的抓取需求。 4. **数据清洗与存储**:采集到的数据会经过清洗、去重等预处理步骤,然后存储到数据库或云存储中,便于后续分析和...

    关关采集器V10.0.5674.2187 带3条最新可用采集规则 带文章修复

    在技术层面,关关采集器V10.0.5674.2187可能采用了多线程技术以提高采集效率,同时可能包含了一些智能算法,比如反反爬虫策略,使得它能够在不被目标网站封锁的情况下持续稳定地抓取数据。此外,考虑到数据安全和...

    新闻自动采集系统源代码_新闻文章发布系统.rar

    5. **爬虫技术**:系统中的爬虫可能采用了多线程或者异步IO来提高抓取速度,同时为了避免被目标网站封禁,可能还实现了反反爬策略,如设置用户代理、模拟浏览器行为、延迟请求等。 6. **新闻发布功能**:采集到的...

    文章采集系统源码(c#)

    6. **并发与多线程**:为了提高采集效率,系统可能采用多线程或异步编程模型,如Task并行库,来并发处理多个网页请求。 7. **IP切换与反反爬策略**:为了避免被目标网站封禁,系统可能包含IP轮换机制,同时,需要...

    web网站信息采集系统的设计方案.pdf

    - **并发采集**:为了提高效率,系统可以实现多线程或异步采集,同时处理多个网站或页面。 - **异常处理**:添加错误处理机制,应对可能出现的网络问题、服务器错误等。 - **智能更新**:根据网站内容的更新频率...

    关关采集杰奇通用版【高级优化版V3.5】.rar

    9. **多线程处理**:利用多线程技术,提升采集效率,降低服务器负载。 10. **日志记录**:详尽的采集日志,便于问题排查和优化采集策略。 杰奇CMS配合关关采集器,能够实现内容的自动化管理,大大减轻了网站运营者...

    SNA新闻采集系统v1.0.2 For 动易4.03

    2. **多线程处理**:通过多线程技术,SNA系统能同时处理多个采集任务,大大提高工作效率。 3. **异常处理机制**:遇到网络异常、服务器故障等情况,系统能自动恢复并继续执行任务,保证采集工作的连续性。 五、...

    关关超级采集器8.4全功能版.zip

    1. **全功能高级版本**:此版本包含了关关采集器的所有核心功能,包括网页智能解析、多线程采集、自定义采集规则等,适用于各种复杂的网络数据抓取需求。 2. **杰奇系统支持**:关关采集器8.4特别优化了对杰奇CMS...

    DXC采集2.5版50条采集规则X2.5.

    7. **多线程与分布式采集**:为了提高采集效率,DXC2.5可能增强了多线程和分布式处理能力,能同时处理大量采集任务,尤其适用于大规模数据抓取。 8. **可视化配置**:新版本可能会提供更加直观的用户界面,让非编程...

    IP软件电话在Linux智能终端上的多线程设计.pdf

    本文提出了一种基于Linux智能终端的IP软件电话实现方法,采用模块化多线程设计策略,以提高系统的效率和响应速度。设计中,每个功能模块对应一个独立的线程,通过多线程机制和缓冲区队列实现并行处理,确保了语音...

    关关采集器5.5支持乱序杰奇版本

    为了保证采集效率和数据质量,关关采集器5.5版本可能会内置多线程抓取、IP代理池等功能,使得在高并发环境下也能稳定运行,避免被目标网站封禁。同时,数据存储和导出功能也必不可少,用户可以将采集到的信息保存为...

Global site tag (gtag.js) - Google Analytics