目录:
1、爬虫原理
2、本地文件数据提取及分析
3、单网页数据的读取
4、运用正则表达式完成超连接的连接匹配和提取
5、广度优先遍历,多网页的数据爬取
6、多线程的网页爬取
7、总结
爬虫实现原理
网络爬虫基本技术处理
网络爬虫是数据采集的一种方法,实际项目开发中,通过爬虫做数据采集一般只有以下几种情况:
1) 搜索引擎
2) 竞品调研
3) 舆情监控
4) 市场分析
网络爬虫的整体执行流程:
1) 确定一个(多个)种子网页
2) 进行数据的内容提取
3) 将网页中的关联网页连接提取出来
4) 将尚未爬取的关联网页内容放到一个队列中
5) 从队列中取出一个待爬取的页面,判断之前是否爬过。
6) 把没有爬过的进行爬取,并进行之前的重复操作。
7) 直到队列中没有新的内容,爬虫执行结束。
这样完成爬虫时,会有一些概念必须知道的:
1) 深度(depth):一般来说,表示从种子页到当前页的打开连接数,一般建议不要超过5层。
2) 广度(宽度)优先和深度优先:表示爬取时的优先级。建议使用广度优先,按深度的层级来顺序爬取。
Ⅰ 在进行网页爬虫前,我们先针对一个飞机事故失事的文档进行数据提取的练习,主要是温习一下上一篇的java知识,也是为了下面爬虫实现作一个热身准备。
首先分析这个文档,,关于美国历来每次飞机失事的数据,包含时间地点、驾驶员、死亡人数、总人数、事件描述,一共有12列,第一列是标题,下面一共有5268条数据。
现在我要对这个文件进行数据提取,并实现一下分析:
根据飞机事故的数据文档来进行简单数据统计。
1) 哪年出事故次数最多
2) 哪个时间段(上午 8 – 12,下午 12 – 18,晚上 18 – 24,凌晨 0 – 8 )事故出现次数最多。
3) 哪年死亡人数最多
4)哪条数据的幸存率最高。
代码实现:(一切知识从源码获取!)
1 package com.plane;
2
3 import java.io.*;
4 import java.text.ParseException;
5 import java.text.SimpleDateFormat;
6 import java.util.*;
7 /**
8 * 飞机事故统计
9 * @author k04
10 *sunwengang
11 *2017-08-11
12 */
13 public class planeaccident {
14 //数据获取存取链表
15 private static List<String> alldata=new ArrayList<>();
16
17 public static void main(String args[]){
18 getData("飞行事故数据统计_Since_1908.csv");
19 alldata.remove(0);
20 //System.out.println(alldata.size());
21 //死亡人数最多的年份
22 MaxDeadYear();
23 //事故发生次数最多的年份
24 MaxAccidentsYear();
25 //事故各个时间段发生的次数
26 FrequencyPeriod();
27 //幸村率最高的一条数据
28 MaximumSurvival();
29 }
30
31 /**
32 * 从源文件爬取数据
33 * getData(String filepath)
34 * @param filepath
35 */
36 public static void getData(String filepath){
37 File f=new File(filepath);
38 //行读取数据
39 try{
40 BufferedReader br=new BufferedReader(new FileReader(f));
41 String line=null;
42 while((line=(br.readLine()))!=null){
43 alldata.add(line);
44 }
45 br.close();
46 }catch(Exception e){
47 e.printStackTrace();
48 }
49 }
50 /**
51 * 记录每年对应的死亡人数
52 * @throws
53 * 并输出死亡人数最多的年份,及该年死亡人数
54 */
55 public static void MaxDeadYear(){
56 //记录年份对应死亡人数
57 Map<Integer,Integer> map=new HashMap<>();
58 //时间用date显示
59 SimpleDateFormat sdf=new SimpleDateFormat("MM/dd/YYYY");
60 //循环所有数据
61 for(String data:alldata){
62 //用逗号将数据分离,第一个是年份,第11个是死亡人数
63 String[] strs=data.split(",");
64 if(strs[0]!=null){
65 //获取年份
66 try {
67 Date date=sdf.parse(strs[0]);
68 int year=date.getYear();
69 //判断map中是否记录过这个数据
70 if(map.containsKey(year)){
71 //已存在,则记录数+该年死亡人数
72 map.put(year, map.get(year)+Integer.parseInt(strs[10]));
73 }else{
74 map.put(year, Integer.parseInt(strs[10]));
75 }
76
77 } catch (Exception e) {
78 // TODO Auto-generated catch block
79
80 }
81
82 }
83 }
84 //System.out.println(map);
85
86 //记录死亡人数最多的年份
87 int max_year=-1;
88 //记录死亡人数
89 int dead_count=0;
90 //用set无序获取map中的key值,即年份
91 Set<Integer> keyset=map.keySet();
92 //
93 for(int year:keyset){
94 //当前年事故死亡最多的年份,记录年和次数
95 if(map.get(year)>dead_count&&map.get(year)<10000){
96 max_year=year;
97 dead_count=map.get(year);
98 }
99 }
100
101 System.out.println("死亡人数最多的年份:"+(max_year+1901)+" 死亡人数:"+dead_count);
102 }
103 /**
104 * 记录事故次数最多的年份
105 * 输出该年及事故次数
106 */
107 public static void MaxAccidentsYear(){
108 //存放年份,该年的事故次数
109 Map<Integer,Integer> map=new HashMap<>();
110 SimpleDateFormat sdf =new SimpleDateFormat("MM/dd/YYYY");
111 //循环所有数据
112 for(String data:alldata){
113 String[] strs=data.split(",");
114 if(strs[0]!=null){
115 try {
116 Date date=sdf.parse(strs[0]);
117 //获取年份
118 int year=date.getYear();
119 //判断是否存在记录
120 if(map.containsKey(year)){
121 //已存在记录,+1
122 map.put(year, map.get(year)+1);
123 }else{
124 map.put(year, 1);
125 }
126 } catch (Exception e) {
127 // TODO Auto-generated catch block
128 }
129 }
130 }
131 //记录事故次数最多的年份
132 int max_year=0;
133 //该年事故发生次数
134 int acc_count=0;
135 //循环所有数据,获取事故次数最多的年份
136 Set<Integer> keyset=map.keySet();
137 for(int year:keyset){
138 if(map.get(year)>acc_count){
139 max_year=year;
140 acc_count=map.get(year);
141 }
142 }
143 //输出结果
144 System.out.println("事故次数最多的年份"+(max_year+1901)+" 该年事故发生次数:"+acc_count);
145 }
146 /**
147 * FrequencyPeriod()
148 * 各个时间段发生事故的次数
149 */
150 public static void FrequencyPeriod(){
151 //key为时间段,value为发生事故次数
152 Map<String,Integer> map=new HashMap<>();
153 //String数组存放时间段
154 String[] strsTime={"上午(6:00~12:00)","下午(12:00~18:00)","晚上(18:00~24:00)","凌晨(0:00~6:00)"};
155 //小时:分钟
156 SimpleDateFormat sdf=new SimpleDateFormat("HH:mm");
157
158 for(String data:alldata){
159 String[] strs=data.split(",");
160 //判断时间是否记录,未记录则忽略
161 if(strs[1]!=null){
162 try {
163 Date date=sdf.parse(strs[1]);
164 //取得小时数
165 int hour=date.getHours();
166 //判断小时数在哪个范围中
167 int index=0;
168 if(hour>=12&&hour<18){
169 index=1;
170 }else if(hour>=18){
171 index=2;
172 }else if(hour<6){
173 index=3;
174 }
175 //记录到map中
176 if(map.containsKey(strsTime[index])){
177 map.put(strsTime[index], map.get(strsTime[index])+1);
178 }else{
179 map.put(strsTime[index], 1);
180 }
181 } catch (ParseException e) {
182 }
183 }
184
185 }
186 /*
187 System.out.println("各时间段发生事故次数:");
188 for(int i=0;i<strsTime.length;i++){
189 System.out.println(strsTime[i]+" : "+map.get(strsTime[i]));
190 }
191 */
192 // 记录出事故最多的时间范围
193 String maxTime = null;
194 // 记录出事故最多的次数
195 int maxCount = 0;
196
197 Set<String> keySet = map.keySet();
198 for (String timeScope : keySet) {
199 if (map.get(timeScope) > maxCount) {
200 // 当前年就是出事故最多的年份,记录下年和次数
201 maxTime = timeScope;
202 maxCount = map.get(timeScope);
203 }
204 }
205 System.out.println("发生事故次数最多的时间段:");
206 System.out.println(maxTime+" : "+maxCount);
207 }
208 /**
209 * 获取幸村率最高的一条数据的内容
210 * 返回该内容及幸存率
211 */
212 public static void MaximumSurvival(){
213 //存放事故信息以及该事故的幸村率
214 Map<String,Float> map=new HashMap<>();
215 //SimpleDateFormat sdf =new SimpleDateFormat("MM/dd/YYYY");
216 //事故幸存率=1-死亡率,第十一个是死亡人数,第十个是总人数
217 float survial=0;
218 //循环所有数据
219 for(String data:alldata){
220 try{
221 String[] strs=data.split(",");
222 //计算幸存率
223 float m=Float.parseFloat(strs[10]);
224 float n=Float.parseFloat(strs[9]);
225 survial=1-m/n;
226 map.put(data, survial);
227 }catch(Exception e){
228
229 }
230 }
231 //记录事故次数最多的年份
232 float max_survial=0;
233 //幸存率最高的数据信息
234 String this_data="null";
235 //循环所有数据,获取事故次数最多的年份
236 Set<String> keyset=map.keySet();
237 for(String data:keyset){
238 if(map.get(data)>max_survial){
239 this_data=data;
240 max_survial=map.get(data);
241 }
242 }
243 System.out.println("幸存率最高的事故是:"+this_data);
244 System.out.println("幸存率为:"+survial);
245 }
246 }
Ⅱ 接下来我们就可以在网页的数据上下手了。
下面先实现一个单网页数据提取的功能。
使用的技术可以有以下几类:
1) 原生代码实现:
a) URL类
2) 使用第三方的URL库
a) HttpClient库
3) 开源爬虫框架
a) Heritrix
b) Nutch