所以爬取网页数据有两点问题:如何爬去数据,以及怎么实现翻页
爬取数据,首先要知道该网页的统一资源定位符,比如这个网页的统一资源定位符为:http://www.ceic.ac.cn/speedsearch?time=5&&page=1
源代码为:
该文件为HTML文件,程序中利用JSOUP外包进行解析
第二个问题就是怎么实现翻页,其实这些网页都有共性,如第二页数据的统一资源定位符为:http://www.ceic.ac.cn/speedsearch?time=5&&page=2
发现只是后面的page=?发生了变化,我们可以使用循环,但是循环到多少为止,代码中出现这样一行:
只要我们能读出53这个数,就知道了循环的次数了,程序是这样实现的:
最终爬取到了地震的数据,存于大文件夹下,resources\data.dat文件中
关于大型数据的处理,在java中一般由数据库来完成,但是由于本人能力有限,所以采用java和FORTRAN的混编,fortran是一种专门进行科学计算的高级语言,其实面向过程的语言呢,在科学计算领域具有很强的功能和较快的运算速度,实现java和FORTRAN的混编,借助VS+IVF软件,是微软的Visual Studio和Intel fortran,该软件可以将程序编译为可供其他程序调用的动态链接库文件,即dll文件:
实现Java与fortran的混编,需要用到jna-4.0.0.jar和jna.platform-4.0.0.jar文件
在java程序中的体现为:
该程序使用了柱状图来反映地震数据的相关结果,地震数据最好的成图方法是使用世界地图进行成像,但是本人能力有限,多次尝试也未能完成。
在该程序中使用了三维柱状图,调用了gnujaxp-1.0.0.jar、jcommon-1.0.16.jar、jfreechart-1.0.13.jar这三个外包,反映了近一年来世界地震的震级分布,在图中表现为在3-4级中间比较集中,地震学上定义,三级以上为有感地震,五级以上为破坏型地震,而对于八级以上地震更为罕见,破坏力极强,同时按照里氏震级分析,截至目前,世界上没有出现过九级以上地震,最大为智利8.9级地震,图中的第二幅图是列举我国地震最为频繁的四个省份,云南、西藏、新疆、四川,从图中可以看出新疆的近一年的地震次数最多,达到了210余次,四川次之,也有125次,而云南和西藏也同样在30次以上。如图所示:
由于本人能力有限,程序中存在的问题还是有很多,比如调用了Windows操作系统的dll文件,所以该程序不具有跨平台性,而且界面设计过于简单,同时在按下第一个按钮是,确保计算机连接网络,不然程序会报错,同时会停滞,希望在以后能完善这个程序。
package homework;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
/**
* 程序功能:该程序用于从中国地震台网中心爬取近一年的地震数据
* @author 陈浩
* @version 1.0
* */
public class URLdata {
public void getdata() throws Exception {
String str = "http://www.ceic.ac.cn/speedsearch?time=5&&page=";
//添加字符输出缓冲流
BufferedWriter bw = new BufferedWriter(new FileWriter("resources//data.dat"));
//定义统一资源标识符
URL url = null;
//定义字符缓冲类
StringBuffer sb = new StringBuffer();
//定义字节输入流
InputStream is = null;
//定义字符输入流,可修改编码表
InputStreamReader isr =null;
//定义字符输入缓冲流
BufferedReader bf =null;
//定义文件类
Document document = null;
Elements element =null;
url = new URL(str+""+1);
is = url.openStream();
isr = new InputStreamReader(is,"UTF-8");//使用"UTF-8"编码
bf = new BufferedReader(isr);
String line =null;
while((line = bf.readLine())!= null){
//System.out.println(line);
sb.append(line+"\n"); //数据写入缓冲流中
}
//使用Jsoup进行html的解析
document = Jsoup.parse(sb.toString()) ;
element = document.select("tr");//网页节点
for(int i=1;i<element.size();i++){
bw.write(element.get(i).text()); //将数据写入文件中
bw.newLine(); //写换行
bw.flush(); //刷新
//System.out.println();
}
Element ele = document.getElementById("pagenum");//网页节点
//目的为了读取翻页网页的数量,即该数据在很多相似的网页上
String string = ele.text().toString();
String[] arraystr = string.split(":");//分割字符串
Integer isign = new Integer(arraystr[2]);//字符变数组
int intsign = isign.intValue();
bf.close();
sb.delete(0, sb.length());
//利用上面读到的翻页数量,进行网页的循环读取
for(int sign=2; sign<=intsign; sign++){
url = new URL(str+""+sign);
is = url.openStream();
isr = new InputStreamReader(is,"UTF-8");
bf = new BufferedReader(isr);
line =null;
while((line = bf.readLine())!= null){
//System.out.println(line);
sb.append(line+"\n");
}
document = Jsoup.parse(sb.toString()) ;
element =document.select("tr");
for(int i=1;i<element.size();i++){
bw.write(element.get(i).text());
bw.newLine();
bw.flush();
//System.out.println(element.get(i).text());
}
bf.close();
sb.delete(0, sb.length());
}
bw.close();
System.out.println("已全部加载完");
}
}
package homework;
import java.awt.Font;
import java.awt.GridLayout;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
public class BarChart {
/**
* 程序功能:实现数据的绘图,本程序绘制了两张柱状图
*/
int[] barchart = new int[7];
/**
* 调用改方法实现绘图
* */
public static void Plot() {
JFrame frame=new JFrame("数据绘图");
frame.setLayout(new GridLayout(2,2));//设置网格布局
frame.add(new BarChart().getChartPanel1()); //添加ChartPanel类
frame.add(new BarChart().getChartPanel2());
frame.setBounds(200,20, 900, 700);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
ChartPanel frame1,frame2;
CategoryDataset dataset =null;
JFreeChart chart = null;
public BarChart(){
dataset = getDataSet1();//将获得的数据传递给CategoryDataset类的对象
chart = ChartFactory.createBarChart3D(
"近一年地震震级分布", // 图表标题
"地震震级", // 目录轴的显示标签
"地震数量", // 数值轴的显示标签
dataset, // 数据集
PlotOrientation.VERTICAL, // 图表方向:水平、垂直
true, // 是否显示图例(对于简单的柱状图必须是false)
false, // 是否生成工具
false // 是否生成URL链接
);
CategoryPlot plot=chart.getCategoryPlot();//获取图表区域对象
CategoryAxis domainAxis=plot.getDomainAxis(); //水平底部列表
domainAxis.setLabelFont(new Font("黑体",Font.BOLD,14)); //水平底部标题
domainAxis.setTickLabelFont(new Font("宋体",Font.BOLD,12)); //垂直标题
ValueAxis rangeAxis=plot.getRangeAxis();//获取柱状
rangeAxis.setLabelFont(new Font("黑体",Font.BOLD,15));
chart.getLegend().setItemFont(new Font("黑体", Font.BOLD, 15));
chart.getTitle().setFont(new Font("宋体",Font.BOLD,20));//设置标题字体
frame1=new ChartPanel(chart,true); //这里也可以用chartFrame,可以直接生成一个独立的Frame
dataset = getDataSet2();//将获得的数据传递给CategoryDataset类的对象
chart = ChartFactory.createBarChart3D(
"中国地震多发地区数据分析", // 图表标题
"省份", // 目录轴的显示标签
"地震数量", // 数值轴的显示标签
dataset, // 数据集
PlotOrientation.VERTICAL, // 图表方向:水平、垂直
true, // 是否显示图例(对于简单的柱状图必须是false)
false, // 是否生成工具
false // 是否生成URL链接
);
CategoryPlot plot1=chart.getCategoryPlot();//获取图表区域对象
CategoryAxis domainAxis1=plot1.getDomainAxis(); //水平底部列表
domainAxis1.setLabelFont(new Font("黑体",Font.BOLD,14)); //水平底部标题
domainAxis1.setTickLabelFont(new Font("宋体",Font.BOLD,12)); //垂直标题
ValueAxis rangeAxis1=plot1.getRangeAxis();//获取柱状
rangeAxis1.setLabelFont(new Font("黑体",Font.BOLD,15));
chart.getLegend().setItemFont(new Font("黑体", Font.BOLD, 15));
chart.getTitle().setFont(new Font("宋体",Font.BOLD,20));//设置标题字体
frame2=new ChartPanel(chart,true); //这里也可以用chartFrame,可以直接生成一个独立的Frame
}
/**
* 调用该方法实现从文件中获取近一年地震震级分布数据
* */
private CategoryDataset getDataSet1() {
BufferedReader bw =null;
try {
FileReader fr = new FileReader("resources\\summag.txt");
bw = new BufferedReader(fr);
int i=0;
String line = null;
while( (line = bw.readLine()) != null ){
//System.out.println(line);
Integer dd = new Integer(line);
barchart[i] = dd.intValue();
//System.out.println(barchart[i]);
i++;
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if(bw!=null)
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(barchart[0], "3级", "3");
dataset.addValue(barchart[1], "4级", "4");
dataset.addValue(barchart[2], "5级", "5");
dataset.addValue(barchart[3], "6级", "6");
dataset.addValue(barchart[4], "7级", "7");
dataset.addValue(barchart[5], "8级", "8");
dataset.addValue(barchart[6], "9级", "9");
return dataset; //返回dataset数据
}
/**
* 调用该方法实现从文件中获取中国地震多发地区数据
* */
private CategoryDataset getDataSet2() {
try {
FileReader fr = new FileReader("resources\\sumarea.txt");
BufferedReader bw = new BufferedReader(fr);
int i=0;
String line = null;
while( (line = bw.readLine()) != null ){
//System.out.println(line);
Integer dd = new Integer(line);
barchart[i] = dd.intValue();
//System.out.println(barchart[i]);
i++;
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(barchart[0], "云南", "云南");
dataset.addValue(barchart[1], "西藏", "西藏");
dataset.addValue(barchart[2], "新疆", "新疆");
dataset.addValue(barchart[3], "四川", "四川");
return dataset;
}
public ChartPanel getChartPanel1(){
return frame1;
}
public ChartPanel getChartPanel2(){
return frame2;
}
}
package homework;
import com.sun.jna.Library;
/**
* 接口功能:用于实现Java和FORTRAN的混编,Java调用动态链接库
* */
public interface DLL extends Library{
//定义一个抽象方法,该方法是在fortran程序下所写的子例行程序
public abstract void READDATA();
}
附上Fortran源代码:
!************************************
! 程序功能:将java程序从网络爬取的数据进行相关处理
!-------------------------------------------------------c
Subroutine readdata()
!DEC$ ATTRIBUTES DLLEXPORT::readdata
Implicit none
integer i,j
integer sum,sum3,sum4,sum5,sum6,sum7,sum8,sum9
integer sumyn,sumxz,sumxj,sumsc
integer intdatem,intdated
real,allocatable::emag(:),latitude(:),longtitude(:)
character*30,allocatable::area(:),date(:),time(:)
character*30 str,carea,cdate
sum=0
open(10,file='resources\data.dat',status='old')
do while(.not.eof(10))
read(10,*)
sum=sum+1
end do
rewind 10
allocate(emag(sum),latitude(sum),longtitude(sum),area(sum),date(sum),time(sum))
do i=1,sum
read(10,*) emag(i),date(i),time(i),latitude(i),longtitude(i),str,area(i)
end do
close(10)
sum3=0
sum4=0
sum5=0
sum6=0
sum7=0
sum8=0
sum9=0
do i=1,sum,1
if(emag(i)>=9) then
sum9=sum9+1
elseif(emag(i)>=8) then
sum8=sum8+1
elseif(emag(i)>=7) then
sum7=sum7+1
elseif(emag(i)>=6) then
sum6=sum6+1
elseif(emag(i)>=5) then
sum5=sum5+1
elseif(emag(i)>=4) then
sum4=sum4+1
else
sum3=sum3+1
end if
end do
sumyn=0
sumxz=0
sumxj=0
sumsc=0
do i=1,sum,1
carea=trim(adjustl(area(i)))
if(carea(1:4)=='云南') then
sumyn=sumyn+1
elseif(carea(1:4)=='西藏') then
sumxz=sumxz+1
elseif(carea(1:4)=='新疆') then
sumxj=sumxj+1
elseif(carea(1:4)=='四川') then
sumsc=sumsc+1
end if
end do
open(20,file='resources\summag.txt')
write(20,100) sum3
write(20,100) sum4
write(20,100) sum5
write(20,100) sum6
write(20,100) sum7
write(20,100) sum8
write(20,100) sum9
close(20)
100 format(g0)
open(30,file='resources\sumarea.txt')
write(30,100) sumyn
write(30,100) sumxz
write(30,100) sumxj
write(30,100) sumsc
close(30)
end subroutine
package homework;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.sun.jna.Native;
/**
* 程序功能:总程序的入口,用于进行GUI设计*/
public class MyFrame {
public MyFrame(){
//定义一个框架
final JFrame frame = new JFrame("中国地震台网信息实时获取系统");
frame.setSize(400, 300); //框架尺寸
frame.setBounds(300, 100, 600, 500); //边界范围
frame.setVisible(true); //可显示
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置框架关闭方式
JPanel panel = new JPanel(); //定义一个面板
panel.setLayout(null); //设置格局
//设置三个按钮
JButton button1 = new JButton("数据爬取");
button1.setBounds(50,70 , 100,50 );
button1.setFont(new Font("Dialog",1,15));
//第一个按钮设置监听器,用于数据爬取程序的开启
button1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
URLdata url = new URLdata();
try {
url.getdata();
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
JButton button2 = new JButton("数据预处理");
button2.setBounds(200,70 , 120,50 );
button2.setFont(new Font("Dialog",1,15));
//按钮2设置监听器,用于调用FORTRAN程序,进行数据预处理
button2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
DLL dll = (DLL) Native.loadLibrary("dlldata.dll", DLL.class);
dll.READDATA();
}
});
JButton button3 = new JButton("数据绘图");
button3.setBounds(370,70 , 100,50 );
button3.setFont(new Font("Dialog",1,15));
//按钮三设置监听器,用于进行绘图程序调用
button3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
BarChart.Plot();
}
});
panel.add(button1);
panel.add(button2);
panel.add(button3);
frame.add(panel);
}
@SuppressWarnings("unused")
public static void main(String[] args) {
MyFrame myframe = new MyFrame();
}
}
源代码文件和外包
提取码:pbhg