三、将新闻分类进行测试应用及展示

本项目利用WebMagic爬取测试网页保存到本地。之后去除网页源码的html标签并用IKAnalyzer进行中文分词,完成文本的预处理。再利用编写的java代码生成测试集,最后调用贝叶斯算法完成对网页的分类。

朴素贝叶斯分类的原理与流程

朴素贝叶斯分类是一种十分简单的分类算法,叫它朴素贝叶斯分类是因为这种方法的思想真的很朴素,朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。通俗来说,就好比这么个道理,你在街上看到一个黑人,我问你你猜这哥们哪里来的,你十有八九猜非洲。为什么呢?因为黑人中非洲人的比率最高,当然人家也可能是美洲人或亚洲人,但在没有其它可用信息下,我们会选择条件概率最大的类别,这就是朴素贝叶斯的思想基础。

朴素贝叶斯分类的正式定义如下:

那么现在的关键就是如何计算第3步中的各个条件概率。我们可以这么做:

根据上述分析,朴素贝叶斯分类的流程可以由下图表示(暂时不考虑验证):

需求描述

输入某个网页的网址,通过对该网页的的爬取,完成对网页的清洗和分类两个需求。项目初步设定网页可以分的类别有10种,分别是体育、军事、财经、娱乐、文化、健康、汽车、教育、房产和其它。

需求描述

输入某个网页的网址,通过对该网页的的爬取,完成对网页的清洗和分类两个需求。项目初步设定网页可以分的类别有10种,分别是体育、军事、财经、娱乐、文化、健康、汽车、教育、房产和其它。

实验环境

Linux Ubuntu 16.04

Eclipse 4.2

Apache Tomcat v7.0

Google Chrome

结果展示与说明

将要分类的网页的网址输入如图所示的网页URL对应的文本框中,然后点击爬取,可以把该网页的源码爬取下来并显示在如图所示的文本域中。然后可以根据自己的需求对网页进行清洗或者分类,如果需要清洗网页,可以点击如图所示中的清洗按钮,会将清洗结果显示在文本域中;如果需要对网页分类,则点击如图所示的网页分类按钮,稍等片刻,仪表盘便会指出该 网页所属的类别。

项目框架简介

1.项目框架结构如下图所示

2.框架说明

框架解释说明:

(1)src目录下是项目主要的后台部分,用于对页面中操作请求的实现。

(2)WebContent下show.jsp文件为该项目的展示页面,js目录存放的是show.jsp页面中需要的js文件,WEB-INF中的lib下存放的是项目需要的jar包和为servlet定制url的web.xml文件。

后台工作原理说明:

(1)客户端向后台发送请求。在jsp页面根据对数据的要求设置需要调用的servlet文件名称,这时请求会先到达web.xml文件。

(2)web.xml文件里面设置了各个servlet文件的路径,所以客户端发送过来的请求会通过web.xml,提交到相应的如图(工作原理图)servlet层下的servlet类。

(3)当成功提交到这个servlet类时,servlet类会调用如图(工作原理图)dao层(或者调用nb层、webmagic层)中相应的类里面的方法将获取的数据返回

(4)dao层为接口类,接口中方法的实现在impl层如图(工作原理图),impl目录下的类方法与本地数据进行相应的读写等操作,从而获得数据。

工作原理图所示:

文本下载与项目运行

1.打开终端模拟器,切换到data目录下,下载该项目

cd /data
wget http://10.51.46.104:32600/allfiles/classify/PageClassify.tar.gz

2.解压PageClassify.tar.gz包到当前目录

tar zxvf PageClassify.tar.gz

3.在/data目录下创建classifydata文件夹,在该文件夹下,下载网页分类所需的训练集。

cd /data
mkdir classifydata
cd classifydata
wget http://10.51.46.104:32600/allfiles/classify/trainSpecial.tar.gz

4.解压trainSpecial.tar.gz包到当前目录

tar zxvf trainSpecial.tar.gz

5.打开Eclipse,导入PageClassify项目。

6.将项目在tomcat服务器上运行。

选择Window=>Show View,选择Servers

在Server处右键选择Add and Remove

将PageClassify项目添加到Configured中后,点击Finish

右键Server选择Start,开启Tomcat

7.打开浏览器输入以下网址,便可以开始对网页进行分类了

localhost:8080/PageClassify/show.jsp

详解重要代码

1.Gethtmlcode类,用于对测试网页的爬取,主要代码如下:

检测/data/classifydata/目录下是否存在testdata文件夹,如果没有创建该文件夹,用于存储爬取下来的测试网页(注:此文件夹下只存放测试文本,切勿随意存放其它文件夹和文件),将从InputurlServlet类中传来的网页数据命名为crawlPage.html并保存在该文件夹下。

