前言
不知道你们有没有这样一个习惯,当需要下载某个资源时,在互联网上查找一段时间后,你找到了一个可以下载到你需要的资源的网站,你在这个网站下载了所需要的资源,它帮了你大忙,这时候就会想将这个帮了自己大忙的网站收藏到浏览器,但随着浏览器中收藏的资源网站越来越多,当自己后面需要寻找另一个资源时,不得不在每个资源网站间来回切换搜索,这时候就非常麻烦,这时候我在想是不是可以开发一个通用的能够聚合搜索所有浏览器中的资源网站的系统,实现一次搜索来检索多个网站。
列举出所有功能——》列举对象和响应的属性——》实现对应的功能——》不断完善和更新网站功能
目录
1.聚合搜索具体要实现的功能(画出思维导图)
2.根据思维导图进行大致的项目搭建
3.对具体功能进行实现
1.聚合搜索具体要实现的功能(画出思维导图)
普通用户:可以登陆系统、提交网站、提供网站解析规则、浏览编辑删除查找自己的解析的规则、可以设置规则为公开还是私有。
管理员:可以管理用户、对提交的功能进行审核、可以对已有规则进行编辑删除查找等操作。
2.根据思维导图进行大致的项目搭建
因为是web项目,所以采用当下比较热的spring boot来作为后端的服务。
管理模块:该模块用户的登陆退出、对网站的规则进行管理,维护网站。
项目启动模块:这个是整个项目的启动模块。
公共模块:封装了一些常用的方法,定义了统一的结果返回类和通用异常类。
搜索模块:该模块是整个项目的核心所在,用于解析数据库中保存的数据,并根据用户的请求进行解析和返回结果。
管理模块
当前模块还在有序开发中,后续将持续更新。
项目启动模块
具体代码如下
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ServerStart {
public static void main(String[] args) {
SpringApplication.run(ServerStart.class);
System.out.println("启动成功!!!");
}
}
该类没什么好讲的,主要是spring boot的启动类。
公共模块
ResponseResult类,该类主要是用于返回统一的json数据,具体代码如下。
public class ResponseResult<T>{
/**
* 状态码
*/
private Integer code;
/**
* 提示信息,如果有错误时,前端可以获取该字段进行提示
*/
private String msg;
/**
* 查询到的结果数据,
*/
private T data;
public ResponseResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
}
FileUtil类,该类是一个用于读取resource目录下的文件,具体代码如下。
import org.springframework.data.repository.init.ResourceReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class FileUtil {
/**
* 读取resource下的json规则文件
* @return
*/
public static String getFileData() {
String fileData=null;
InputStream inputStream = ResourceReader.class.getResourceAsStream("/urlRule.json");
if (inputStream != null) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
StringBuffer stringBuffer=new StringBuffer();
while ((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
fileData=stringBuffer.toString();
reader.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}else {
System.out.println("resource目录中的json文件读取失败!!!!");
}
return fileData;
}
}
搜索模块
该模块用于解析网站的规则,并将结果返回到前端。
SoftwareSearchImpl类,该类是这个模块的核心,主要用于解析网站,返回处理后的结果,后续该类可能会做性能优化,采用多线程方式来实现,目前的解析实现如下。
import com.gc.config.HttpclientPool;
import com.gc.config.InitData;
import com.gc.domain.SearchResult;
import com.gc.domain.UrlRule;
import com.gc.service.SoftwareSearch;
import com.gc.service.UrlRuleParse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Service
public class SoftwareSearchImpl implements SoftwareSearch {
private static List<UrlRule> urlRuleList=null;
@Autowired
HttpclientPool httpclientPool;
@Autowired
UrlRuleParse urlRuleParse;
@Override
public List<SearchResult> searchParse(String s, Integer p){
List<UrlRule> urlRuleList = InitData.urlRuleList;
List<UrlRule> requestUrlList = urlRuleParse.urlRuleParse(urlRuleList, s, p);
String html=null;
List<SearchResult> ResultList=new ArrayList<>();
int id=0;
CloseableHttpClient httpclient=null;
CloseableHttpResponse response1=null;
for (UrlRule urlRule: requestUrlList){
//httpclient = HttpClients.createDefault();
httpclient=httpclientPool.getHttpClient();
HttpGet httpGet = new HttpGet(urlRule.getFinalUrl());
httpGet.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62");
if (!"".equals(urlRule.getCookie())){
httpGet.addHeader("Cookie",urlRule.getCookie());
}
try {
response1 = httpclient.execute(httpGet);
html = EntityUtils.toString(response1.getEntity(), "UTF-8");
Document doc = Jsoup.parse(html);
Elements pictureElements = doc.select(urlRule.getPicturePosition());
Elements titleElements = doc.select(urlRule.getTitlePosition());
Elements urlElements = doc.select(urlRule.getUrlPosition());
Elements synopsisElements=doc.select(urlRule.getSynopsisPosition());
for (int i = 0;i<pictureElements.size();i++){
SearchResult searchResult = new SearchResult();
String pictureSrc=null;
if ("".equals(urlRule.getPicturePositionAttr())){
pictureSrc = pictureElements.get(i).text();
}else {
pictureSrc = pictureElements.get(i).attr(urlRule.getPicturePositionAttr());
}
String url=null;
if ("".equals(urlRule.getUrlPositionAttr())){
url = urlElements.get(i).text();
}else {
url = urlElements.get(i).attr(urlRule.getUrlPositionAttr());
}
String title=null;
if ("".equals(urlRule.getTitlePositionAttr())){
title = titleElements.get(i).text();
}else {
title = titleElements.get(i).attr(urlRule.getTitlePositionAttr());
}
String synopsis=null;
if ("".equals(urlRule.getSynopsisPositionAttr())){
synopsis = synopsisElements.get(i).text();
}else {
synopsis = synopsisElements.get(i).attr(urlRule.getTitlePositionAttr());
}
searchResult.setId((id++)+"");
searchResult.setSiteName(urlRule.getSiteName());
searchResult.setFormUrl(urlRule.getUrlBase());
//根据url是否是绝对路径来组合判断
if (urlRule.getUrlAbs()){
searchResult.setUrl(url);
searchResult.setPictureUrl(pictureSrc);
}else {
String substring =
urlRule.getUrlBase().substring(0, urlRule.getUrlBase().length() - 1);
searchResult.setUrl(substring+url);
searchResult.setPictureUrl(substring+pictureSrc);
}
searchResult.setTitle(title.trim());
searchResult.setSynopsis(synopsis.replaceAll((char)12288+"",""));
ResultList.add(searchResult);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (response1!=null){
try {
//httpclient.close();
response1.close();
httpGet.releaseConnection();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return ResultList;
}
}
其他类先暂且省略,当项目趋近于完整时,在来更新。
接口测试
总结
目前功能还比较单一,后续将开发,文档也将持续更新。