数据采集

根据实验17

得到对链接:https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98&productId=4432058&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0&fold=1 的评论的爬取结果。

查看爬取结果

  1. [hfut@master ~]$ cd tmp
  2. [hfut@master tmp]$ ls

综合设计报告详细 - 图1

可见已经正确爬取数据,对其中一个文件进行查看

  1. [hfut@master tmp]$ cat 1293744-page70.html

综合设计报告详细 - 图2

可见数据需要清洗处理。

综合设计报告详细 - 图3

  1. package my.webcollector;
  2. import java.io.*;
  3. import okhttp3.Request;
  4. import cn.edu.hfut.dmic.webcollector.model.CrawlDatum;
  5. import cn.edu.hfut.dmic.webcollector.model.CrawlDatums;
  6. import cn.edu.hfut.dmic.webcollector.model.Page;
  7. import cn.edu.hfut.dmic.webcollector.plugin.berkeley.BreadthCrawler;
  8. import cn.edu.hfut.dmic.webcollector.plugin.net.OkHttpRequester;
  9. public class JDCommentCrawler extends BreadthCrawler {
  10. public JDCommentCrawler(String crawlPath) {
  11. // 第二个参数表示不需要自动探测URL
  12. super(crawlPath, false);
  13. // 设置线程数为1
  14. setThreads(1);
  15. // 添加种子(评论API对应的URL,这里翻页10次)
  16. for (int pageIndex = 0; pageIndex < 100; pageIndex++) {
  17. String seedUrl = String
  18. .format("https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98&productId=1293744&score=0&sortType=5&page=%d&pageSize=10&isShadowSku=0&fold=1",
  19. pageIndex);
  20. // 在添加种子的同时,记录对应的页号
  21. addSeedAndReturn(seedUrl).meta("pageIndex", pageIndex);
  22. }
  23. }
  24. @Override
  25. public void visit(Page page, CrawlDatums crawlDatums) {
  26. // 模拟人访问网页的速度
  27. try {
  28. Thread.sleep(3000);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. // 获取之前保存的页号信息
  33. int pageIndex = page.metaAsInt("pageIndex");
  34. String body = page.html();
  35. // 保存当前访问的productPageComments页面信息
  36. JDCommentCrawler.createFile(body, "/home/hfut/tmp/1293744-page"
  37. + pageIndex + ".html");
  38. }
  39. /**
  40. * 将字符串保存到文件
  41. */
  42. public static boolean createFile(String content, String filePath) {
  43. // 标记文件生成是否成功
  44. boolean flag = true;
  45. try {
  46. // 保证创建一个新文件
  47. File file = new File(filePath);
  48. if (!file.getParentFile().exists()) { // 如果父目录不存在,创建父目录
  49. file.getParentFile().mkdirs();
  50. }
  51. if (file.exists()) { // 如果已存在,删除旧文件
  52. file.delete();
  53. }
  54. file.createNewFile();
  55. // 将格式化后的字符串写入文件
  56. Writer write = new OutputStreamWriter(new FileOutputStream(file),
  57. "UTF-8");
  58. write.write(content);
  59. write.flush();
  60. write.close();
  61. } catch (Exception e) {
  62. flag = false;
  63. e.printStackTrace();
  64. }
  65. return flag;
  66. }
  67. /**
  68. * 模拟普通用户使用浏览器访问
  69. */
  70. public static class MyRequester extends OkHttpRequester {
  71. String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0";
  72. // 每次发送请求前都会执行这个方法来构建请求
  73. @Override
  74. public Request.Builder createRequestBuilder(CrawlDatum crawlDatum) {
  75. // 这里使用的是OkHttp中的Request.Builder
  76. // 可以参考OkHttp的文档来修改请求头
  77. return super.createRequestBuilder(crawlDatum)
  78. .removeHeader("User-Agent") //移除默认的UserAgent
  79. .addHeader("Referer", "https://item.jd.com/")
  80. .addHeader("User-Agent", userAgent);
  81. }
  82. }
  83. public static void main(String[] args) throws Exception {
  84. // 实例化一个评论爬虫,并设置临时文件夹为crawl
  85. JDCommentCrawler crawler = new JDCommentCrawler("crawl");
  86. // 抓取1层
  87. crawler.start(1);
  88. }
  89. }

综合设计报告详细 - 图4

comments中一条评论的结构:

  1. "id": 11079346347,
  2. "topped": 0,
  3. "guid": "e6cf6af4-7872-438f-a05a-7c08fc45a99d",
  4. "content": "发货很迅速,第二天下午就很快收到了。手机用着还好,中规中矩,国货中性价比是不错了。很喜欢。外壳膜配件不是很好配,毕竟小众。希望能够用得久些。期待中。电池个人感觉一般。其他暂时感觉正常,是一款性价比较高的手机。", //评论内容 √
  5. "creationTime": "2017-12-17 02:25:02", //写评论的时间 √
  6. "isTop": false, //是否置顶
  7. "referenceId": "4432058",
  8. "referenceImage": "jfs/t5527/223/1660932474/149818/343ed1d7/59130e4cNa6d07fe0.jpg",
  9. "referenceName": "锤子 坚果Pro 128GB 细红线特别版 全网通 移动联通电信4G手机 双卡双待",
  10. "referenceTime": "2017-12-07 04:17:50", //收货时间 √
  11. "referenceType": "Product",
  12. "referenceTypeId": 0,
  13. "firstCategory": 9987, //第一分类 √
  14. "secondCategory": 653, //第二分类 √
  15. "thirdCategory": 655, //第三分类 √
  16. "replies": [ ],
  17. "replyCount": 15,
  18. "replyCount2": 0,
  19. "score": 5, //打分 √
  20. "status": 1,
  21. "title": "",
  22. "usefulVoteCount": 48,
  23. "uselessVoteCount": 0,
  24. "userImage": "storage.360buyimg.com/i.imageUpload/776471664757787153445a68535731343232373835363632353035_sma.jpg",
  25. "userImageUrl": "storage.360buyimg.com/i.imageUpload/776471664757787153445a68535731343232373835363632353035_sma.jpg",
  26. "userLevelId": "105",
  27. "userProvince": "",
  28. "viewCount": 0,
  29. "orderId": 0,
  30. "isReplyGrade": false,
  31. "nickname": "九***女", //昵称 √
  32. "userClient": 4,
  33. "images": [],
  34. "videos": [],
  35. "showOrderComment": {},
  36. "mergeOrderStatus": 2,
  37. "discussionId": 302057364,
  38. "productColor": "细红线特别版",
  39. "productSize": "128GB",
  40. "imageCount": 8,
  41. "integral": -20,
  42. "userImgFlag": 1,
  43. "anonymousFlag": 1,
  44. "userLevelName": "钻石会员", //会员级别 √
  45. "plusAvailable": 103,
  46. "productSales": [],
  47. "mobileVersion": "",
  48. "userLevelColor": "#666666",
  49. "recommend": true,
  50. "userClientShow": "来自京东Android客户端", //评论设备
  51. "isMobile": true, //是否移动端
  52. "days": 10, //评论时间距【收货/下单】时间多长时间
  53. "afterDays": 0

数据清洗

根据实验18-数据清洗,可将爬取的数据清晰,获取有用的数据-评价。

数据清洗是对数据进行重新审查和校验的过程,目的在于删除重复信息、纠正存在的错误,并提供数据一致性。

数据清洗从名字上也看的出就是把“脏”的“洗掉”,指发现并纠正数据文件中可识别的错误的最后一道程序,包括检查数据一致性,处理无效值和缺失值等。因为数据仓库中的数据是面向某一主题的数据的集合,这些数据从多个业务系统中抽取而来而且包含历史数据,这样就避免不了有的数据是错误数据、有的数据相互之间有冲突,这些错误的或有冲突的数据显然是我们不想要的,称为“脏数据”。我们要按照一定的规则把“脏数据”“洗掉”,这就是数据清洗。而数据清洗的任务是过滤那些不符合要求的数据,将过滤的结果交给业务主管部门,确认是否过滤掉还是由业务单位修正之后再进行抽取。不符合要求的数据主要是有不完整的数据、错误的数据、重复的数据三大类。数据清洗是与问卷审核不同,录入后的数据清洗一般是由计算机而不是人工完成。

综合设计报告详细 - 图5

为了之后的分词的需要,数据清洗结果只保留评论结果,故将数据清洗的代码修改如下。

  1. import java.io.IOException;
  2. import org.apache.hadoop.fs.Path;
  3. import org.apache.hadoop.io.Text;
  4. import org.apache.hadoop.mapreduce.Job;
  5. import org.apache.hadoop.mapreduce.Mapper;
  6. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  7. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  8. import com.alibaba.fastjson.JSONArray;
  9. import com.alibaba.fastjson.JSONObject;
  10. public class MRDataClean4JDCommets2 {
  11. public static void main(String[] args) throws IOException,
  12. ClassNotFoundException, InterruptedException {
  13. Job job = Job.getInstance();
  14. job.setJobName("MRDataClean4JDCommets");
  15. job.setJarByClass(MRDataClean4JDCommets.class);
  16. job.setMapperClass(doMapper.class);
  17. // job.setReducerClass(doReducer.class);
  18. job.setOutputKeyClass(Text.class);
  19. job.setOutputValueClass(Text.class);
  20. Path in = new Path("hdfs://master:9000/MRDataClean/in");
  21. Path out = new Path("hdfs://master:9000/MRDataClean/out/3");
  22. FileInputFormat.addInputPath(job, in);
  23. FileOutputFormat.setOutputPath(job, out);
  24. System.exit(job.waitForCompletion(true) ? 0 : 1);
  25. }
  26. public static class doMapper extends Mapper<Object, Text, Text, Text> {
  27. @Override
  28. protected void map(Object key, Text value, Context context)
  29. throws IOException, InterruptedException {
  30. String initJsonString = value.toString();
  31. JSONObject initJson = JSONObject.parseObject(initJsonString );
  32. if (!initJsonString.contains("productCommentSummary") && !initJsonString.contains("comments")) {
  33. return;
  34. }
  35. JSONObject myjson = initJson.getJSONObject("ten");
  36. /* comments 包括十条评论 */
  37. JSONArray comments = myjson.getJSONArray("comments");
  38. for (int i = 0; i < comments.size(); i++) {
  39. JSONObject comment = comments.getJSONObject(i);
  40. // String guid = comment.getString("guid");
  41. String content = comment.getString("content").replace('\n', ' ');
  42. StringBuilder sb = new StringBuilder();
  43. sb.append( content ); sb.append("\t");
  44. String result = sb.toString();
  45. context.write(new Text(result), new Text(""));
  46. }
  47. }
  48. }
  49. }

查看清洗后的数据

创建/DataClean/目录并切换目录到/DataClean/下。在命令行界面,输入脚本,查看hdfs上/MRDataClean/out是否有内容输出

  1. [hfut@master ~]$ mkdir /home/hfut/DataClean
  2. [hfut@master ~]$ cd /home/hfut/DataClean
  3. [hfut@master DataClean]$ hadoop fs -lsr /MRDataClean/out

将hdfs输出内容,下载到linux本地

  1. [hfut@master DataClean]$ hadoop fs -get /MRDataClean/out/2/*

可以查看数据清洗后只剩下评论,可以作为下一步分词中的输入。

综合设计报告详细 - 图6

分词

将获得的评论按行分词输出到一个文件中,为下一步的统计词频做铺垫。

根据实验19-文本分词为基础进行改进。

通过数据的清洗,我们可以得到结构比较清晰的文档,这一步需要对文本进行分词。

中文分词指的是将一个汉字序列切分成一个一个单独的词。分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。我们知道,在英文的行文中,单词之间是以空格作为自然分界符的,而中文只是字、句和段能通过明显的分界符来简单划界,唯独词没有一个形式上的分界符,虽然英文也同样存在短语的划分问题,不过在词这一层上,中文比之英文要复杂的多、困难的多。

使用IKAnalyzer进行分词,由于实验19中已经为了便于在MapReduce、Spark中调用分词,将分词的方法序列化,可以直接修改IKAnalyzerTest2 .java,按行读取进行分词。

具体的修改为:

  1. 修改输入输出路径
    1. FileSystem fs = FileSystem.get(conf);
    2. Path file = new Path("/MRDataClean/out/3/part-r-00000");//input file
    3. FSDataInputStream getIt = fs.open(file);
    4. BufferedReader d = new BufferedReader(new InputStreamReader(getIt));
    5. // String content = d.readLine(); // 读取文件一行
    6. // System.out.println(content);
    7. String str = null;
    8. String filename = "/MRDataClean/out/3/test.txt";
    9. FSDataOutputStream os = fs.create(new Path(filename));
  1. 修改读取方式,按行读取

    1. while((str = d.readLine()) != null)
    2. {
    3. String resulte = IKAnalyzerTest2.splitWords(str);
    4. System.out.println(str);
    5. try {
    6. // Configuration conf = new Configuration();
    7. // conf.set("fs.defaultFS", "hdfs://master:9000");
    8. // conf.set("fs.hdfs.impl",
    9. // "org.apache.hadoop.hdfs.DistributedFileSystem");
    10. // FileSystem fs = FileSystem.get(conf);
    11. byte[] buff = resulte.getBytes();
    12. // String filename = "/MRDataClean/out/3/test.txt";
    13. // FSDataOutputStream os = fs.create(new Path(filename));
    14. os.write(buff, 0, buff.length);
    15. // System.out.println("Create:" + filename);
    16. // os.close();
    17. // fs.close();
    18. } catch (Exception e) {
    19. e.printStackTrace();
    20. }
    21. }
  1. 修改输出格式,换行输出
    1. while (tokenStream.incrementToken()) {
    2. CharTermAttribute charTermAttribute = tokenStream.getAttribute(CharTermAttribute.class);
    3. //System.out.print(charTermAttribute.toString() + "|");
    4. sb.append(charTermAttribute.toString());
    5. sb.append("\n");
    6. }

    修改完成后:
  1. private static final long serialVersionUID = 1L;
  2. public static void main(String[] args) throws IOException {
  3. try {
  4. Configuration conf = new Configuration();
  5. conf.set("fs.defaultFS", "hdfs://master:9000");
  6. conf.set("fs.hdfs.impl",
  7. "org.apache.hadoop.hdfs.DistributedFileSystem");
  8. FileSystem fs = FileSystem.get(conf);
  9. Path file = new Path("/MRDataClean/out/3/part-r-00000");//input file
  10. FSDataInputStream getIt = fs.open(file);
  11. BufferedReader d = new BufferedReader(new InputStreamReader(getIt));
  12. // String content = d.readLine(); // 读取文件一行
  13. // System.out.println(content);
  14. String str = null;
  15. String filename = "/MRDataClean/out/3/test.txt";
  16. FSDataOutputStream os = fs.create(new Path(filename));
  17. while((str = d.readLine()) != null)
  18. {
  19. String resulte = IKAnalyzerTest2.splitWords(str);
  20. System.out.println(str);
  21. try {
  22. byte[] buff = resulte.getBytes();
  23. // String filename = "/MRDataClean/out/3/test.txt";
  24. // FSDataOutputStream os = fs.create(new Path(filename));
  25. os.write(buff, 0, buff.length);
  26. } catch (Exception e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. os.close();
  31. d.close(); // 关闭文件
  32. fs.close(); // 关闭hdfs
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. //这是分词的接口,line输入,然后
  37. public static String splitWords(String line){
  38. String result = "";
  39. IKAnalyzer analyzer = new IKAnalyzer(true);
  40. try {
  41. TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(line));
  42. tokenStream.addAttribute(CharTermAttribute.class);
  43. StringBuilder sb = new StringBuilder();
  44. while (tokenStream.incrementToken()) {
  45. CharTermAttribute charTermAttribute = tokenStream.getAttribute(CharTermAttribute.class);
  46. //System.out.print(charTermAttribute.toString() + "|");
  47. sb.append(charTermAttribute.toString());
  48. sb.append("\n");
  49. }
  50. result = sb.toString();
  51. } catch (Exception e) {
  52. System.err.println(e.getMessage());
  53. }
  54. analyzer.close();
  55. return result;
  56. }
  57. }

查看结果:

综合设计报告详细 - 图7

统计词频

根据上一步得到的分词后的结果,对其进行统计词频。

修改之前实验wordcount中的输入和输出地址。

修改为:

  1. hdfs://master:9000/MRDataClean/out/3/test.txt hdfs://master:9000/MRDataClean/out/4

综合设计报告详细 - 图8

编写代码:

  1. import java.io.IOException;
  2. import java.util.Iterator;
  3. import java.util.StringTokenizer;
  4. import org.apache.hadoop.conf.Configuration;
  5. import org.apache.hadoop.fs.Path;
  6. import org.apache.hadoop.io.IntWritable;
  7. import org.apache.hadoop.io.Text;
  8. import org.apache.hadoop.mapreduce.Job;
  9. import org.apache.hadoop.mapreduce.Mapper;
  10. import org.apache.hadoop.mapreduce.Reducer;
  11. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  12. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  13. import org.apache.hadoop.util.GenericOptionsParser;
  14. public class WordCountTest {
  15. public WordCountTest() {
  16. }
  17. public static void main(String[] args) throws Exception {
  18. Configuration conf = new Configuration();
  19. String[] otherArgs = (new GenericOptionsParser(conf, args)).getRemainingArgs();
  20. if(otherArgs.length < 2) {
  21. System.err.println("Usage: wordcount <in> [<in>...] <out>");
  22. System.exit(2);
  23. }
  24. Job job = Job.getInstance(conf, "word count test");
  25. job.setJarByClass(WordCountTest.class);
  26. job.setMapperClass(WordCountTest.TokenizerMapper.class);
  27. job.setCombinerClass(WordCountTest.IntSumReducer.class);
  28. job.setReducerClass(WordCountTest.IntSumReducer.class);
  29. job.setOutputKeyClass(Text.class);
  30. job.setOutputValueClass(IntWritable.class);
  31. for(int i = 0; i < otherArgs.length - 1; ++i) {
  32. FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
  33. }
  34. FileOutputFormat.setOutputPath(job, new Path(otherArgs[otherArgs.length - 1]));
  35. System.exit(job.waitForCompletion(true)?0:1);
  36. }
  37. public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
  38. private IntWritable result = new IntWritable();
  39. public IntSumReducer() {
  40. }
  41. public void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
  42. int sum = 0;
  43. IntWritable val;
  44. for(Iterator itr = values.iterator(); itr.hasNext(); sum += val.get()) {
  45. val = (IntWritable)itr.next();
  46. }
  47. this.result.set(sum);
  48. context.write(key, this.result);
  49. }
  50. }
  51. public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {
  52. private static final IntWritable one = new IntWritable(1);
  53. private Text word = new Text();
  54. public TokenizerMapper() {
  55. }
  56. public void map(Object key, Text value, Mapper<Object, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
  57. StringTokenizer itr = new StringTokenizer(value.toString());
  58. while(itr.hasMoreTokens()) {
  59. this.word.set(itr.nextToken());
  60. context.write(this.word, one);
  61. }
  62. }
  63. }
  64. }

查看分词后的结果,可见分词正确。

综合设计报告详细 - 图9

格式转换

将分词结果改为可以输入mysql的格式,同时过滤掉出现频次很低的单词,它们可能是乱码或者其他错误分词。

代码如下

  1. import java.io.BufferedReader;
  2. import java.io.InputStreamReader;
  3. import org.apache.hadoop.fs.FSDataOutputStream;
  4. import org.apache.hadoop.fs.Path;
  5. import org.apache.hadoop.fs.FSDataInputStream;
  6. import org.apache.hadoop.conf.Configuration;
  7. import org.apache.hadoop.fs.FileSystem;
  8. public class Fileconver{
  9. public static void main(String[] args) {
  10. try {
  11. Configuration conf = new Configuration();
  12. conf.set("fs.defaultFS", "hdfs://master:9000");
  13. conf.set("fs.hdfs.impl",
  14. "org.apache.hadoop.hdfs.DistributedFileSystem");
  15. FileSystem fs = FileSystem.get(conf);
  16. Path file = new Path("/MRDataClean/out/4/part-r-00000");
  17. FSDataInputStream getIt = fs.open(file);
  18. BufferedReader d = new BufferedReader(new InputStreamReader(getIt));
  19. String str = null;
  20. String filename = "/MRDataClean/out/4/test2.txt";
  21. FSDataOutputStream os = fs.create(new Path(filename));
  22. while((str = d.readLine()) != null)
  23. {
  24. String[] res = str.split(" ");
  25. System.out.println(res[0]);
  26. String resulte = "('" + res[0] +"', " + res[1] + ")," + "\n";
  27. if (Integer.parseInt(res[1]) > 50)
  28. {
  29. try {
  30. //
  31. byte[] buff = resulte.getBytes();
  32. os.write(buff, 0, buff.length);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }
  38. os.close();
  39. d.close(); // 关闭文件
  40. fs.close(); // 关闭hdfs
  41. } catch (Exception e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. }

查看结果

综合设计报告详细 - 图10

将结果添加到MySql

创建ciyun数据库:

  1. mysql> create database ciyun;
  2. mysql> use ciyun;

创建词云数据表kwonecloud

create table kwonecloud(kw varchar(50), num double);

插入测试数据:

('pro', 88),
('very', 58),
('一步', 71),
('不知道', 54),
('不错', 305),
('中', 62),
('买', 279),
('买了', 94),
('京东', 207),
('人性化', 54),
('价格', 92),
('体验', 76),
('值', 69),
('做工', 52),
('充电', 90),
('入手', 52),
('内存', 82),
('到了', 94),
('割手', 57),
('力', 56),
('功能', 197),
('卡', 118),
('双', 53),
('喜欢', 154),
('坚果', 156),
('外观', 172),
('大爆炸', 62),
('太', 87),
('好用', 98),
('好看', 66),
('安', 51),
('小米', 52),
('屏', 74),
('屏幕', 129),
('希望', 90),
('很喜欢', 59),
('很好', 123),
('很快', 96),
('快递', 84),
('性价比', 113),
('感觉', 285),
('手', 163),
('手感', 98),
('手机', 945),
('拍照', 74),
('指纹', 85),
('挺', 89),
('挺好', 63),
('操作', 51),
('支持', 96),
('收到', 61),
('时间', 60),
('服务', 52),
('机', 85),
('流畅', 160),
('满意', 83),
('漂亮', 89),
('物流', 63),
('特别', 60),
('用了', 155),
('电池', 82),
('真的', 122),
('确实', 75),
('第一次', 75),
('系统', 376),
('续航', 56),
('罗', 192),
('耳机', 58),
('胶囊', 66),
('膜', 93),
('苹果', 95),
('解锁', 57),
('设计', 65),
('评价', 64),
('说', 101),
('赞', 88),
('软件', 59),
('运行', 75),
('速度', 120),
('都是', 61),
('锤子', 428),
('键', 79),
('非常好', 85),
('高', 93);

查看插入数据,检查是否有乱码。

mysql> select * from kwonecloud;

综合设计报告详细 - 图11

生成词云

将词云的数据表指定为新创建的表:

String sql = "select kw, max(num) as num from kwonecloud group by kw order by num desc limit 30;";

代码如下:

package my.dao;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import my.domain.WordCloud;

import org.apache.log4j.Logger;

public class WordCloudDao {
public static Logger logger = Logger.getLogger(WordCloudDao.class);

    public static List<WordCloud> getWordCloudList() {

        String sql = "select kw, max(num) as num from kwonecloud group by kw order by num desc limit 30;";
        Connection conn=  null;
        ResultSet set = null;
        Statement stmt = null;
        List<WordCloud> list = new ArrayList<WordCloud>();

        try {
            conn = DBHelper.connDB();
            stmt = conn.createStatement();
            set = stmt.executeQuery(sql);
            while (set.next()) {
                WordCloud bean = new WordCloud();
                bean.setIsmobile(set.getString("kw"));
                bean.setNum(set.getInt("num"));
                list.add(bean);
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally{
            DBHelper.free(set, stmt, conn);
        }
        return list;
    }

}

Tomcat开启后,打开Firefox浏览器,输入下面网址

http://localhost:8081/ciyun/1/ksh1.jsp

可以看到MySQL库中的kwcloud表中的数据已经通过词云的形式展现出来了。

综合设计报告详细 - 图12