C#程序设计之多线程爬虫程序

一、简单介绍:
技术方面主要包括:
(1)技术选型:
1) 课程设计使用的开发语言是C#。
2)课程设计选用了文件流方式获取网站数据。
3)课程设计使用多线程抓取网页代码。
4)课程设计使用了正则表达式对源码进行解析处理。

(2)程序运行流程:
通过图示可以更形象的了解程序运行的整个流程:
1)程序首先下载网站首页的源代码
2)对首页源代码进行分析,提取出网站建设类目下的链接并存储到队列中。
3)运用多线程,分别同时下载队列中的链接。
4)利用正则表达式对下载的链接源码进行分析。提取图片的URL并下载图片。截取需要的文本信息。
5)保存下载的文本和图片。把下载分析链接的信息显示在操作界面上。

程序的整体流程:
这里写图片描述

二、再贴代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Threading;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace crawlWebsiteAndExtractInfo
{

    public partial class frmCrawlWebsite : Form
    {
        public static Queue<String> q = new Queue<string>();
        public static string[] surl = new string[100];       //照片冗余
        public static int a = 0;
        public static int j = 0;
        //public static int m = 0;
        public static bool b = false;
        public static object locker = new object();//添加一个对象作为锁
        public static object locker1 = new object();
        public static object locker2 = new object();
        public static object locker3 = new object();
        public static Stopwatch watch = new Stopwatch();
        public static bool flag=true;
        public static string textbox = string.Empty;
        public int Num=5;
         //string[] surl = new string[300];
        public frmCrawlWebsite()
        {
            InitializeComponent();
        }

        private void btnCrawlAndExtract_Click(object sender, EventArgs e)
        {

            //获得网址
            //http://www.hyzbi.com
            string urlToCrawl = txbUrlToCrawl.Text;

            //HTTP请求
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(urlToCrawl);
            //GET方法
            req.Method = "GET";
            //获得HTTP回复
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            //定义编码方式
            string htmlCharset = "utf-8";
            //编码方式
            Encoding htmlEncoding = Encoding.GetEncoding(htmlCharset);
            StreamReader sr = new StreamReader(resp.GetResponseStream(), htmlEncoding);
            //显示html内容
            string respHtml = sr.ReadToEnd();
            rtbExtractedHtml.Text = respHtml;
        }

        private void btnExtractInfo_Click(object sender, EventArgs e)
        {
            //找到网站建设类目下面的连接         
            string h1userP = @"/portal/article/index/cid/19/id/(\d+)";
            //捕获匹配
            MatchCollection foundH1user = (new Regex(h1userP)).Matches(rtbExtractedHtml.Text);
            foreach (Match m in foundH1user)
            {

                string url = "http://www.hyzbi.com" + (string)m.Value;

                q.Enqueue(url);


            }
            Thread[] downloadThread;//声名下载线程

            downloadThread = new Thread[21];//为线程申请资源,确定线程总数
            //int i=0;
           // richTextBox1.Text = "aaa";

            watch.Start();
            for (int i = 0; i <Num; i++)
            {
                ThreadStart startDownload = new ThreadStart(DownLoad);
                //ParameterizedThreadStart startDownload = new ParameterizedThreadStart(DownLoad);

                downloadThread[i] = new Thread(startDownload);//指定线程起始设置
                downloadThread[i].Start();//逐个开启线程
            }

            //while (q.Count != 0) ;




        }
        public delegate void ProcessDelegate();
        public void richTextShow(string ss)
        {
            string s=ss;
            richTextBox1.Text=s;

        }

        public void picture(string rrh)
        {
            string rh = rrh;
            string pp = @"src\s*=\s*[""']?([^'"" >]+?)[ '""][^>]*?>";
            MatchCollection found = (new Regex(pp)).Matches(rh);

            foreach (Match mm in found)
            {

                string urll = "http://www.hyzbi.com" + (string)mm.Groups[1].Value;
                int id = Array.IndexOf(surl, urll);
                //exists = ((IList)surl).Contains(urll);
                if (id == -1)
                {
                    surl[a] = urll;
                    a++;
                    try
                    {
                        Bitmap img = null;
                        HttpWebRequest req = (HttpWebRequest)(WebRequest.Create(urll));
                        req.Method = "GET";
                        HttpWebResponse res = (HttpWebResponse)(req.GetResponse());
                        img = new Bitmap(res.GetResponseStream());
                        lock (locker3)
                        {
                            img.Save(@"e:/c/" + a + ".jpg");
                        }
                        //m++;
                    }
                    catch (Exception ee)
                    {

                    }

                }
            }
        }
       // public void picture save()
        public string wenben(string rrh)
        {
            string rh = rrh;
            //抓取标题
            //string hstrOutput=null;
            string bt = @"<h2>(.*)</h2>";
            //StreamWriter sw = new StreamWriter("e:/b.txt", true);
            //MatchCollection bbt = (new Regex(bt)).Matches(rh);
            Match nnn = (new Regex(bt)).Match(rh);

           // foreach (Match nn in bbt)
            //{
                //richTextBox1.Text += nn.Value + "\n";
                Regex hregex = new Regex("<.+?>", RegexOptions.IgnoreCase);
                string hstrOutput = hregex.Replace(nnn.Value, "");//替换掉"<"和">"之间的内容
                hstrOutput = hstrOutput.Replace("<", "");
                hstrOutput = hstrOutput.Replace(">", "\r");
                hstrOutput = hstrOutput.Replace(" ", "");
               // sw.WriteLine(hstrOutput);
               // sw.WriteLine("\n");
           // }
                string p = @"<p.*>(<span .*>)?(.*)(</span>)?</p>";
                MatchCollection tp = (new Regex(p)).Matches(rh);
                lock (locker)
                {
                    StreamWriter sw = new StreamWriter("e:/b.txt", true);
                    sw.WriteLine(hstrOutput);
                    sw.WriteLine("\n");
                    //StreamWriter sw = new StreamWriter("e:/b.txt", true);
                    //string p = @"<p.*>(<span .*>)?(.*)(</span>)?</p>";
                    //MatchCollection tp = (new Regex(p)).Matches(rh);
                    foreach (Match n in tp)
                    {
                        Regex regex = new Regex("<.+?>", RegexOptions.IgnoreCase);
                        string strOutput = regex.Replace(n.Value, "");//替换掉"<"和">"之间的内容

                        strOutput = strOutput.Replace("<", "");

                        strOutput = strOutput.Replace(">", "\r");

                        strOutput = strOutput.Replace(" ", "");

                        //sw.WriteLine(n.Groups[0].Value);
                        sw.WriteLine(strOutput);
                        sw.WriteLine("\n");
                    }
                    sw.Close();
                }
            return hstrOutput;
        }
        public  void DownLoad()
        {

                while (true)
                {
                    string url;
                    string h2;
                    if (q.Count != 0)
                    {
                        lock (locker1)
                        {
                            url = q.Dequeue();
                            j++;
                        }
                        //richTextBox1.Text = url;
                        try
                        {
                            HttpWebRequest rr = (HttpWebRequest)WebRequest.Create(url);
                            rr.Method = "GET";
                            HttpWebResponse resp = (HttpWebResponse)rr.GetResponse();

                            string htmlCharset = "utf-8";
                            Encoding htmlEncoding = Encoding.GetEncoding(htmlCharset);
                            StreamReader sr = new StreamReader(resp.GetResponseStream(), htmlEncoding);
                            string rh = sr.ReadToEnd();
                            picture(rh);
                            h2 = wenben(rh);
                            lock (locker2)
                            {

                                ProcessDelegate showProcess = delegate()
                                {

                                    richTextBox1.AppendText(url + h2 + DateTime.Now.ToString() + "\n");

                                };

                                richTextBox1.Invoke(showProcess);

                            }



                        }
                        catch (Exception eee)
                        {

                        }
                    }
                    else
                    {
                       /* flag = false;
                        watch.Stop();
                        string time = watch.ElapsedMilliseconds.ToString();
                        ProcessDelegate showTime = delegate()
                        {

                            richTextBox1.AppendText("共用时" + time + "\n");

                        };

                        richTextBox1.Invoke(showTime);*/
                        break;
                    }

                }
        }



       // }

        private void button1_Click(object sender, EventArgs e)
        {
            //string path1 = @"e:\c";  //打开D盘下的log.txt文件
            //System.Diagnostics.Process.Start(path1);
            string path2 = @"e:\c";  //调用资源管理器,打开e盘下的c文件夹
            System.Diagnostics.Process.Start("explorer", path2);
        }

        private void button2_Click(object sender, EventArgs e)
        {

                string path1 = @"e:\b.txt";
                System.Diagnostics.Process.Start("explorer",path1);

        }

        private void button3_Click(object sender, EventArgs e)
        {
            try
            {
                textbox = this.textBox1.Text;
                Num = Convert.ToInt32(textbox);
            }
            catch (Exception eee)
            {

            }

        }

    }
}

