去年在接触Java爬虫的时候,接触到了一个关于知乎的爬虫。个人觉得写的非常好,当时抓取的效率和成功率还是特别特别高,现在可能知乎反扒做的更好,这个开源知乎爬虫没之前抓取的那么顺利了。我记得当时在我的i7+8g的机器上爬了将近两天,大概爬取了60多w的数据。当然,实际抓取的用户数据数量肯定比这个多,只是持久化过程不同步而已,也就是抓取的好几个用户可能只有一个存入数据库中。
最后,本文提供的知乎网名数据是2017年12月份左右抓取的数据。
60w知乎网名数据:
链接:https://pan.baidu.com/s/1pEcHAbRyKP7KGcgTuZfPVA 密码:l3so
项目源码地址(如果觉得 有帮助的话,欢迎给个Star.):
https://github.com/Snailclimb/J2ee-Advanced (Java Web进阶学习的一些源码加详细讲解)
细心的同学会发现,我其实只从数据库抓取了9条数据出来。因为我的SQL语句写错了(逃....),大家可以自己修改Mapper文件。
声明一下,笔主使用的是MyEclipse2016(主要是为了暑假做的项目的编码环境的统一,所以我选择了MyEclipse2016)。
需要的jar包,都在这里配置好。另外我配置了一个Tomcat插件,这样就可以通过Maven Build的方式来运行项目了。具体运行方式如下:
右键项目->run as -> Maven build
然后输入tomcat7:run后点击run即可
这里提一点:@ResponseBody注解要把对象转换成json格式,所以需要添加相关转换依赖的jar包(jackson)
pom.xml
4.0.0 spring ssm-echarts-demo war 0.0.1-SNAPSHOT ssm-echarts-demo Maven Webapp http://maven.apache.org 3.0 UTF-8 <!-- spring版本号 -->4.0.0.RELEASE <!-- log4j日志文件管理包版本 --><!-- 1.6.6 1.2.12 --><!-- mybatis版本号 -->3.1.1 <!-- aspectJ版本号 -->1.7.4 <!-- 数据库连接池版本号 -->1.4 1.6 <!-- jackson的版本号 json和xml解析 -->2.8.9 <!-- mybatis/spring包的版本号 -->1.1.1 <!-- mysql的版本号 -->5.1.29 <!-- jstl的版本号 -->1.2.1 <!-- 文件上传用的包 的版本号 -->1.3.1 2.4 <!-- 数据校验使用的包的版本号 -->2.0.0.Final 5.4.1.Final <!-- 单元测试 -->junit junit 4.12 test <!-- -->org.springframework spring-test 4.0.0.RELEASE test commons-logging commons-logging 1.1.3 commons-collections commons-collections 3.2.1 <!-- jstl -->javax.servlet jstl 1.2 <!-- mybatis -->org.mybatis mybatis ${mybatis.version} org.mybatis mybatis-spring ${mybatis-spring.version} mysql mysql-connector-java ${mysql.version} com.alibaba druid 0.2.23 <!-- spring -->org.springframework spring-core ${spring.version} org.aspectj aspectjrt ${aspectJ.version} org.aspectj aspectjweaver ${aspectJ.version} org.springframework spring-context ${spring.version} org.springframework spring-aop ${spring.version} org.springframework spring-tx ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-web ${spring.version} org.springframework spring-webmvc ${spring.version} org.springframework spring-context-support ${spring.version} <!-- Jackson -->com.google.code.gson gson 2.8.5 org.codehaus.jackson jackson-mapper-asl 1.9.13 com.alibaba fastjson 1.2.3 com.fasterxml.jackson.core jackson-annotations ${jackson.version} com.fasterxml.jackson.core jackson-core ${jackson.version} com.fasterxml.jackson.core jackson-databind ${jackson.version} <!-- 解决.jsp出错 -->javax.servlet javax.servlet-api 3.1.0 provided <!-- 连接池 -->commons-dbcp commons-dbcp ${commons-dbcp.version} commons-pool commons-pool ${commons-pool.version} <!-- 文件上传用的包 -->commons-fileupload commons-fileupload ${commons-fileupload.version} commons-io commons-io ${commons-io.version} <!-- 数据校验 -->javax.validation validation-api ${validation-api.version} org.hibernate hibernate-validator ${hibernate-validator.version} ssm-echarts-demo maven-compiler-plugin 2.3.2 1.8 1.8 org.apache.tomcat.maven tomcat7-maven-plugin / 8082 maven-war-plugin 2.6 false <!--资源文件打包 -->src/main/resources **/*.properties **/*.xml **/*.tld false src/main/java **/*.properties **/*.xml **/*.tld false 由于Spring配置较多所以我这里分了4层,分别是dao、service、transaction、web。
applicationContext-dao.xml
<!-- 加载数据库属性文件,为配置数据源做准备 --> <!-- bean采用注解式注入 --> <!-- 配置数据源 使用dbcp --> <!-- =================================MyBatis的整合====================================== --><!-- 配置mybatis的SqlSessionFactoryBean --><!-- 指定mybatis 全局配置文件的位置 --> <!-- mapper配置: mapper批量处理,从mapper包中扫描mapper接口,自动创建代理对象并且在spring容器中注册 遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录中自动扫描
出来的mapper的bean的id为mapper类名(首字母小写) --> applicationContext-service.xml
<!-- 用户管理service --> applicationContext-trans.xml:
<!--事务管理器 --> <!--通知 --> springmvc.xml
<!-- 配置扫描器:@Controller是@Component的一个子类。@service --> <!-- 配置注解器: --> <!-- 配置springMVC的视图解析器 --> <!-- 开启shiro注解功能 --><!--
--><!-- 全局异常配置 --><!-- --><!-- 文件上传 --><!-- 上传文件大小上限,单位为字节(10MB) -->10485760 <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->UTF-8 <!-- 配置校验器 ,使用的是hibernate框架的validate,但与hibernate无关 --><!-- 校验器,使用hibernate校验器 --> <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下面的ValidationMessages.properties文件 --> <!-- 校验错误信息配置文件 --><!-- 资源文件名 -->classpath:validateMessage
<!-- 资源文件编码格式 --> <!-- 对资源文件内容缓存时间,单位秒 --> Log4J的配置文件(Configuration File)就是用来设置记录器的级别、存放器和布局的,它可接key=value格式的设置或xml格式的设置信息。通过配置,可以创建出Log4J的运行环境。
log4j.properties
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%-5p] %c - %m%n db.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spider_zhihu_crawler
jdbc.username=root
jdbc.password=xxxxxx spring-mybatis.xml
<!-- 开启mybatis的缓存机制,开启二级缓存 --> <!-- mybatis中的别名命名 --> web.xml
ssm-echarts-demo index.html index.htm index.jsp default.html default.htm default.jsp <!-- springMVC 静态资源显示问题。使用服务器来处理静态资源。 原因:客户端发送request,springMVC有DispatcherServlet转发,而DiapatchServlet的url-pattern是“/”,代表着所有的请求都要由DispatcherServlet
转发,因此处理静态资源,第一种就采用服务器端处理,请求先过DefaultServlet,静态资源过滤则剩下的请求就交给dispatcherServlet处理。 -->default org.apache.catalina.servlets.DefaultServlet debug 0 listings false 1 <!-- 加载spring容器 ,如果没有配置这个话会出现service无法注入到controller层的情况 -->contextConfigLocation classpath:spring/applicationContext-*.xml org.springframework.web.context.ContextLoaderListener <!-- js静态资源 -->default *.js <!-- css静态资源 -->default *.css <!-- gif 静态资源 -->default *.gif <!-- jpg静态资源 -->default *.jpg <!-- ico -->default *.ico <!-- png -->default *.png <!-- htm -->default *.htm <!-- eot -->default *.eot <!-- svg -->default *.svg <!-- ttf -->default *.ttf <!-- woff -->default *.woff default *.json <!-- 配置springMVC的DispathServlet -->spring org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring/springmvc.xml 1 spring / <!-- 配置过滤器:CharacterEncodingFilter -->Encoding org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 Encoding * User.java
package com.snailclimb.bean;
/**
* 用户实体类
*
* @author Snailclimb
*
*/
public class User {
private Integer id;
private String userToken;
private String location;
private String business;
private String sex;
private String employment;
private String education;
private String username;
private String url;
private Integer agrees;
private Integer thanks;
private Integer asks;
private Integer answers;
private Integer posts;
private Integer followees;
private Integer followers;
private String hashid;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserToken() {
return userToken;
}
public void setUserToken(String userToken) {
this.userToken = userToken == null ? null : userToken.trim();
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location == null ? null : location.trim();
}
public String getBusiness() {
return business;
}
public void setBusiness(String business) {
this.business = business == null ? null : business.trim();
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex == null ? null : sex.trim();
}
public String getEmployment() {
return employment;
}
public void setEmployment(String employment) {
this.employment = employment == null ? null : employment.trim();
}
public String getEducation() {
return education;
}
public void setEducation(String education) {
this.education = education == null ? null : education.trim();
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url == null ? null : url.trim();
}
public Integer getAgrees() {
return agrees;
}
public void setAgrees(Integer agrees) {
this.agrees = agrees;
}
public Integer getThanks() {
return thanks;
}
public void setThanks(Integer thanks) {
this.thanks = thanks;
}
public Integer getAsks() {
return asks;
}
public void setAsks(Integer asks) {
this.asks = asks;
}
public Integer getAnswers() {
return answers;
}
public void setAnswers(Integer answers) {
this.answers = answers;
}
public Integer getPosts() {
return posts;
}
public void setPosts(Integer posts) {
this.posts = posts;
}
public Integer getFollowees() {
return followees;
}
public void setFollowees(Integer followees) {
this.followees = followees;
}
public Integer getFollowers() {
return followers;
}
public void setFollowers(Integer followers) {
this.followers = followers;
}
public String getHashid() {
return hashid;
}
public void setHashid(String hashid) {
this.hashid = hashid == null ? null : hashid.trim();
}
} ScoreResult.java
package com.snailclimb.bean;
/**
* 圆饼图展示的时候需要使用到的对象
*
* @author Snailclimb
*
*/
public class ScoreResult {
public int value; // 点赞数
public String name;// 人名
public float getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ScoreResult(int value, String name) {
super();
this.value = value;
this.name = name;
}
@Override
public String toString() {
return "ScoreResult [value=" + value + ", name=" + name + "]";
}
}
UserMapper.java
package com.snailclimb.dao;
import java.util.List;
import com.snailclimb.bean.User;
public interface UserMapper {
public List selecAgreesTop10();
}
UserMapper.xml
UserService.java
package com.snailclimb.service;
import java.util.List;
import com.snailclimb.bean.User;
public interface UserService {
public List selecAgreesTop10();
}
UserServiceImpl.java
package com.snailclimb.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.snailclimb.bean.User;
import com.snailclimb.dao.UserMapper;
import com.snailclimb.service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List selecAgreesTop10() {
return userMapper.selecAgreesTop10();
}
}
package com.snailclimb.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.snailclimb.bean.ScoreResult;
import com.snailclimb.bean.User;
import com.snailclimb.service.UserService;
@Controller
@RequestMapping(value = "/echarts")
public class UserController {
@Autowired
private UserService userService;
/**
* 折线图和直方图
*
* @return
*/
@RequestMapping(value = "/agreeLineAndBar")
@ResponseBody
public List getAgreesTop10() {
return userService.selecAgreesTop10();
}
/**
* 圆饼图
*
* @return
*/
@RequestMapping(value = "/agreePie")
@ResponseBody
public List getAgreesTop10T() {
List list = userService.selecAgreesTop10();
List results = new ArrayList();
for (User user : list) {
ScoreResult scoreResult = new ScoreResult(user.getAgrees(), user.getUsername());
results.add(scoreResult);
}
return results;
}
// 跳转到圆饼图展示
@RequestMapping(value = "/intoagreePie")
public String intoagreePie() {
return "agreePie";
}
// 跳转到折线图和直方图
@RequestMapping(value = "/intoagreeLineAndBar")
public String Index() {
return "agreeLineAndBar";
}
} 由于JSP页面代码过多,大家可以直接去我上传在Github的源码上拷贝。下面我只贴一下Ajax请求的代码。
下面以圆饼图为例,看看如何通过Ajax请求获取数据动态填充
<!-- 显示Echarts图表 -->
对应的Controller层代码:
/**
* 圆饼图
*/
@RequestMapping(value = "/agreePie")
@ResponseBody
public List getAgreesTop10T() {
//查询数据
List list = userService.selecAgreesTop10();
List results = new ArrayList();
//将数据添加到results中
for (User user : list) {
ScoreResult scoreResult = new ScoreResult(user.getAgrees(), user.getUsername());
results.add(scoreResult);
}
//返回results
return results;
}
后台返回都是JSON格式的数据,如下图所示:
这里只是以知乎赞同数TOP10为例子,带着大家学习了SSM环境的搭建以及代码的编写,代码中有很详细的注释。
通过本例子,大家完全可以自己做一个知乎粉丝数TOP、知乎感谢数TOP10等等例子出来。
另外本例子知识演示了圆饼图、折线图、柱状图的使用,大家可以自己去Echarts官网深入学习。
最后,本项目只是一个演示,还有很多需要优化的地方。比如可以使用redis来做缓存提高查询速度、可以创建索引提高查询速度或者直接将查询到的数据缓存下来等等方法来提高查询速度。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/4383/viewspace-2805244/ ,如需转载,请注明出处,否则将追究法律责任。