探索唐诗奥秘

2023-10-16 23:59
文章标签 探索 奥秘 唐诗

本文主要是介绍探索唐诗奥秘,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、项目背景
说起唐诗,我想你第一个想起的就是李白,对他的诗也应该是耳熟能详,然而唐代的诗人可并不止他一人,那么在众多的诗人中,到底谁的创作数量更多呢?这是个值得深究的的问题。
此外,唐朝是个比较开明的王朝,每个诗人的作品也风格迥异,像诗仙李白之豪迈浪漫,诗圣杜甫之沉郁顿挫,他们每个人的生活背景不同,作品的风格也不同,那么,在这么多首诗中,到底哪种风格的作品较多呢?要了解这个问题,我们就需要对这些唐诗的词语进行分析,分析出这些诗中每个词语出现的频次,由此,我们就可以判断出唐朝诗歌的整体风格偏向了。到此,我们就有了两个任务了,分析唐代诗人每个诗人的创作数量以及所有唐诗中词语的出现频次。由此,我们的唐诗项目也就应运而生了。

二、项目简介:

唐诗分析程序是通过抓取互联网上的唐诗,对其网页上的数据进行解析、清洗、存储到数据库,然后再对内容进行分析,最后输出报告。

三、 数据来源

数据采集主要来自:古诗文网

四、 功能

实现数据采集、清洗、存储、分析、分析结果可视化展示。这几个功能

五、 所用技术

Java方面:面向对象(接口,类)、多线程、集合框架、设计模式、JDBC编程、常用类、Stream流、Lambda表达式

第三方的:htmlunit(网页分析工具)、Ansj(分词)、sparkjava、Druid(数据库连接池)

前端:HTML、JavaScript、jQuery、Echarts

工具:IDEA、Maven

六、 实现流程
在这里插入图片描述
整体流程概括:
(1)采集数据并解析
先从原始网页拿到古诗文网的URL地址,用htmlunit网页分析工具对其网页源码进行分析,用采集器(webClient)对网页的数据进行采集, 并对采集得到的page对象进行解析,解析的结果若是详情页则放在一个详情页的队列中,若是是非详情页则把它放在一个文档页的队列中,继续对文档队列中的page对象进行解析。
(2)对解析结果进行清洗并存储
从详情队列中取出一个详情页调用清洗的多线程调度器page对象进行清洗,将清洗的结果存到数据库中。同时在控制台打印。
(3)访问数据库中的数据并对其进行分析
采用JDBC编程对数据库进行操作,通过数据访问拿到数据库中的数据对其进行分析,其中包含作者的创作数量分析以及词云分析,分析的结果存储在其对应的集合中
(4)通过web接口将结果部署到服务器
通过web接口将其我们的结果序列化后部署到服务器上,再通过js可视化编程,将分析结果以图表的形式在网页上显示出来。至此就完成了我们的唐诗分析流程。

七、 代码模块功能介绍
在这里插入图片描述
每个模块的详细介绍及框架如图:
在这里插入图片描述
接下来我们对每一部分的代码进行展示说明:

八、项目源码

crawler爬虫类的包

该类是为采集器所抓取到的网页对象的封装

public class Page {private final String base;// 数据网站的根地址private final String path;//具体网页的路径private HtmlPage htmlPage;//网页的DOM对象private final boolean detail;//表示网页是否是详情页private Set<Page> subPage=new HashSet<>();//子页面对象集合private DataSet dataset=new DataSet();//数据对象public Page(String base, String path, boolean detail) {this.base = base;this.path = path;this.detail = detail;}public String getUrl(){return this.base+this.path;}public void setHtmlPage(HtmlPage htmlPage) {this.htmlPage = htmlPage;}
}

该类采用Map集合存储清洗的数据

package com.symx.poetrytang.crawler.common;import lombok.ToString;
import java.util.HashMap;
import java.util.Map;/*** 存储清洗的数据*    data 把DOM解析清洗之后存储的数据*      eg:*        标题:送孟浩然之广陵*        作者:李白*        正文:******/
@ToString
public class DataSet {private Map<String,Object> data=new HashMap<>();public void putData(String key,Object value){this.data.put(key,value);}//通过key值获取一个数据对象public Object getData(String key){return this.data.get(key);}//获取所有数据对象的集合public Map<String,Object> getData(){return new HashMap<>(this.data);}
}

