关键技术之单机爬虫的实现(3)---URL存储到哪呢,存内存太占内存,存数据库效能不佳

这个问题,其实归根到底是空间与时间的问题。可以想象如果将url全部存在内存中,那么很快内存就会被全部占用。但是如果存在文件中,每次读取或者加入都去要操作文件。这个性能消耗是比较大的。因此,很快我们可以想到计算机中的缓存出现的原因不就是这点嘛。我的设计思路是:通过建立内存、文件、数据库三级存储。这样可以一定程度上取得满意的效果。 说明下我这里设计数据库主要是为分布式网络爬虫用的。就是当网络爬虫发现url时,我们把是本节点的任务的url存入文件。不是本节点任务的url存入公共数据库。这样就不需节点之间频繁的传递url,这是后面我们要说的。现在我们考虑单机爬虫,需要解决下面几个问题。

        1、网络爬虫发现url时怎么存

        2、网络爬虫需要取url时怎么取

        3、存取不能破坏原先的顺利

        这样的话,我们的设计三个队列。内存队列cache,文件队列finding,waitting队列。finding队列是负责存放从网页中分析出来的URL,该队列在磁盘上。waitting队列是用来存放每次从内存中写回到磁盘上的URL,该队列也在磁盘上,优先级高于finding队列。cache队列用来存放每次读到内存中将要进行DNS解析的URL,该队列在内存中。

        首先,所有的URLs都被存在磁盘上的finding队列中,在common队列中,对于磁盘上的每个新的URL,都要对其hostname进行DNS解析,由于DNS解析的过程非常耗时,因此我们需要一个缓存去存放URLs,因此我们在内存中建了一个队列(cache队列),但是缓存的空间是有限的,当内存队列满了以后,这些多余URLs就会被写回到磁盘上,为了仍然保持原来的优先级,我们在磁盘上又建了一个队列(waitting队列)去存储那些被写回到磁盘上的URLs,否则如果仍被存于原来的finding队列中,根据队列的性质,将被写回到队列尾部,这样便会影响原有URL的优先级。因此,当内存中的队列下次有空余时,每次便先会从waitting队列中取URLs,当waitting队列空了以后,才会继续从finding队列中取得URL进行以后的工作。

我们建立一个UrlQueueManager类,下面是没有考虑waitting时候的参考代码:

     public class UrlQueueManager
    {
        public Queue<string> cachequeue = new Queue<string>();
        private FileIO finding= new FileIO();  
       
        public void Init()
        {
            finding.CloseWriteFile();
            finding.CloseReadFile();
            finding.OpenWriteFile(Spider.root + "finding.txt");
            finding.OpenReadFile(Spider.root + "finding.txt");
        }

        public int Count
        {
            get
            {
                int count = 0;
                count = cachequeue.Count;
                return count;
            }
        }

        public void Clear()
        {
              cachequeue.Clear();
        }

        public void Enqueue(UrlInfo url)
        {
                if (Count < Spider.urlqueuemaxcount)
                {
                    cachequeue.Enqueue(url.ToString());
                }
                else
                {
                    finding.WriteLine(url.ToString());
                }
        }

        public UrlInfo Dequeue()
        {
            UrlInfo url = new UrlInfo();
                if (Count > 0)
                {

                    if (Count < Spider.urlqueuemaxcount / 2)
                    {
                        for (int i = 0; i < Spider.urlqueuemaxcount / 2; i++)
                        {
                            if (!finding.IsEof())
                            {
                                cachequeue.Enqueue(finding.ReadLine());
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    string cacheurl = cachequeue.Dequeue();
                    string[] spiler = cacheurl.Split('|');
                    url.Url = spiler[0];
                    url.Depth = Convert.ToInt32(spiler[1]);
                    url.Weight = Convert.ToInt32(spiler[2]);
                }
                else
                {
                    if (!finding.IsEof())
                    {
                        string waitingurl = finding.ReadLine();
                        string[] spiler = waitingurl.Split('|');
                        url.Url = spiler[0];
                        url.Depth = Convert.ToInt32(spiler[1]);
                        url.Weight = Convert.ToInt32(spiler[2]);
                    }
                }
            return url;
        }
    }

看明白了思路,实现问题不大。待续。。。

posted @ 2010-04-19 22:52  小军人  阅读(4408)  评论(4编辑  收藏  举报