public class Gethtmlcode implements PageProcessor {

0
	private Site site = Site.me().setRetryTimes(3).setSleepTime(100);

	public void process(Page page) {
		try {
			String allhtml = page.getHtml().toString();
			File filedir = new File("/data/classifydata/testdata/");
			if (!filedir.exists()) {
				filedir.mkdir();
			}
			File file = new File("/data/classifydata/testdata/crawlPage.html");
			if (!file.exists()) {
				file.createNewFile();
			}

			FileWriter fw = new FileWriter(file.getAbsoluteFile());
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(allhtml);
			bw.close();
		} catch (IOException e) {

			e.printStackTrace();
		}
	}

	public Site getSite() {
		return site;
	}

}

2.网页清洗功能CleanoutDaoImpl类中,主要代码如下:

按行读取网页源码文件crawlPage.html,并去除每行中的a标签和网页中各种标签、属性,然后将不属于中文的内容(针对script等部分源码),清洗完成后将结果返回给show.jsp页面。

public  StringBuilder cleancode() throws FileNotFoundException {
        BufferedReader br = null;
        FileReader fileReader = null;
        StringBuilder htmlcode =null;
        File file=new File("/data/classifydata/testdata/crawlPage.html");
        fileReader = new FileReader(file);
        try {
        	StringBuilder sb = new StringBuilder();
            String line = "";

            br = new BufferedReader(fileReader);

    		while ((line = br.readLine()) != null) {
				line = line.replaceAll("<a(.*?)</a>.", "").replaceAll(
						"</?[^>]+>", "").replaceAll("[^\\u4e00-\\u9fa5]", "");
        htmlcode=sb.append(line);

        }

        } catch (Exception e) {
        e.printStackTrace();
        } finally {
        try {
        br.close();
        fileReader.close();
        } catch (IOException e) {
        e.printStackTrace();
        }
        }
        return htmlcode;
        }

3.文本预处理DataPreProcess类中,主要代码如下:

该部分功能为去除网页各种标签以及标签中的中文,然后进行中文分词,并通过IKAnalyzer.cfg.xml加载chinese_stopword.dic停用词文件,去掉停用词,完成文本的预处理。详细说明可以参照代码注释

public static void createProcessFile(String fileFullName, String toFile) {
		BufferedWriter bw = null;
		BufferedReader br = null;
		FileWriter fileWriter = null;
		FileReader fileReader = null;
		try {
			String line, resLine = null;

			StringBuilder sb = new StringBuilder();

			fileReader = new FileReader(fileFullName);
			br = new BufferedReader(fileReader);
			// 去掉a标签和html标签
			while ((line = br.readLine()) != null) {
				String conts = "";
				line = line.replaceAll("<a(.*?)>/a>.", "")
						.replaceAll("</?[^>]+>", "")
        .replaceAll("[^\\u4e00-\\u9fa5]", "");
        conts += line;

        // 开始进行中文分词,用/n分割

        if (!conts.isEmpty()) {
        // lucene--中文分词IKAnalyzer
        IKAnalyzer analyzer = new IKAnalyzer(true);
        // 通过分析器Analyzer将一个字符串创建成Taken流,第一个参数是一个名字没有实际作用
        TokenStream tokenStream = analyzer.tokenStream("content",
        new StringReader(conts));
        while (tokenStream.incrementToken()) {
        // 保存相应词汇
        CharTermAttribute charTermAttribute = tokenStream
        .getAttribute(CharTermAttribute.class);
        sb.append(charTermAttribute.toString());
        sb.append("\n");
        }
        resLine = sb.toString();
        analyzer.close();

        }

        }
        fileWriter = new FileWriter(toFile);
        bw = new BufferedWriter(fileWriter);
        bw.write(resLine);
        } catch (Exception e) {
        e.printStackTrace();
        } finally {
        try {
        bw.close();
        br.close();
        fileWriter.close();
        fileReader.close();
        } catch (IOException e) {
        e.printStackTrace();
        }
        }
        }

4.ComputeWordsVector.java,该类将testdata下的预处理文本解析,生成只含有特征词的文本。

统计每个词的总的出现次数,返回出现次数大于3的词语。

public SortedMap<String, Double> countWords(String strDir,
			Map<String, Double> wordMap) throws IOException {
		File sampleFile = new File(strDir);
		File[] sample = sampleFile.listFiles();
		String word;
		for (int i = 0; i < sample.length; i++) {
			if (!sample[i].isDirectory()) {
				FileReader samReader = new FileReader(sample[i]);
				BufferedReader samBR = new BufferedReader(samReader);
				while ((word = samBR.readLine()) != null) {
					if (!word.isEmpty() && wordMap.containsKey(word)) {
						double count = wordMap.get(word) + 1;
						wordMap.put(word, count);
					} else {
						wordMap.put(word, 1.0);
					}
				}
			} else
				countWords(sample[i].getCanonicalPath(), wordMap);
		}
		// 只返回出现次数大于3的词语
		SortedMap<String, Double> newWordMap = new TreeMap<String, Double>();
		Set<Map.Entry<String, Double>> allWords = wordMap.entrySet();
		for (Iterator<Map.Entry<String, Double>> it = allWords.iterator(); it
				.hasNext();) {
			Map.Entry<String, Double> me = it.next();
			if (me.getValue() >= 3) {
				newWordMap.put(me.getKey(), me.getValue());
			}
		}
		return newWordMap;
	}

