热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

简单多线程网络爬虫的实现

虽然在博客园也有两年的时间了,但基本没写过什么博客,昨天心血来潮,写了个专门针对某个网站的站外搜索工具,今天想写点什么和大家分享下。 由于使用Lucene搭建搜索引擎已经有大篇的文献可参考,所以在此不再详述。 主要实现功能: (1)实现简单网络爬虫,多线程从互联网下载网页,防死循环 (2)使用Lucene实现本地搜索,对下载的网页进行分析,创建索引

前言

虽然在博客园也有两年的时间了,但基本没写过什么博客,昨天心血来潮,写了个专门针对某个网站的站外搜索工具,今天想写点什么和大家分享下。

由于使用Lucene搭建搜索引擎已经有大篇的文献可参考,所以在此不再详述。

主要实现功能

(1)实现简单网络爬虫,多线程从互联网下载网页,防死循环

(2)使用Lucene实现本地搜索,对下载的网页进行分析,创建索引

爬虫原理

本文实现的爬虫通过给定的一个网站链接,下载链接的网页,用正则表达式提取链接内的网址,然后再重复下载获得的网址的网页,提取链接。

实现

由于这只是简单的实现爬虫,所以未使用数据库来保存待请求,已经请求过得链接,而使用Dictionary类型数据来代替,Dictionary类型数据能有效的减短链接匹配的时间复杂度。

为了防止类似 http://xxxx.xxx?xx=xxx&t=123456 这样的类似的网站循环导向  http://xxxx.xxx?xx=xxx&t=123457 导致爬虫死循环,所以把链接分层管理,主页的链接为第一层,第一层链接的网页内容中的链接为第二层链接,依次类推,理论上网页的深度不超过17层,所以可以通过控制爬虫深爬的层数来防止死循环。   

主要代码:

        /// 
        /// 请求过的链接
        /// 
        Dictionary<string, UrlInfo> requestedUrls = new Dictionary<string, UrlInfo>();
        /// 
        /// 将要请求的链接
        /// 
        Dictionary<string, UrlInfo> toRequestUrls = new Dictionary<string, UrlInfo>();
        /// 
        /// 正在请求的链接
        /// 
        Dictionary<string, UrlInfo> OnRequestUrls= new Dictionary<string, UrlInfo>();
        /// 
        /// 当前文档编码
        /// 
        Encoding encoder = Encoding.UTF8;
        /// 
        /// 字典数据读写锁
        /// 
        object lockhash = new object();
        /// 
        /// 处理一个链接,请求链接内容,获取网址保存到toRequestUrls,并建立文档索引
        /// 
        public string RequestWebPageThread(UrlInfo urlinfo, Encoding encoder=null)
        {
            Uri uri = new Uri(urlinfo.Url, UriKind.RelativeOrAbsolute);
            if (!uri.IsAbsoluteUri || uri.IsFile)
                return string.Empty;

            WebClient wc = new WebClient();
            //伪装User-Agent,防止部分网站禁止爬取数据
            wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4) Gecko/20011128 Netscape6/6.2.1");
            var downdata = wc.DownloadData(uri);
            string datastr = string.Empty;
            if (encoder != null)
            {
                datastr = encoder.GetString(downdata);
            }
            else
            {
                string html = Encoding.UTF8.GetString(downdata);
                //更具网页内容,获取编码方式
                Encoding realEncoding = HtmlHelper.GetEncoding(html);
                datastr = realEncoding.GetString(downdata);
            }
           //理论上网站最多17层,当当前请求的网页相对于主页的层数超过webrequestLevel时则不获取其页面内的链接
            if (urlinfo.Level < webrequestLevel)
            {
                var mts = urlReg.Matches(datastr);
                var dataurls = from Match m in mts
                               select m.Groups[1].Value;
                lock (lockhash)
                {
                    //添加url到字典中
                    foreach (string durl in dataurls)
                    {
                        try
                        {
                            Uri nuri;
                            if (absobleUrlReg.IsMatch(durl))
                            {
                                nuri = new Uri(durl, UriKind.Absolute);
                                if (nuri.Host != uri.Host)
                                    continue;
                            }
                            else
                            {
                                nuri = new Uri(uri, durl);
                            }
                            FileBase fb = new FileBase(nuri.ToString());
                            if (unacceptext.Contains(fb.Ext.ToLower()))
                                continue;
                            //添加url到索引列表
                            if (!(requestedUrls.ContainsKey(nuri.ToString()) || toRequestUrls.ContainsKey(nuri.ToString()) || onRequestUrls.ContainsKey(nuri.ToString())))
                                toRequestUrls.Add(nuri.ToString(), new UrlInfo() { Url = nuri.ToString(), Level = urlinfo.Level + 1 });
                        }
                        catch (Exception ex) { }
                    }
                }
            }
            if (!string.IsNullOrWhiteSpace(datastr))
            {
                var titleMT = MakeIndexRule.TitleReg.Match(datastr);
                string title = titleMT.Success ? titleMT.Groups[1].Value : string.Empty;
                if (!string.IsNullOrWhiteSpace(title))
                {
                    indexrule.AddItem(uri.ToString(), title,datastr);
                }
            }
            Console.WriteLine(string.Format("{0}-{1}-{2}",requestedUrls.Count,onRequestUrls.Count,toRequestUrls.Count));
            return datastr;
        }
        /// 
        /// 网页请求线程函数
        /// 
        public void RequestThread(object e)
        {
            while (true)
            {
                if (requestedUrls.Count > 0 && toRequestUrls.Count <1 && onRequestUrls.Count <1)
                {
                    break;
                }
                if (toRequestUrls.Count <1)
                {
                    Thread.Sleep(200);
                    continue;
                }
                UrlInfo url=null;
                lock (lockhash)
                {
                    //获取下一个要请求的url
                    if (toRequestUrls.Count > 0)
                    {
                        url = toRequestUrls.FirstOrDefault().Value;
                        onRequestUrls.Add(url.Url,url);
                        toRequestUrls.Remove(url.Url);
                    }
                }
                if (null!=url)
                {
                    try
                    {
                        //请求网页
                        RequestWebPageThread(url, encoder);
                    }
                    catch (Exception)
                    {
} finally { lock (lockhash) { onRequestUrls.Remove(url.Url); requestedUrls.Add(url.Url,url); } } } } } /// /// 初始化爬虫 /// public void StartRequestThread(string url,Encoding encoder,int threadcount) { this.encoder = encoder; toRequestUrls.Add(url,new UrlInfo() { Url=url,Level=1}); //创建索引构造器 indexrule = new MakeIndexRule(); try { Uri uri = new Uri(url, UriKind.Absolute); //获取索引绝对位置 indexrule.INDEX_DIR = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, uri.Host); if (indexrule.StartIndex(uri.Host)) { for (int i = 0; i ) { //创建网页请求线程 Thread thread = new Thread(RequestThread); thread.IsBackground = true; thread.Start(url); } } } catch (Exception) { } finally { while (true) { if (requestedUrls.Count > 0 && toRequestUrls.Count <1 && onRequestUrls.Count <1 ) { break; } Thread.Sleep(1000); } indexrule.Rebuild(); indexrule.Close(); } }