三、多说一点:
(1)文件流方式:
C#中通常有三种方法获取网页内容。第一种方式为:使用webclient、第二种方式为:webBrowser、第三种方式为HttpWebRequest/HttpWebResponse
。此次程序设计中选用的是第三种方式即HttpWebResquest/HttpWebResponse方式。这是一种比较通用的获取方式。
(2)c#中的多线程:
在Visul C#中System.Threading 命名空间提供一些使得可以进行多线程编程的类和接口,其中线程的创建有以下三种方法:Thread、ThreadPool、Timer。
本次课程设计中选用的是Thread方式。这也许是最复杂的方法,但它提供了对线程的各种灵活控制。首先你必须使用它的构造函数创建一个线程实例,它的参数比较简单,只有一个ThreadStart 委托:
public Thread(ThreadStart start);
然后调用Start()启动它。
(3)正则表达式:
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
在此次课程设计中,使用了正则表达式对网页的源码进行截取和分析,以获得想要的数据信息。
程序流程详解:
(1)程序使用文件流方式从首页hyzbi.com中下载整个页面的源代码。
(2)通过使用正则表达式,将网站建设类目下的网页URL截取下来,并保存在一个队列中。
(3)启用多线程,每个线程中从队列中拿取一个URL。此时此时使用了互斥锁,避免线程冲突。线程每拿取一个URL就将队列中的URL记录删除,避免重复下载。线程中同样运用文件流的方式下载网页URL的源代码。并封装了两个函数,分别对应图片和文本的下载分析。
当线程工作完成时,将完成时间以及完成项目显示在操作界面上。
(4)对于图面的解析,使用正则表达式截取网页URL中所有的图片URL。把图片URL保存在数组中,美获得一个URL就和数组中的项比较,有重复项就舍弃,无重复项就下载,这样就做到了图片的冗余,避免下载重复的图片。
(5)对于文本的解析,则使用了正则表达式的截取和替代。在文本的保存上,也加了一个互斥锁,使同一时刻只有一个线程可以访问本地文件保存文本。
四、程序运行结果:
1、主程序界面
这里写图片描述
2、爬取的网站中的图片信息:
这里写图片描述
3、爬取的网站中的文本信息:
这里写图片描述