5.CreateTrainAndTestSample.java类,用于生成测试集。

调用ComputeWordsVector 中的countWords方法,根据testdata中包含非特征词的文档集生成只包含特征词的文档集保存到testSpecial目录下(注:此文件夹下只存放该项目生成的文本集,切勿随意存放其它文件夹和文件)

ComputeWordsVector cwv = new ComputeWordsVector();
	void testSpecialWords() throws IOException {
		// TODO Auto-generated method stub
		String word;
		String fileDir = "/data/classifydata/testdata/";
		SortedMap<String, Double> wordMap = new TreeMap<String, Double>();
		wordMap = cwv.countWords(fileDir, wordMap);
		File[] sampleDir = new File(fileDir).listFiles();
		for (int i = 0; i < sampleDir.length; i++) {
			String fileShortName = sampleDir[i].getName();
			String targetDir = "/data/classifydata/testSpecial/"
					+ fileShortName;
			FileWriter tgWriter = new FileWriter(targetDir);
			FileReader samReader = new FileReader(sampleDir[i]);
			BufferedReader samBR = new BufferedReader(samReader);
			while ((word = samBR.readLine()) != null) {
				if (wordMap.containsKey(word)) {
					tgWriter.append(word + "\n");
				}
			}
			tgWriter.flush();
			tgWriter.close();
		}

	}

6.NaiveBayesianClassifier.java.java,编写贝叶斯算法,实现对网页的分类

统计某类训练样本中每个词语的出现的次数

public Map<String, Double> getCateWordsProb(String strDir)
			throws IOException {
		// cateWordsProb
		Map<String, Double> cateWordsProb = new TreeMap<String, Double>();
		File sampleFile = new File(strDir);
		File[] sampleDir = sampleFile.listFiles();
		String word;
		for (int i = 0; i < sampleDir.length; i++) {
			File[] sample = sampleDir[i].listFiles();
			for (int j = 0; j < sample.length; j++) {
				FileReader samReader = new FileReader(sample[j]);
				BufferedReader samBR = new BufferedReader(samReader);
				while ((word = samBR.readLine()) != null) {
					String key = sampleDir[i].getName() + "_" + word;
					if (cateWordsProb.containsKey(key)) {
						double count = cateWordsProb.get(key) + 1.0;
						cateWordsProb.put(key, count);
					} else {
						cateWordsProb.put(key, 1.0);
					}
				}
			}
		}
		return cateWordsProb;
	}

获得每个类目下的单词总数,其中trainDir为程序定义的训练文档集目录

private Map<String, Double> getCateWordsNum(String trainDir)
			throws IOException {
		// TODO Auto-generated method stub
		Map<String, Double> cateWordsNum = new TreeMap<String, Double>();// <目录名,单词总数>
		File[] sampleDir = new File(trainDir).listFiles();
		for (int i = 0; i < sampleDir.length; i++) {
			double count = 0;
			File[] sample = sampleDir[i].listFiles();
			for (int j = 0; j < sample.length; j++) {
				FileReader spReader = new FileReader(sample[j]);
				BufferedReader spBR = new BufferedReader(spReader);
				while (spBR.readLine() != null) {
					count++;
				}
			}
			cateWordsNum.put(sampleDir[i].getName(), count);
		}
		return cateWordsNum;
	}

计算某一个测试样本分别属于某个类别的概率,详细说明见下面代码中注释