网页的编码方式有多种,使用WebClient请求的网页数据时常乱码,经查看网页文件发现通常有一行

所以我们可以根据meta标签内的编码方式来编码。

        public static Encoding GetEncoding(string html)
        {
            var pattern = "(?i)charset *= *[\'\"]?(?[-a-zA-Z_0-9]+)[\'\"]?"; 
            var charset = Regex.Match(html,pattern,RegexOptions.IgnoreCase).Groups["charset"].Value; 
            if(charset.Length <= 0)
            { 
                charset = Encoding.UTF8.BodyName;
            }
            try
            {
                return Encoding.GetEncoding(charset);
            }
            catch(Exception)
            {
                return Encoding.Default;
            }
        }

实现效果

 

下载地址:http://sdrv.ms/1camhyg

总结

     心血来潮就写了这个,只为学习参考,代码写的有点乱。毕业了,这是我在学校的最后一个学习项目。工作了要多到园子里分享,毕竟园子里有很多大神帮助过我们。就这样吧,回家了。


推荐阅读
  • 本文介绍了如何使用Python爬取妙笔阁小说网仙侠系列中所有小说的信息,并将其保存为TXT和CSV格式。主要内容包括如何构造请求头以避免被网站封禁,以及如何利用XPath解析HTML并提取所需信息。 ... [详细]
  • python模块之正则
    re模块可以读懂你写的正则表达式根据你写的表达式去执行任务用re去操作正则正则表达式使用一些规则来检测一些字符串是否符合个人要求,从一段字符串中找到符合要求的内容。在 ... [详细]
  • 本文将介绍如何在混合开发(Hybrid)应用中实现Native与HTML5的交互,包括基本概念、学习目标以及具体的实现步骤。 ... [详细]
  • C#实现文件的压缩与解压
    2019独角兽企业重金招聘Python工程师标准一、准备工作1、下载ICSharpCode.SharpZipLib.dll文件2、项目中引用这个dll二、文件压缩与解压共用类 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本教程详细介绍了如何使用 Spring Boot 创建一个简单的 Hello World 应用程序。适合初学者快速上手。 ... [详细]
  • Java设计模式详解:解释器模式的应用与实现
    本文详细介绍了Java设计模式中的解释器模式,包括其定义、应用场景、优缺点以及具体的实现示例。通过音乐解释器的例子,帮助读者更好地理解和应用这一模式。 ... [详细]
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • WCF类型共享的最佳实践
    在使用WCF服务时,经常会遇到同一个实体类型在不同服务中被生成为不同版本的问题。本文将介绍几种有效的类型共享方法,以解决这一常见问题。 ... [详细]
  • 本文总结了Java初学者需要掌握的六大核心知识点,帮助你更好地理解和应用Java编程。无论你是刚刚入门还是希望巩固基础,这些知识点都是必不可少的。 ... [详细]
  • JUC(三):深入解析AQS
    本文详细介绍了Java并发工具包中的核心类AQS(AbstractQueuedSynchronizer),包括其基本概念、数据结构、源码分析及核心方法的实现。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • Tornado框架中模块与静态文件的应用
    本文详细介绍了Tornado框架中模块和静态文件的使用方法。首先明确模块与模板的区别,然后通过具体的代码示例展示如何在HTML文档中使用模块,并配置模块的路由。最后,提供了模块类中参数获取的示例。 ... [详细]
author-avatar
mingtong77_491_981
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有