解析器相关类

package com.symx.poetrytang.crawler.parse;
import com.symx.poetrytang.crawler.common.Page;
/***解析页面**/
public interface Parse {void parse(final Page page);
}

对文档页面的解析

package com.symx.poetrytang.crawler.parse;import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.symx.poetrytang.crawler.common.Page;
/*** 文档页面**/
public class DocumentParse implements Parse {@Overridepublic void parse(final Page page) {if (page.isDetail()) {return;}HtmlPage htmlPage = page.getHtmlPage();htmlPage.getBody().getElementsByAttribute("div", "class", "typecont").forEach(div -> {DomNodeList<HtmlElement> aNodeList = div.getElementsByTagName("a");aNodeList.forEach(aNode -> {String path = aNode.getAttribute("href");Page subPage = new Page(page.getBase(), path, true);page.getSubPage().add(subPage);});});}
}

对详情页面的解析

package com.symx.poetrytang.crawler.parse;import com.gargoylesoftware.htmlunit.html.*;
import com.symx.poetrytang.crawler.common.Page;
import com.symx.poetrytang.analyze.entity.PoetryInfo;/*** 详情解析页面* */
public class DataPageParse implements Parse {@Overridepublic void parse(final Page page) {if (!page.isDetail()) {return;}HtmlPage htmlPage = page.getHtmlPage();HtmlElement body = htmlPage.getBody();//标题String titlePath = "//div[@class='cont']/h1/text()";DomText titleDom = (DomText) body.getByXPath(titlePath).get(0);String title = titleDom.asText();//朝代String dynastyPath = "//div[@class='cont']/p/a[1]";HtmlAnchor dynastyDom = (HtmlAnchor) body.getByXPath(dynastyPath).get(0);String dynasty = dynastyDom.asText();//作者String authorPath = "//div[@class='cont']/p/a[2]";HtmlAnchor authorDom = (HtmlAnchor) body.getByXPath(authorPath).get(0);String author = authorDom.asText();//正文String contentPath = "//div[@class='cont']/div[@class='contson']";HtmlDivision contentDom = (HtmlDivision) body.getByXPath(contentPath).get(0);String content = contentDom.asText();page.getDataset().putData("title",title);page.getDataset().putData("dynasty",dynasty);page.getDataset().putData("author",author);page.getDataset().putData("content",content);//更多的数据page.getDataset().putData("url",page.getUrl());}
}

清洗器相关类

package com.symx.poetrytang.crawler.pipeline;import com.symx.poetrytang.crawler.common.Page;public interface Pipeline {void pipeline(final Page page);
}

将清洗后的数据存放到数据库(采用JDBC编程)

package com.symx.poetrytang.crawler.pipeline;
import com.symx.poetrytang.crawler.common.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/***将数据存储在数据库**/
public class DatabasePipeline implements Pipeline {private final Logger logger = LoggerFactory.getLogger(DatabasePipeline.class);private final DataSource dataSource;public DatabasePipeline(DataSource datasource){this.dataSource=datasource;}@Overridepublic void pipeline(Page page){String dynasty=(String)page.getDataset().getData("dynasty");String author=(String)page.getDataset().getData("author");String title=(String)page.getDataset().getData("title");String content=(String)page.getDataset().getData("content");String sql="insert into poetry(title,dynasty,author,content) values(?,?,?,?)";try(Connection connection=dataSource.getConnection();PreparedStatement statement=connection.prepareStatement(sql)){statement.setString(1,title);statement.setString(2,dynasty);statement.setString(3,author);statement.setString(4,content);statement.executeUpdate();}catch(SQLException e){logger.error("Database insert occur exception {}.", e.getMessage());}}public DataSource getDataSource() {return dataSource;}
}

将清洗后得到的数据存储到Map集合里,再将其打印输出到控制台。

package com.symx.poetrytang.crawler.pipeline;import com.symx.poetrytang.crawler.common.Page;import java.util.Map;
/*** 将数据打印到控制台**/
public class ConsolePipeline implements Pipeline {@Overridepublic void pipeline(final Page page) {Map<String,Object> data=page.getDataset().getData();//存储System.out.println(data);}
}

爬虫类进行调度

package com.symx.poetrytang.crawler;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.symx.poetrytang.crawler.common.Page;
import com.symx.poetrytang.crawler.parse.Parse;
import com.symx.poetrytang.crawler.pipeline.Pipeline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;/***WebClient(BrowserVersion.CHROME) */
public class Crawler {private final Logger logger = LoggerFactory.getLogger(Crawler.class);private  final Queue<Page> docQueue=new LinkedBlockingDeque<>();//放置文档页面(超链接)private  final Queue<Page> detailQueue=new LinkedBlockingDeque<>();//放置详情页面private final WebClient webclient;//采集器private final List<Parse> parseList=new LinkedList<>();//所有解析器private final List<Pipeline> pipelineList=new LinkedList<>();//所有清洗器private final ExecutorService excutorservice; //线程执行器public Crawler(){this.webclient=new WebClient(BrowserVersion.CHROME);this.webclient.getOptions().setJavaScriptEnabled(false);//设置js脚本无用//创建线程执行器this.excutorservice= Executors.newFixedThreadPool(8, new ThreadFactory() {//采用自增长方式给爬虫的线程执行器的Id 赋值private final AtomicInteger id=new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {Thread thread=new Thread(r);thread.setName("Crawler-Thread-"+id.getAndIncrement());return thread;}});}public void start(){this.excutorservice.submit(this::parse);//爬取解析this.excutorservice.submit(this::pipeline);//清洗}//爬取解析private void parse(){while(true) {try {Thread.sleep(1000);} catch (InterruptedException e) {logger.error("Parse occur exception {} .", e.getMessage());}final Page page = this.docQueue.poll();if (page == null) {continue;}this.excutorservice.submit(() -> {try {//采集HtmlPage htmlpage = Crawler.this.webclient.getPage(page.getUrl());page.setHtmlPage(htmlpage);for (Parse parse : this.parseList) {parse.parse(page);}//是详情页面执行if (page.isDetail()) {Crawler.this.detailQueue.add(page);} else {Iterator<Page> iterator = page.getSubPage().iterator();while (iterator.hasNext()) {Page subPage = iterator.next();Crawler.this.docQueue.add(subPage);iterator.remove();}}} catch (IOException e) {logger.error("Parse task occur exception {} .", e.getMessage());}});}}//清洗public void pipeline(){while(true){try {Thread.sleep(1000);} catch (InterruptedException e) {logger.error("Parse task occur exception {} .", e.getMessage());}//detail队列的page对象final Page page=this.detailQueue.poll();if(page==null){continue;}//清洗的多线程调度this.excutorservice.submit(()->{for(Pipeline pipeline:Crawler.this.pipelineList){pipeline.pipeline(page);}});}}public void addPage(Page page){this.docQueue.add(page);}//添加解析器public void addParse(Parse parse){this.parseList.add(parse);}//添加管道public void addPipeLine(Pipeline pipeline){this.pipelineList.add(pipeline);}//爬虫停止public void stop(){if(this.excutorservice!=null&&this.excutorservice.isShutdown()){this.excutorservice.shutdown();}logger.info("Crawler stopped...");}
}

Dao包下的相关类:

package com.symx.poetrytang.analyze.dao.impl;
import com.symx.poetrytang.analyze.entity.PoetryInfo;
import com.symx.poetrytang.analyze.model.AuthorCount;
import java.util.List;
/*** 分析唐诗中作者的创作数量**/
public interface AnalyzeDao {List<AuthorCount> analyzeAuthorCount();//分析作者的创作数量List<PoetryInfo> queryAllPoetryInfo();//查询所有诗文
}

采用JDBC编程连接到数据库,对数据库中的数据进行查询操作,得到每首诗的信息以及每个作者的创作数量。

package com.symx.poetrytang.analyze.dao.impl;
import com.symx.poetrytang.analyze.entity.PoetryInfo;
import com.symx.poetrytang.analyze.model.AuthorCount;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/*** 将数据库中存储的诗取出来进行分类整理得到每个作者的创作数量**/
public class AnalyzeDaoImpl implements AnalyzeDao {private final Logger logger = LoggerFactory.getLogger(AnalyzeDaoImpl.class);private DataSource dataSource; //数据源对象public AnalyzeDaoImpl(DataSource dataSource) {this.dataSource = dataSource;}@Override/***方法名:analyzeAuthorCount()*功能:分析作者的创作数量*返回值:List集合,里面存储每位作者及其对应的作品数量**/public List<AuthorCount> analyzeAuthorCount(){List<AuthorCount> datas=new ArrayList<>();String sql="select count(*) as count,author from poetry_info group by author;";try (Connection connection=dataSource.getConnection();  //通过数据源连接到数据库//返回sql语句执行结果的结果集PreparedStatement statement=connection.prepareStatement(sql); ResultSet rs=statement.executeQuery()){while(rs.next()){AuthorCount authorCount=new AuthorCount();authorCount.setAuthor(rs.getString("author"));authorCount.setCount(rs.getInt("count"));datas.add(authorCount);}} catch (SQLException e) {e.printStackTrace();}return datas;}/***方法名:queryAllPoetryInfo()*功能:查询所有诗的相关信息,包括题目、朝代、作者、内容,将其所查结果保存到一个链表 *          中,链表中的每个元素为没收拾的相关信息* 返回值:保存有所有诗的相关信息的链表          */public  List<PoetryInfo> queryAllPoetryInfo(){List<PoetryInfo> datas=new ArrayList<>();String sql="select title,dynasty,author,content from poetry_info;";try(Connection connection=dataSource.getConnection();PreparedStatement statement=connection.prepareStatement(sql);ResultSet rs=statement.executeQuery();){while(rs.next()) {PoetryInfo poetryInfo=new PoetryInfo();poetryInfo.setTitle(rs.getString("title"));poetryInfo.setDynasty(rs.getString("dynasty"));poetryInfo.setAuthor(rs.getString("author"));poetryInfo.setContent(rs.getString("content"));datas.add(poetryInfo);}}catch(SQLException e){logger.error("Database query occur exception{}.",e.getMessage());}return datas;}
}

entity包下:

定义一首诗的相关信息

package com.symx.poetrytang.analyze.entity;import lombok.Data;
/*** 对诗的信息进行分类**/
@Data
public class PoetryInfo {private String title;private String dynasty;private String author;private String content;
}

model包下:

定义作者及其创作数量相关的类

package com.symx.poetrytang.analyze.model;import lombok.Data;
/*** 有关作者创作信息的类,包括作者以及作者的创作数量**/
@Data
public class AuthorCount {private String author;private Integer count;
}

定义词频相关的类

package com.symx.poetrytang.analyze.model;import lombok.Data;
/*** 词频相关类**/
@Data
public class WordCount {private String word;private Integer count;
}

service包下:

定义作者创作分析以及词云分析相关的类,相当于对前面model包下两个类的一次封装。

package com.symx.poetrytang.analyze.service.impl;import com.symx.poetrytang.analyze.model.AuthorCount;
import com.symx.poetrytang.analyze.model.WordCount;
import java.util.List;public interface AnalyzeService {List<AuthorCount> analyzeAuthorCount(); //作者创作分析List<WordCount> analyzeWordCount();//词云分析
}

基于前面 AnalyzeDao 对作者创作数量的分析结果,该类对此结果进行整理并进行了排序,同时进行分词操作

package com.symx.poetrytang.analyze.service.impl;import com.symx.poetrytang.analyze.dao.impl.AnalyzeDao;
import com.symx.poetrytang.analyze.entity.PoetryInfo;
import com.symx.poetrytang.analyze.model.AuthorCount;
import com.symx.poetrytang.analyze.model.WordCount;
import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.NlpAnalysis;
import java.util.*;public class AnalyzeServiceImp implements AnalyzeService {private final AnalyzeDao analyzeDao;public AnalyzeServiceImp(AnalyzeDao analyzeDao) {this.analyzeDao = analyzeDao;}@Overridepublic List<AuthorCount> analyzeAuthorCount(){//此处结果并未排序//排序方式//1. DAO层SQL排序//2. Service层进行数据排序List<AuthorCount> authorCounts=analyzeDao.analyzeAuthorCount();//按count升序排authorCounts.sort(Comparator.comparing(AuthorCount::getCount));return  authorCounts;}public List<WordCount> analyzeWordCount(){/*** 1,查询出所有数据* 2,取出title,content* 3,分词(过滤w,null,length<2)* 4,统计(key-value)**/Map<String,Integer> map=new HashMap<>();List<PoetryInfo> poetryInfos=analyzeDao.queryAllPoetryInfo();for(PoetryInfo poetryInfo:poetryInfos){List<Term> terms=new ArrayList<>();String title=poetryInfo.getTitle();String content=poetryInfo.getContent();terms.addAll(NlpAnalysis.parse(title).getTerms());terms.addAll(NlpAnalysis.parse(content).getTerms());Iterator<Term> iterator=terms.iterator();while(iterator.hasNext()){Term term=iterator.next();//词性的过滤if(term.getNatureStr()==null||term.getNatureStr().equals("w")){iterator.remove();continue;}//词的过滤if(term.getRealName().length()<2){iterator.remove();continue;}//统计String realName=term.getRealName();int count=0;if(map.containsKey(realName)){count=map.get(realName)+1;}else{count=1;}map.put(realName,count);}}List<WordCount> wordCounts=new ArrayList<>();for(Map.Entry<String,Integer> entry:map.entrySet()){WordCount wordCount=new WordCount();wordCount.setCount(entry.getValue());wordCount.setWord(entry.getKey());wordCounts.add(wordCount);}return wordCounts;}
}

config包下:

ConfigProperties 类用来把代码里的输入固化到属性文件中,提供了连接所需的准。备信息,方便之后的修改

package com.symx.poetrytang.config;import lombok.Data;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/*** 从外部文件获得一些配置信息在该类里进行处理**/
@Data
public class ConfigProperties {//爬虫相关配置变量的定义private String crawlerBase;private String crawlerPath;private boolean crawlerDetail;//数据库相关配置变量的定义private String dbUsername;private String dbPassword;private String dbUrl;private String dbDriverClass;//控制台相关配置变量的定义private boolean enableConsole;public ConfigProperties(){//从外部文件加载InputStream inputStream=ConfigProperties.class.getClassLoader().getResourceAsStream("config.properties");Properties p=new Properties();try {p.load(inputStream);} catch (IOException e) {e.printStackTrace();}this.crawlerBase=String.valueOf(p.get("crawler.base"));this.crawlerPath=String.valueOf(p.get("crawler.path"));this.crawlerDetail=Boolean.parseBoolean(String.valueOf(p.get("crawler.detail")));this.dbUsername=String.valueOf(p.get("db.username"));this.dbPassword=String.valueOf(p.get("db.password"));this.dbDriverClass=String.valueOf(p.get("db.url"));this.dbUrl=String.valueOf(p.get("db.driver_class"));this.enableConsole=Boolean.valueOf(String.valueOf(p.getProperty("config.enable_console","false")));}
}

ObjectFactory是对象工厂类,用来实例化我们所需要的所有对象,如配置对象,数据源对象,爬虫对象,web对象。这里用到了工厂方法的设计模式,其目的是为了将所有对象的创建与初始化放到工厂类中完成,这样在主方法里只需创建一个工厂对象就可完成所有所需对象的创建与初始化,进行对象封装,简化了主方法的代码量,使客户端变得更加简单,降低了代码的耦合度。
同时用单例的设计模式使构造方法私有化,目的是因为我们的数据源对象和爬虫对象等只需要有一个,因此初始化一个对象就可以,单例模式正好满足了我们的要求,保证了外部无法产生新的实例化对象,一定程度上也保证了程序的安全性。

package com.symx.poetrytang.config;import com.alibaba.druid.pool.DruidDataSource;
import com.symx.poetrytang.analyze.dao.impl.AnalyzeDao;
import com.symx.poetrytang.analyze.dao.impl.AnalyzeDaoImpl;
import com.symx.poetrytang.analyze.service.impl.AnalyzeService;
import com.symx.poetrytang.analyze.service.impl.AnalyzeServiceImp;
import com.symx.poetrytang.crawler.Crawler;
import com.symx.poetrytang.crawler.common.Page;
import com.symx.poetrytang.crawler.parse.DataPageParse;
import com.symx.poetrytang.crawler.parse.DocumentParse;
import com.symx.poetrytang.crawler.pipeline.ConsolePipeline;
import com.symx.poetrytang.crawler.pipeline.DatabasePipeline;
import com.symx.poetrytang.web.WebController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;public class ObjectFactory {private static final ObjectFactory instance=new ObjectFactory();private final Logger logger = LoggerFactory.getLogger(ObjectFactory.class);//存放所有对象private  static final Map<Class,Object> objectHashMap=new HashMap<>();private ObjectFactory(){//1.初始化配置对象initConfigProperties();//2,创建数据源对象initDataSource();//3,爬虫对象initCrawler();//4,web对象initWebController();//5.对象清单打印输出printObjectList();}private void initWebController() {DataSource dataSource=getObject(DataSource.class);AnalyzeDao analyzeDao=new AnalyzeDaoImpl(dataSource);AnalyzeService analyzeService=new AnalyzeServiceImp(analyzeDao);WebController webController=new WebController(analyzeService);objectHashMap.put(WebController.class,webController);}private void initCrawler() {ConfigProperties configProperties=getObject(ConfigProperties.class);DataSource dataSource=getObject(DataSource.class);final Page page =new Page(configProperties.getCrawlerBase(),configProperties.getCrawlerPath(),configProperties.isCrawlerDetail());Crawler crawler=new Crawler();crawler.addParse(new DocumentParse());crawler.addParse(new DataPageParse());if(configProperties.isEnableConsole()){crawler.addPipeLine(new ConsolePipeline());}crawler.addPipeLine(new DatabasePipeline(dataSource));crawler.addPage(page);objectHashMap.put(Crawler.class,crawler);}private void initDataSource() {ConfigProperties configProperties=getObject(ConfigProperties.class);DruidDataSource dataSource =new DruidDataSource();dataSource.setUsername(configProperties.getDbUsername());dataSource.setPassword(configProperties.getDbPassword());dataSource.setDriverClassName(configProperties.getDbDriverClass());dataSource.setUrl(configProperties.getDbUrl());objectHashMap.put(DataSource.class,dataSource);}private void initConfigProperties() {ConfigProperties configProperties=new ConfigProperties();objectHashMap.put(ConfigProperties.class,configProperties);//打印出配置信息logger.info("ConfigProperties info:\n{}", configProperties.toString());}public  <T> T getObject(Class classz){if(!objectHashMap.containsKey(classz)){throw new IllegalArgumentException("class"+classz.getName()+"noy found Object");}return (T)objectHashMap.get(classz);}public static ObjectFactory getInstance(){return instance;}private void printObjectList(){logger.info("====== ObjectFactory List =====");for(Map.Entry<Class,Object> entry:objectHashMap.entrySet()){logger.info(String.format("[%-5s] ==> [%s]", entry.getKey().getCanonicalName(),entry.getValue().getClass().getCanonicalName()));}logger.info("================================");}
}

唐诗分析的主调度类

package com.symx.poetrytang;import com.symx.poetrytang.config.ObjectFactory;
import com.symx.poetrytang.crawler.Crawler;
import com.symx.poetrytang.web.WebController;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;/*** 唐诗分析主类* */
public class tangshiAnalyze {//加日志private static final Logger LOGGER =  LoggerFactory.getLogger(tangshiAnalyze.class);public static void main(String[] args) {WebController webController = ObjectFactory.getInstance().getObject(WebController.class);//运行了web服务,提供接口LOGGER.info("Web Server launch ...");webController.launch();//启动爬虫if (args.length == 1 && args[0].equals("run-crawler")) {Crawler crawler = ObjectFactory.getInstance().getObject(Crawler.class);LOGGER.info("Crawler started ...");crawler.start();}}
}

web包

将程序部署到服务器上

package com.symx.poetrytang.web;import com.google.gson.Gson;
import com.symx.poetrytang.analyze.model.AuthorCount;
import com.symx.poetrytang.analyze.model.WordCount;
import com.symx.poetrytang.analyze.service.impl.AnalyzeService;
import com.symx.poetrytang.config.ObjectFactory;
import com.symx.poetrytang.crawler.Crawler;
import spark.ResponseTransformer;
import spark.Spark;
import java.util.List;
/*** Web API* Sparkjava完成API的开发* */
public class WebController {private final AnalyzeService analyzeService;public WebController(AnalyzeService analyzeService){this.analyzeService=analyzeService;}public List<AuthorCount> analyzeAuthorCount(){return  analyzeService.analyzeAuthorCount();}public List<WordCount> analyzeWordCount(){return analyzeService.analyzeWordCount();}public void launch(){ResponseTransformer transformer=new JSONResponseTransformer();//前端静态文件的目录Spark.staticFileLocation("/static");Spark.get("/analyze/author_count", ((request, response) -> analyzeAuthorCount()),transformer);Spark.get("/analyze/word_count",((request, response) -> analyzeWordCount()),transformer);Spark.get("/crawler/stop", ((request, response) -> {Crawler crawler = ObjectFactory.getInstance().getObject(Crawler.class);crawler.stop();return "爬虫停止";}));}//Gson() 方法用于将对象序列化为字符串public static  class JSONResponseTransformer implements ResponseTransformer{private Gson gson=new Gson();@Overridepublic  String render(Object o)throws Exception{return gson.toJson(o);}}
}

数据库的创建语句

create database if not exists `tangshi`;
use tangshi;
create table if not exists `poetry_info`(
title varchar(32) not null,
dynasty varchar(32) not null,
author varchar(12) not null,
content varchar(1024) not null
);

js编程让其在浏览器显示

function creationRanking(id) {//HTTP Method : GET//HTTP URL : 请求地址(服务端提供的API接口)$.get({url: "/analyze/author_count",dataType: "json",method: "get",success: function (data, status, xhr) {//echarts图表对象var myChart = echarts.init(document.getElementById(id));var options = {//图标的标题title: {text: '唐诗创作排行榜'},tooltip: {},//柱状图的提示信息legend: {data: ['数量(首)']},//X轴的数据:作者xAxis: {data: []},//Y轴的数据:创作的数量yAxis: {},series: [{name: '创作数量',type: 'bar',data: []}]};//List<AuthorCount>for (var i=0; i< data.length; i++) {var authorCount  = data[i];options.xAxis.data.push(authorCount.author);options.series[0].data.push(authorCount.count);}myChart.setOption(options, true);},error: function (xhr, status, error) {}});
}function cloudWorld(id) {$.get({url: "/analyze/word_cloud",dataType: "json",method: "get",success: function (data, status, xhr) {var myChart = echarts.init(document.getElementById(id));var options = {series: [{type: 'wordCloud',shape: 'pentagon',left: 'center',top: 'center',width: '80%',height: '80%',right: null,bottom: null,sizeRange: [12, 60],rotationRange: [-90, 90],rotationStep: 45,gridSize: 8,drawOutOfBound: false,textStyle: {normal: {fontFamily: 'sans-serif',fontWeight: 'bold',color: function () {//rgb(r,g,b)return 'rgb(' + [Math.round(Math.random() * 160),Math.round(Math.random() * 160),Math.round(Math.random() * 160)].join(',') + ')';}},emphasis: {shadowBlur: 10,shadowColor: '#333'}},// Data is an array. Each array item must have name and value property.data: []}]};for (var i=0 ;i<data.length; i++) {var wordCount = data[i];//wordCount => 词 : 词频options.series[0].data.push({name: wordCount.word,value: wordCount.count,textStyle: {normal: {},emphasis: {}}});}myChart.setOption(options, true);},error: function (xhr, status, error) {}});
}

九、运行结果

作者创作数量分析结果图:
在这里插入图片描述

词云分析结果图
在这里插入图片描述
十、项目测试

说到测试,我们首先要回到需求,最初我们的需求是分析唐诗中作者的创作数量以及词语的词频,因此我们很有必要对分析结果的可靠性进行测试,以下列出了一些测试点作为测试用例对该项目进行测试;
在这里插入图片描述
测试结果:
在对爬虫的正常功能进行测试时,我们先在古诗文网页里选了一篇超链接页和详情页查看它的网页源码, 通过对源码的分析,我们写程序来抓取它的Dom对象,并对其里面的内容进行解析清洗,将结果在控制台打印出来,打印结果无误,我们在采用多线程编程对整个网页的数据进行采集、解析、清洗与存储,然后在数据库里查看其存储的内容对其爬取结果的正确性进行分析。
在异常测试时发现,更改数据库的密码,则爬取结果不能存储到数据库中,更改网站的url地址,则爬虫不能爬取到我们需要的数据;在爬虫爬取时断网,则爬取的数据不完整,致使最终的分析结果不准确,影响功能的正常实现。
在性能测试方面我是采用手工测的,因为我们采用了多线程调度器来进行处理,因此处理速度还是较快的,差不多一分钟的时间就已经爬取到了所有的诗文。
十一、总结
这次项目的完成中,遇到了很多技术上的难题,比如爬虫那块,不知道如何爬取网页上的数据并进行解析,后经老师指导用htmlunit工具来实现爬虫部分,还有前端的js部分也不太会,后经过查找资料,了解它的用法,来帮助我们实现了前端的可视化页面部分。由此总结,做项目可能有的东西你并没有学过,但只要我们知道了它如何使用,解决了实际问题就行了。还有一点就是不言败喽。

这篇关于探索唐诗奥秘的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/221598

相关文章

pip install jupyterlab失败的原因问题及探索

《pipinstalljupyterlab失败的原因问题及探索》在学习Yolo模型时,尝试安装JupyterLab但遇到错误,错误提示缺少Rust和Cargo编译环境,因为pywinpty包需要它... 目录背景问题解决方案总结背景最近在学习Yolo模型,然后其中要下载jupyter(有点LSVmu像一个

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

轻松录制每一刻:探索2024年免费高清录屏应用

你不会还在用一些社交工具来录屏吧?现在的市面上有不少免费录屏的软件了。别看如软件是免费的,它的功能比起社交工具的录屏功能来说全面的多。这次我就分享几款我用过的录屏工具。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  这个软件的操作方式非常简单,打开软件之后从界面设计就能看出来这个软件操作的便捷性。界面的设计简单明了基本一打眼你就会轻松驾驭啦

深入探索嵌入式 Linux

摘要:本文深入探究嵌入式 Linux。首先回顾其发展历程,从早期尝试到克服诸多困难逐渐成熟。接着阐述其体系结构,涵盖硬件、内核、文件系统和应用层。开发环境方面包括交叉编译工具链、调试工具和集成开发环境。在应用领域,广泛应用于消费电子、工业控制、汽车电子和智能家居等领域。关键技术有内核裁剪与优化、设备驱动程序开发、实时性增强和电源管理等。最后展望其未来发展趋势,如与物联网融合、人工智能应用、安全性与

【vue3|第28期】 Vue3 + Vue Router:探索路由重定向的使用与作用

日期:2024年9月8日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉在这里插入代码片得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.4083;0.98365 = 0.0006 说

多云架构下大模型训练的存储稳定性探索

一、多云架构与大模型训练的融合 (一)多云架构的优势与挑战 多云架构为大模型训练带来了诸多优势。首先,资源灵活性显著提高,不同的云平台可以提供不同类型的计算资源和存储服务,满足大模型训练在不同阶段的需求。例如,某些云平台可能在 GPU 计算资源上具有优势,而另一些则在存储成本或性能上表现出色,企业可以根据实际情况进行选择和组合。其次,扩展性得以增强,当大模型的规模不断扩大时,单一云平

探索Invoke:Python自动化任务的瑞士军刀

文章目录 探索Invoke:Python自动化任务的瑞士军刀背景:为何选择Invoke?`invoke`是什么?如何安装`invoke`?简单的`invoke`库函数使用方法场景应用:`invoke`在实际项目中的使用场景一:自动化测试场景二:代码格式化场景三:部署应用 常见问题与解决方案问题一:命令执行失败问题二:权限不足问题三:并发执行问题 总结 探索Invoke:P

使用亚马逊Bedrock的Stable Diffusion XL模型实现文本到图像生成:探索AI的无限创意

引言 什么是Amazon Bedrock? Amazon Bedrock是亚马逊云服务(AWS)推出的一项旗舰服务,旨在推动生成式人工智能(AI)在各行业的广泛应用。它的核心功能是提供由顶尖AI公司(如AI21 Labs、Anthropic、Cohere、Meta、Mistral AI、Stability AI以及亚马逊自身)开发的多种基础模型(Foundation Models,简称FMs)。

探索Python的数学魔法:Numpy库的神秘力量

文章目录 探索Python的数学魔法:Numpy库的神秘力量背景:为什么选择Numpy?Numpy是什么?如何安装Numpy?五个简单的库函数使用方法场景应用常见Bug及解决方案总结 探索Python的数学魔法:Numpy库的神秘力量 背景:为什么选择Numpy? 在Python的世界中,数据处理和科学计算是不可或缺的一部分。但原生Python在处理大规模数据时可能会显