为了保持消息灵通,博主没事会上上校内论坛看看新帖,做为爬虫爱好者,博主萌生了写个爬虫自动下载的想法。java
嗯,此次就选Java。node
Jsoup是一款比较好的Java版HTML解析器。可直接解析某个URL地址、HTML文本内容。它提供了一套很是省力的API,可经过DOM,CSS以及相似于jQuery的操做方法来取出和操做数据。mysql
mysql-connector-java是java JDBC的MySQL驱动,能够提供方便统一的接口来操纵MySQL数据库sql
博主爬取的是浙大的cc98论坛,须要内网才能上,新帖会在其中一个版面内出现,界面大概是这样:数据库
分析一下以后,发现100条新帖一共有5页,内容呈如今一张表格上。cookie
再用Firefox分析一下页面,发现是一个class="tableborder1"的table下有20行记录,每一行有4个td,爬虫只要获取这四个td数据就能够了。ide
因为这个网站是要用用户名和密码登陆的,博主一开始都在使用POST方法,后来用Firefox抓包分析以后,才发现可使用带cookies的GET方法登陆。工具
private void getDoc(String url, String page) { try { //获取网页 this.doc = Jsoup.connect(url) .cookie("aspsky", "***") .cookie("BoardList","BoardID=Show") .cookie("owaenabled","True") .cookie("autoplay","True") .cookie("ASPSESSIONIDSSCBSSCR","***") .data(" stype","3") .data("page",page) //Page就是1-5页 .get(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(); } }
这里使用到的Jsoup很强大,其实还能够添加header之类的做修饰,但博主发现只要加了cookies以后就能成功访问了。学习
根据目标分析的结果,咱们能够开始解析HTML文档,一样地,Jsoup容许使用JQuery方法来解析,十分方便网站
private void parse() { //采用JQuery CSS选择 Elements rows = doc.select(".tableborder1 tr"); //去掉表头行 rows.remove(0); for(Element row : rows) { String theme = row.select("td:eq(0) a:eq(1)").text().trim(); String url = "http://www.cc98.org/" + row.select("td:eq(0) a:eq(1)").attr("href"); String part = row.select("td:eq(1) a").text().trim(); String author = row.select("td:eq(2) a").text().trim(); if(author.length()==0) { author = "匿名"; } String rawTime = row.select("td:eq(3)").text(). replace("\n","") .replace("\t",""); try { Date publishTime = sdf.parse(rawTime); System.out.println(publishTime+" "+theme); System.out.println("---------------------------------------------------------"); storage.store(theme,publishTime,part,author,url); } catch (ParseException e) { e.printStackTrace(); } } }
为了方便往后的分析(博主还打算偶尔分析一下各个版面的活跃状况),咱们要把数据存储到硬盘上,这里选用了jdbc链接MySQL
public void store(String theme, Date publishTime, String part, String author, String url) { try { String sql = "INSERT INTO news (theme," + "publishTime,part,author,url)VALUES(?,?,?,?,?)"; //使用预处理的方法 PreparedStatement ps = null; ps = conn.prepareStatement(sql); //依次填入参数 ps.setString(1,theme); java.sql.Time time = new java.sql.Time(publishTime.getTime()); //这里使用数据库的时间戳 ps.setTimestamp(2,new Timestamp(publishTime.getTime())); ps.setString(3,part); ps.setString(4,author); ps.setString(5,url); ps.executeUpdate(); } catch (SQLException e) { //主要是重复的异常,在MySQL中已经有约束unique // e.printStackTrace(); } }
咱们能够经过MySQL可视化工具查看结果,因为MySQLworkbench不太好用,博主使用了DBeaver,结果以下:
结果很是使人满意。
Spider.java
package com.company; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Spider { private Document doc; //定义时间格式 private SimpleDateFormat sdf = new SimpleDateFormat( " yyyy/MM/dd HH:mm " ); Storage storage = new Storage(); private void getDoc(String url, String page) { try { //获取网页 this.doc = Jsoup.connect(url) .cookie("aspsky", "***") .cookie("BoardList","BoardID=Show") .cookie("owaenabled","True") .cookie("autoplay","True") .cookie("ASPSESSIONIDSSCBSSCR","***") .data(" stype","3") .data("page",page) .get(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(); } } private void parse() { //采用JQuery CSS选择 Elements rows = doc.select(".tableborder1 tr"); //去掉表头行 rows.remove(0); for(Element row : rows) { String theme = row.select("td:eq(0) a:eq(1)").text().trim(); String url = "http://www.cc98.org/" + row.select("td:eq(0) a:eq(1)").attr("href"); String part = row.select("td:eq(1) a").text().trim(); String author = row.select("td:eq(2) a").text().trim(); if(author.length()==0) { author = "匿名"; } String rawTime = row.select("td:eq(3)").text(). replace("\n","") .replace("\t",""); try { Date publishTime = sdf.parse(rawTime); System.out.println(publishTime+" "+theme); System.out.println("---------------------------------------------------------"); storage.store(theme,publishTime,part,author,url); } catch (ParseException e) { e.printStackTrace(); } } } public void run(String url) { for (Integer i = 1; i<=5; i++) { getDoc(url, i.toString()); parse(); } storage.close(); } public static void main(String[] args) { Spider spider = new Spider(); spider.run("http://www.cc98.org/queryresult.asp?stype=3"); } }
Storage.java
package com.company; import java.sql.*; import java.util.Date; public class Storage { //数据库链接字符串,cc98为数据库名 private static final String URL="jdbc:mysql://localhost:3306/cc98?characterEncoding=utf8&useSSL=false"; //登陆名 private static final String NAME="***"; //密码 private static final String PASSWORD="***"; private Connection conn = null; public Storage() { //加载jdbc驱动 try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { System.out.println("未能成功加载驱动程序,请检查是否导入驱动程序!"); e.printStackTrace(); } try { conn = DriverManager.getConnection(URL, NAME, PASSWORD); // System.out.println("获取数据库链接成功!"); } catch (SQLException e) { // System.out.println("获取数据库链接失败!"); e.printStackTrace(); } } public void close() { //关闭数据库 if(conn!=null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } public void store(String theme, Date publishTime, String part, String author, String url) { try { String sql = "INSERT INTO news (theme," + "publishTime,part,author,url)VALUES(?,?,?,?,?)"; //使用预处理的方法 PreparedStatement ps = null; ps = conn.prepareStatement(sql); //依次填入参数 ps.setString(1,theme); java.sql.Time time = new java.sql.Time(publishTime.getTime()); //这里使用数据库的时间戳 ps.setTimestamp(2,new Timestamp(publishTime.getTime())); ps.setString(3,part); ps.setString(4,author); ps.setString(5,url); ps.executeUpdate(); } catch (SQLException e) { //主要是重复的异常,在MySQL中已经有约束unique // e.printStackTrace(); } } }
这个项目其实涉及的知识点还挺多的,博主也刚学java,不少细节也没有很好吃透,如JDBC、Jsoup都值得好好学习一下。欢迎你们批评指正。
另,祝你们中秋国庆双节快乐~