在上次的博文中提到,整个爬虫的一个整体流程现在就逐步来分析和实现我们的需求。
第一。根据URL爬取响应的整个HTML页面。 第二。从HTML提取自己想要的东西。
着这是主要的也是最基本的思路,下面我们就把每个过程都细分下,把可能出现的问题都列出来。
(1)URL的去重(已经爬过的URL,不在爬取第二次)
(2)返回的状态码分析。200、300、400、500等
(3)爬取内容的编码问题。
(4)爬取的内容包括:meta 、 head 、 body
(5)网页内容的去标签
(6)网页去噪并提取正文:包括广告,版权,特殊字符,导航等等。
(7)网页请求超时。
(8)网页的有效性。无效页面包括:空白页,登陆页,动态图片,欢迎页面等等
(9)确定网页采集的频率。哪些网页更新快,哪些网页更新慢。
(10)网页采集过快封号,封IP问题
(11)需要解决登录的网站
因为url对应的html是各种各样的,我们在实现之前要想到可能出现的问题,(不一定全面,可能在实际过程中可能还会遇到新的问题,当遇到新的问题时,我们就要把这个新问题列入上面的问题中)列出这些问题我们为什么先不考虑呢,呵呵。这就是我提到的框架,先不要管什么问题,先把流程走通,也就是说先找到一些不是乱码的正规网页,不能登录,不存在任何问题的网页进行实验,若把整个流程都弄通过了那么整个思路就有了,然后在去解决上面一个一个的问题。
网络爬虫,其实就是模仿浏览器去请求服务器。然后处理服务器响应的结果。
既然要模仿浏览器去请求,而浏览器和服务器之间进行工作时要遵守一种协议,那就是HTTP协议,所以搞互联网的话和网络爬虫的话,必须的把HTTP网络协议铭记于心。
为什么要学习HTTP呢,开始我也不懂什么是HTTP但是后来发现它的重要性,在你请求页面是,返回的状态嘛很重要,只有返回状态嘛是200的或者300(需要进行下处理)你才能去接受以及分析它的响应结果,这样1.可以防止不去处理那些由于服务器的原因响应失败或者错误页面。2.可以更好的处理你想要的结果,因为大家都知道服务器响应请求结果有很多类型。比如说纯HTML的文本格式,视频格式,图片等等,如果你只是想要纯文本的HTNL那么就不需要处理非HTML格式的响应结果。所以无论是互联网还是爬虫对HTTP协议的理解和认识非常重要。
给大家介绍下一个博客详细的解释了HTTP协议的内容。写的很错了,建议大家仔细看看 http://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html
下面说下HttpClient这个工具包。http://hc.apache.org/httpclient-3.x/ 官网
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
废话不多说,HTTP协议和HttpClient的介绍和详细内容网上都有,下面来例子(给大家介绍个httpclient的入门级出,自我感觉写的不错 http://www.ibm.com/developerworks/cn/opensource/os-httpclient/)
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.StatusLine;
import org.apache.commons.httpclient.methods.GetMethod;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created with IntelliJ IDEA.
* User: ninja
* Date: 11/5/13
* Time: 12:43 PM
* To change this template use File | Settings | File Templates.
*/
public class Spider {
public static Map<String,String> getHTML(String url) {
Map<String,String> map = new HashMap<String, String>();
StringBuffer HTMLstr = new StringBuffer();
String markCode = null; //响应编码,A表示是正常的。其他都表示有误
String HTML = null; //响应结果HTML
BufferedReader reader = null;
HttpMethod get = null;
try{
HttpClient httpClient = new HttpClient(); // 创建对象
// 连接超时:
// httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(1000); //请求超时处理,表示请求超时超过1秒这抛出异常
// 读取超时:
httpClient.getHttpConnectionManager().getParams().setSoTimeout(3000);//响应超时处理,响应超过3000抛出异常
get = new GetMethod(url); // get方法请求URL
try {
httpClient.executeMethod(get);
} catch (IOException e){
markCode = "B"; //"执行GET方法报IO错"
System.out.println("在执行GET方法时出错。。。。。。。。");
// e.printStackTrace();
}
//执行
int code = get.getStatusCode(); //响应状态码
StatusLine line = get.getStatusLine();
System.out.println("响应状态:------ " + line.toString());
System.out.println("URL为:------" + url + "<begin>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
if (code == 200) { // 响应为200时表示成功
String contentType = get.getResponseHeader("Content-Type").getValue();
if(contentType != null && !"".equals(contentType) && contentType.indexOf("text/html") != -1){
String reg_code = "<meta[^>]*?charset=[\"']?([0-9a-zA-Z-]+[\\W]*?)[\"']*[\\s*]?[/]?>";//编码的正则表达式
reader = new BufferedReader(new InputStreamReader(get.getResponseBodyAsStream(),"ISO-8859-1"));
String temp = null;
String encode = "UTF-8";
boolean flag = true;
while ((temp = reader.readLine()) != null){
if (flag){
Pattern p_code = Pattern.compile(
reg_code, Pattern.CASE_INSENSITIVE);
Matcher m_code = p_code.matcher(temp);
while (m_code.find()) {
encode = m_code.group(1);
flag = false;
}
}
HTMLstr.append(temp + "\r\n");
}
if(HTMLstr.toString() != null && !"".equals(HTMLstr)){
markCode = "A";
HTML = new String(HTMLstr.toString().getBytes("ISO-8859-1"),encode);
map.put("HTML",HTML);
} else {
markCode = "F";
System.out.println("读出的页面是个空页面。。。。。。。。");
}
} else{
markCode = "D"; //响应类型不属于text/html
System.out.println("响应类型不为text/html形式。。。。。。。。:" + contentType);
}
} else{
markCode = "E"; //响应状态码不为200
System.out.println("响应状态码错误 : " + code);
}
} catch (Exception e){
markCode = "C"; //响应超时或者网络不好,以及其他BUG
System.out.println("请求或者响应超时。。。。。。。。");
//e.printStackTrace();
} finally {
if(reader != null){
try {
reader.close();
} catch (Exception e){
e.printStackTrace();
}
}
if(get != null){
get.releaseConnection();
}
}
map.put("markCode",markCode);
return map;
}
public static void main(String[] args){
long a = System.currentTimeMillis();
System.out.println(getHTML("http://3g.163.com/sports/13/1015/13/9B7VASNM00051CD5.html").toString());
System.out.println(System.currentTimeMillis() - a);
}
}
下篇博文我将重点结合上诉代码以及爬虫的问题跟大家分享下自己的思路的处理方案。可能讲的不好,也可能有问题,希望大家多多指教,我们共同进步。如果有什么想知道的或者对代码有疑问的地方等等问题,可以留言告诉我,也可以发邮件给我 zhouxiaoming2013@163.com