四、更多了解:
完整的代码及设计说明我已上至资源,手戳这里是链接儿
【注】程序中指定了爬取的网站网址
不知为什么贴的代码,移动端看,csdn的编辑器会出乱码???
我看了一下,电脑上看,显示的代码是正确的,最好把代码从我的资源中下下来看,没有问题。

描述:由C#编写的多线程异步抓取网页的网络爬虫控制台程序 功能:目前只能提取网络链接,所用的两个记录文件并不需要很大。网页文本、图片、视频和html代码暂时不能抓取,请见谅。 但需要注意,网页的数目是非常庞大的,如下代码理论上大概可以把整个互联网网页链接都抓下来。 但事实上,由于处理器功能和网络条件(主要是网速)限制,一般的家用电脑最多能胜任12个线程左右的抓取任务,抓取速度有限。可以抓取,但需要时间和耐心。 当然,这个程序把所有链接抓下来是可能的,因为链接占系统空间并不多,而且有记录文件的帮助,已抓取网页的数量可以堆积下去, 甚至可以把所有的互联网网络链接都存取下来,当然,最好是分批次。建议设置maxNum为500-1000左右,慢慢累积下去。 另外因为是控制台程序,有时候显示字符过多会系统会暂停显示,这时候只要点击控制台按下回车键就可以了。程序假死的时候,可以按回车键(Enter)试试。 /// 使用本程序,请确保已创建相应的记录文件,出于简化代码的考虑,本程序做的并不健壮,请见谅。 /// 默认的文件创建在E盘根目录“已抓取网址.txt”和“待抓取网址.txt”这两个文本文件中,使用者需要自行创建这两个文件,注意后缀名不要搞错。 这两个文件里面的链接基本都是有效链接,可以单独处理使用。 本爬虫程序的速度如下: 10线程最快大概500个链接每分钟 6-8线程最快大概400-500个链接每分钟 2-4线程最快大概200-400个链接每分钟 单线程最快大概70-100个链接每分钟 之所以用多线程异步抓取完全是出于效率考虑,本程序多线程同步并不能带来速度的提升,只要抓取的网页不要太多重复和冗余就可以,异步并不意味着错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值