/**
	 * 计算某一个测试样本属于某个类别的概率
	 *
	 * @param Map
	 *            <String, Double> cateWordsProb 记录每个目录中出现的词及次数
	 * @param File
	 *            trainFile 该类别所有的训练样本所在目录
	 * @param Vector
	 *            <String> testFileWords 该测试样本中的所有词构成的容器
    * @param double totalWordsNum 记录所有训练样本的单词总数
    * @param Map
    *            <String, Double> cateWordsNum 记录每个类别的单词总数
    * @return BigDecimal 返回该测试样本在该类别中的概率
    * @throws Exception
    * @throws IOException
    *             BigDecimal:http://blog.csdn.net/lisongjia123/article/details/
    *             51232657
    */
    private BigDecimal computeCateProb(File trainFile,
    Vector<String> testFileWords, Map<String, Double> cateWordsNum,
        double totalWordsNum, Map<String, Double> cateWordsProb)
        throws Exception {
        // TODO Auto-generated method stub
        BigDecimal probability = new BigDecimal(1);
        double wordNumInCate = cateWordsNum.get(trainFile.getName());
        BigDecimal wordNumInCateBD = new BigDecimal(wordNumInCate);
        BigDecimal totalWordsNumBD = new BigDecimal(totalWordsNum);
        for (Iterator<String> it = testFileWords.iterator(); it.hasNext();) {
            String onewordSpecial = it.next();
            String key = trainFile.getName() + "_" + onewordSpecial;
            double testFileWordNumInCate;
            if (cateWordsProb.containsKey(key)) {
            testFileWordNumInCate = cateWordsProb.get(key);
            // System.out.println("testFileWordNumInCate-----"+testFileWordNumInCate);
            } else
            testFileWordNumInCate = 0.0;
            BigDecimal testFileWordNumInCateBD = new BigDecimal(
            testFileWordNumInCate);
            // ROUND_CEILING舍向正无穷
            // xcProb=每个目录中出现的词的次数/(所有训练样本的单词总数+每个类别的词总数)
            BigDecimal xcProb = (testFileWordNumInCateBD.add(new BigDecimal(
            0.0001))).divide(totalWordsNumBD.add(wordNumInCateBD), 10,
            BigDecimal.ROUND_CEILING);// 10除法运算结果保留10位小数
            // multiply作用乘积 probability*xcProb
            probability = probability.multiply(xcProb);
            }

            // divide作用除法 wordNumInCateBD/totalWordsNumBD
            // probability*记录每个类别的词总数/所有训练样本的单词总数
            BigDecimal res = probability.multiply(wordNumInCateBD.divide(
            totalWordsNumBD, 10, BigDecimal.ROUND_CEILING));

            return res;
            }

使用朴素贝叶斯对文档集分类,其中为 trainDir为程序定义的训练集目录, testDir为程序定义的测试集目录,详细说明见下面代码中注释

public  String doProcess(String trainDir, String testDir,
			String classifyResultFileNew) throws Exception {
		String bestCateout = null;
		// TODO Auto-generated method stub
		// 保存训练集每个列别的总词语数
		Map<String, Double> cateWordsNum = new TreeMap<String, Double>();
		// 保存训练样本每个类别中每个属性词的出现数量
		Map<String, Double> cateWordsProb = new TreeMap<String, Double>();
		cateWordsProb = getCateWordsProb(trainDir);
		// getCateWordsNum获得每个类目下的单词总数,trainDir训练文档集目录
		cateWordsNum = getCateWordsNum(trainDir);
		// 记录所有训练集的总的词数
		double totalWordsNum = 0.0;
		Set<Map.Entry<String, Double>> cateWordsNumSet = cateWordsNum
				.entrySet();
		for (Iterator<Map.Entry<String, Double>> it = cateWordsNumSet
				.iterator(); it.hasNext();) {
			Map.Entry<String, Double> me = it.next();
			totalWordsNum += me.getValue();
		}
		// 下面开始读取测试样例做分类
		Vector<String> testFileWords = new Vector<String>();
    String word;
    File[] testSample = new File(testDir).listFiles();
    FileWriter crWriter = new FileWriter(classifyResultFileNew);
    for (int j = 0; j < testSample.length; j++) {
    testFileWords.clear();
    FileReader spReader = new FileReader(testSample[j]);
    BufferedReader spBR = new BufferedReader(spReader);
    while ((word = spBR.readLine()) != null) {
    testFileWords.add(word);
    }
    // 下面分别计算测试样例属于10个类别的概率
    File[] trainDirFiles = new File(trainDir).listFiles();
    BigDecimal maxP = new BigDecimal(0);
    String bestCate = null;
    for (int k = 0; k < trainDirFiles.length; k++) {
    // computeCateProb计算某一个测试样本属于某个类别的概率
    BigDecimal p = computeCateProb(trainDirFiles[k], testFileWords,
    cateWordsNum, totalWordsNum, cateWordsProb);
    BigDecimal itp = p;
    if (k == 0) {
    maxP = p;
    bestCate = trainDirFiles[k].getName();
    continue;
    }
    // 即左边比右边数大,返回1,相等返回0,比右边小返回-1。注意 不可用equals进行相等的判断,equals
    if (p.compareTo(maxP) == 1) {
    maxP = p;
    bestCate = trainDirFiles[k].getName();
    }
    }
    bestCateout=bestCate;
    crWriter.append(testSample[j].getName() + " " + bestCate + "\n");
    crWriter.flush();
    }

    crWriter.close();
    return bestCateout;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值