基于HtmlParser的网络爬虫

一、 目标  

      获取网页中的超链接及链接名,如从http://www.hao123.com/开始,抓取所有hao123链接到的超链接,再以获取到的链接网页为目标,获取它所链接到的网页。

 

二、环境及开发工具

 环境:Java

工具:MyEclipse

开发包:如图

三、 原理

     网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。

     而htmlparser能够很容易地提取到网页的信息,例如对HTML进行有效信息搜索、链接提取、用于自动给页面的链接文本加上链接的标签 、资源提取,例如对一些图片、声音的资源的处理,将网页内容保存到本地等等,极大地方便开发。

 

四、类分析,如下图:

各类说明:

FileNameList:内部用linkedlist存放解析出来的xml文件名即每一个链接的text

IndexsList: 用链接来记录索引,即是每一层的层数

LinksList: 记录解析过程中的超链接,

Parse:主要解析功能类

ParseTool:主要负责获得并初始化解析的参数

ResultBean:封装要解析的数据,方便写入xml文件中

UserCase:提供一个用例样例

WritetoXML:把每一层的解析结果写入xml文件中

 

五、类实现如下:

1.FileNameList 

 1 /**
 2  * 
 3  */
 4 package cn.guet.deep;
 5 
 6 import java.net.URL;
 7 import java.util.LinkedList;
 8 
 9 /**
10  * @author 梁渝铭
11  * @project name:HTMLParser
12  * @date:2011-11-24
13  */
14 public class FileNameList {
15 
16     /*
17      * 用链接来记录xml文件名
18      */
19     private static LinkedList<String> links = new LinkedList<String>();
20     private static FileNameList fileNameList = new FileNameList();
21     private static int index = 0;
22     // 控制空链接名时,用来生成xml文件名
23     private static int flag = 0;
24 
25     private FileNameList() {
26 
27     }
28 
29     public static FileNameList getInstance() {
30         return fileNameList;
31     }
32 
33     /*
34      * 入队列操作,检查链接名是否为空
35      */
36     public void enQueue(String name) {
37         // 检查文件名不合法时处理
38         if(links.contains(name)){
39             links.add(name + flag++);
40             return;
41         }
42         if (name != null) {
43             links.addLast(name);
44         }else {
45             links.addLast(("index" + flag++));
46         }
47     }
48 
49     public String next() {
50         return links.get(index++);
51     }
52 
53     public void free() {
54         links.clear();
55     }
56     
57 }

2.IndexsList 

 1 /**
 2  * 
 3  */
 4 package cn.guet.deep;
 5 
 6 import java.util.LinkedList;
 7 
 8 /**
 9  * @author 梁渝铭
10  * @project name:HTMLParser
11  * @date:2011-11-21
12  * 记录每一层的每一个file的索引
13  */
14 public class IndexsList {
15     
16     /*用链接来记录索引
17      */
18     private static LinkedList<Integer> indexList = new LinkedList<Integer>();
19     private static IndexsList indexInstance = new IndexsList();
20     //控制队列出队
21     private static int flag = 0;
22     
23     private IndexsList(){
24         
25     }
26     
27     public static IndexsList getInstance(){
28         return indexInstance;
29     }
30     
31     public int next(){
32         return indexList.get(flag++);
33     }
34 
35     /*入队列操作
36      */
37     public void enQueue(int index){
38         indexList.addLast(index);
39     }
40     
41     public int get(int index){
42         return indexList.get(index);
43     }
44     
45     public void free(){
46         indexList.clear();
47     }
48 }

3.LinksList 

 1 /**
 2  * 
 3  */
 4 package cn.guet.deep;
 5 
 6 import java.util.HashSet;
 7 import java.util.LinkedList;
 8 import java.util.Set;
 9 
10 /**
11  * @author 梁渝铭
12  * @project name:HTMLParser
13  * @date:2011-11-21 记录解析过程中的超链接
14  */
15 public class LinksList {
16 
17     /*
18      * 用链接来记录队列
19      */
20     private static LinkedList<String> links = new LinkedList<String>();
21     private static LinksList linksInstance = new LinksList();
22     //记录已经访问了的url,防止重复访问
23     private static Set<String> visitedSet = new HashSet<String>();
24     //单例
25     private LinksList() {
26 
27     }
28 
29     public static LinksList getInstance() {
30         return linksInstance;
31     }
32 
33     /*
34      * 入队列操作
35      */
36     public void enQueue(String url) {
37         // 先入队,确保每个url只被访问一次,去掉链接最后的'/'
38 /*        String url_temp = url;
39         if (url.endsWith("/")) {
40             url_temp = url.substring(0, url.lastIndexOf("/"));
41         }
42         visitedSet.add(url_temp);
43         if (links.isEmpty() || !visitedSet.contains(url_temp))*/
44         if(!visitedSet.contains(url))
45             links.addLast(url);
46     }
47 
48     public String get(int index) {
49         return links.get(index);
50     }
51 
52     public String next() {
53         String link = links.getFirst();
54         links.removeFirst();
55         return link;
56     }
57     
58     public void free(){
59         links.clear();
60     }
61     
62 }

4.Parse 

  1 /**
  2  * 
  3  */
  4 package cn.guet.deep;
  5 
  6 import java.io.IOException;
  7 import java.util.LinkedList;
  8 
  9 import org.apache.log4j.Logger;
 10 import org.htmlparser.NodeFilter;
 11 import org.htmlparser.Parser;
 12 import org.htmlparser.filters.NodeClassFilter;
 13 import org.htmlparser.tags.LinkTag;
 14 import org.htmlparser.util.NodeList;
 15 import org.htmlparser.util.ParserException;
 16 
 17 
 18 /**
 19  * @author 梁渝铭
 20  * @project name:HTMLParser
 21  * @date:2011-11-21 主要解析类
 22  */
 23 public class Parse {
 24     private Logger logger = Logger.getLogger(this.getClass());
 25     /*
 26      * 初始化参数
 27      */
 28     private static LinksList linksList = LinksList.getInstance();
 29     private static IndexsList indexsList = IndexsList.getInstance();
 30     private static WritetoXML writetoXML = new WritetoXML();
 31     private static FileNameList fileNameList = FileNameList.getInstance();
 32     private static int floor = 0;
 33     private static int floor_count = 0;
 34     //每一个超链接解析出来的超链接的总数
 35     private static int len = 0;
 36     //xml使用的文件名
 37 
 38     private static String path = null;
 39     private static int count = 0;
 40     public Parse(){
 41         indexsList.enQueue(1);
 42     }
 43     
 44     public int getFloor() {
 45         return floor;
 46     }
 47 
 48     public void setFloor(int floor) {
 49         Parse.floor = floor;
 50     }
 51 
 52     public void setUrl(String url) {
 53         fileNameList.enQueue("index");
 54         linksList.enQueue(url);
 55     }
 56 
 57     public void setPath(String path) {
 58         Parse.path = path;
 59     }
 60 
 61     //释放资源
 62     public void free(){
 63         try {
 64              linksList.free();
 65              logger.info("linksList had released...");
 66              indexsList.free();
 67              logger.info("linksList had released...");
 68              fileNameList.free();
 69              logger.info("linksList had released...");
 70              logger.info("all had released...");
 71         } catch (Exception e) {
 72            logger.error(e.getMessage());
 73         }
 74     }
 75 
 76     // 补全url的地址
 77     public String fillUrl(String domain,String url) {
 78         return url.indexOf("http://") != -1 ? url : domain + url;
 79     }
 80     //替换文件名不支持的字符
 81     public String replaceSpe(String link_name){
 82         link_name = link_name.replaceAll("[?]+", "")
 83         .replaceAll("[&nbsp;]+", "")
 84         .replaceAll("[&amp;]+", "")
 85         .replaceAll("[&lt;]+", "")
 86         .replaceAll("[&gt;]+", "")
 87         .replaceAll("[ ]+", "-");
 88         return link_name;
 89     }
 90     /*
 91      * 解析类,采用NodeFilter过滤
 92      */
 93     public void extractLinks() throws IOException {
 94         try {
 95             while(floor != 0) {
 96                 int temp = indexsList.next();
 97                 floor_count++;
 98                 //统计每一层的文件数
 99                 int file_count = 0;
100                 for (int i = 0; i < temp; i++) {    
101                     logger.info("该层的文件总数:"+temp);
102                     //外层循环控制每一次有的文件数    
103                     file_count++;
104                     //每层中的每一个xml文件对应的结果集
105                     LinkedList<ResultBean> resultList = new LinkedList<ResultBean>();;            
106                     NodeFilter filter = new NodeClassFilter(LinkTag.class);
107                     Parser parse = new Parser();
108                     String url = linksList.next();            
109                     try {
110                         //使解析跳过异常,如500,403,404。。。造成解析异常而中止的链接
111                         //让parser继续解析
112                         logger.info("try parse.....");
113                         parse.setURL(url);
114                         parse.setEncoding(parse.getEncoding());
115                         logger.info("set Encoding....");
116                     } catch (Exception e) {
117                         logger.error(e.getMessage());
118                         e.printStackTrace();
119                         file_count--;
120                         continue;
121                     }
122                     //内层循环控制每一层中的由上一层链接解析出来的链接
123                     logger.info("before extract list...."+parse.getURL());
124                     NodeList list;
125                     try{
126                        list = parse.extractAllNodesThatMatch(filter);
127                     }catch(ParserException e){
128                         e.printStackTrace();
129                         continue;
130                     }
131                     logger.info("extracting ....");
132                     for(len=0;len<list.size();len++){
133                        LinkTag node = (LinkTag) list.elementAt(len);
134                        String link_name = replaceSpe(node.getLinkText());
135                        String link = fillUrl(url,node.extractLink());
136                       //封装结果,并写入xml文件中
137                        ResultBean resultBean = new ResultBean();  
138                        resultBean.setName(link_name);
139                        resultBean.setLink(link);
140                        resultList.add(resultBean); 
141                        fileNameList.enQueue(link_name);
142                        linksList.enQueue(link);        
143                        logger.info("第"+(count++)+"个链接, "+"第" +floor_count+"层:"+"第"+file_count+"个文件"+"name: " +node.getLinkText()+"link to: "+link);
144                     }
145                     indexsList.enQueue(len+1);
146                     try{
147                        writetoXML.writeToXML(path, floor_count,fileNameList.next(), resultList);
148                     }catch(Exception e){
149                         e.printStackTrace();
150                         continue;
151                     }
152                    
153                 }
154                 floor--;
155             }
156         } catch (Exception e) {
157             logger.error(e.getMessage());
158             e.printStackTrace();    
159         }
160     }
161 }

5.ParseTool 

 1 /**
 2  * 
 3  */
 4 package cn.guet.deep;
 5 
 6 import java.io.IOException;
 7 
 8 import org.htmlparser.Parser;
 9 
10 /**
11  * @author 梁渝铭
12  * @project name:HTMLParser
13  * @date:2011-11-21
14  * 解析工具类
15  */
16 public class ParseTool {
17     
18     private Parse parse = new Parse();
19     
20     //开始解析 
21     public void parse() throws IOException{
22         parse.extractLinks();    
23     }
24     
25     //设置解析的url
26     public void setUrl(String url){
27         parse.setUrl(url);
28     }
29     
30     //设置解析存放路径
31     public void setPath(String path){
32         parse.setPath(path);
33     }
34     
35     //设置要解析的层数
36     public void setFloor(int floor){
37         parse.setFloor(floor);
38     }
39     
40     //释放资源
41     public void free(){
42         parse.free();
43     }
44 
45 }

6.WritetoXML 

 

 

 

  1 /**
  2  * 
  3  */
  4 package cn.guet.deep;
  5 
  6 import java.io.File;
  7 import java.io.FileOutputStream;
  8 import java.io.IOException;
  9 import java.util.LinkedList;
 10 
 11 import org.apache.log4j.Logger;
 12 import org.dom4j.Document;
 13 import org.dom4j.DocumentHelper;
 14 import org.dom4j.Element;
 15 import org.dom4j.io.OutputFormat;
 16 import org.dom4j.io.XMLWriter;
 17 
 18 /**
 19  * @author 梁渝铭
 20  * @project name:HTMLParser
 21  * @date:2011-11-23 
 22  * 把解析得到的链接按层写入xml文件中
 23  */
 24 public class WritetoXML {
 25     private Logger logger = Logger.getLogger(this.getClass());
 26 
 27     /*
 28      * 存放的xml样式 :
 29      * <links> 
 30      *      <link_sumary>
 31      *          <link_number>每个xml中总共的链接数</link_number>
 32      *      </link_sumary>
 33      *      <link> 
 34      *         <name> links_name </name> 
 35      *         <address> link_address <address>
 36      *      </link>   
 37      *      ...  
 38      *  </links>
 39      */
 40      private static int index = 0;
 41     /*
 42      * path表示xml文件存放的路径,floor表示文件的层次,link_name作为xml文件名
 43      */
 44     public void writeToXML(String path, int floor, String link_name,
 45             LinkedList<ResultBean> links) throws IOException {
 46         //创建一个新的xml文档
 47         Document document = DocumentHelper.createDocument();
 48         Element root = document.addElement("links");
 49         Element link_summary = root.addElement("link_summary");
 50         Element link_number = link_summary.addElement("link_number");
 51         link_number.addText(String.valueOf(links.size()));
 52         /*
 53          * 通过循环将解析结果集中的对象数据转换成xml节点
 54          */
 55         for (int i = 0; i < links.size(); i++) {
 56             Element link = root.addElement("link");
 57             Element name = link.addElement("name");
 58             Element address = link.addElement("address");
 59             name.addText(links.get(i).getName());
 60             address.addText(links.get(i).getLink());
 61         }
 62         path = handlePath(path, floor, link_name);
 63         documentToXML(document, path);
 64     }
 65 
 66     /*
 67      * 把链接写入xml中
 68      */
 69 
 70     public void documentToXML(Document document, String filePath) {
 71 
 72         // 使用org.dom4j.io包下的xmlwriter类将dom4j的文档树对象转换为xml输出
 73         XMLWriter xmlWriter = null;
 74         try {
 75             // 创建有格式和缩进的格式化输出对象
 76             OutputFormat format = OutputFormat.createPrettyPrint();
 77             format.setEncoding("UTF-8");
 78             // 将新的文件输出流对象和格式化对象封装进实例化的xmlwriter对象中
 79             xmlWriter = new XMLWriter(new FileOutputStream(filePath), format);
 80             xmlWriter.write(document);
 81         } catch (IOException e) {
 82             logger.error(e.getMessage());
 83         }finally{
 84             //防止意外而无法关闭资源,造成浪费
 85             try {
 86                 xmlWriter.close();
 87             } catch (IOException e) {
 88                 e.printStackTrace();
 89             }
 90         }
 91     }
 92 
 93     /*
 94      * 按层分文件夹,处理每层的文件名,路径等,返回一个xml文件的路径名
 95      * 如:
 96      * D:/test/floor_1/index.xml
 97      */
 98     public String handlePath(String path, int floor, String link_name)
 99             throws IOException {
100         String filePath = path;
101         File file = new File(filePath);
102         //检查路径是否存在
103         if (!file.exists()) {
104             file.mkdirs();
105         }
106         filePath = path +"//" + "floor_" + floor; 
107         if(!(file = new File(filePath)).exists()){
108             file.mkdirs();
109         }
110         file = null;
111         if(link_name.equals(""))
112             link_name = "link_name" + index++;
113         return filePath + "//" + link_name + ".xml";
114     }
115 }

六、输出结果

1.目录集合,一个文件夹floor_x表示某一爬行层次:

 

2.一个文件夹存放着某一层爬行过的网页,一个文件记录表示一个网页:

3.一个XML文件记录一个网页上的目标链接,记录多个目标链接地址:

转载于:https://www.cnblogs.com/sl-shilong/archive/2013/02/03/2890824.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378908.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware下Ubuntu无法全屏显示问题

一、运行Ubuntu的时候无法全屏显示&#xff0c;如图所示下载VMware Tools 二、之后将下载的文件拷贝到home文件夹下 三、解压该压缩包 由于该压缩包是.tar.gz结尾的故压缩命令&#xff1a;tar -zxvf VMwareTools-10.2.5-8068393.tar.gz&#xff0c;当然各版本有可能不一样&am…

AMQP RabbitMQ

转载&#xff1a;http://blog.ftofficer.com/2010/03/translation-rabbitmq-python-rabbits-and-warrens/官方介绍&#xff1a;http://www.rabbitmq.com/erlang-client-user-guide.html开始吧AMQP当中有四个概念非常重要&#xff1a;虚拟主机&#xff08;virtual host&#xff…

fsync与fflush的关系和区别

read/write/fsync与fread/fwrite/fflush的关系和区别 read/write/fsync&#xff1a; linux底层操作&#xff1b; 内核调用&#xff0c; 涉及到进程上下文的切换&#xff0c;即用户态到核心态的转换&#xff0c;这是个比较消耗性能的操作。 fread/fwrite/fflush&#xff1a;…

lumanager mysql密码_LuManager单独安装mysqli

首先确定你正在使用的php版本以及php.ini的位置&#xff0c;LuManager自带了几个版本。如果是默认安装&#xff0c;应该是5.2.17。php.ini的位置应该是在/usr/local/php_fcgi/lib/php.ini要确定这些信息&#xff0c;可以自己编写一个 info.phpphpinfo();?>把文件存放到网站…

数据库系统数据库管理系统_数据库管理系统介绍

数据库系统数据库管理系统数据库 (Database) A database is a collection of related data. In database any user can efficiently access the data which users want to retrieve. It can be anything from a simple collection of roll numbers, names, addresses and phone…

vba将select的值直接赋给变量

strSql ""strSql strSql & " select max(number) from dbo.#DATA" & vbCrLfrss.Open strSql, cnn numb rss.Fields(0)rss.Close转载于:https://www.cnblogs.com/zigewb/archive/2013/02/06/2900645.html

set_exception_handler 自定义异常处理

刚才已经说过了set_error_handler这个函数&#xff0c;作用就是自定义错误处理&#xff0c; 那么现在就来简单的说一下set_exception_handler&#xff0c;看名字我们就能发现&#xff0c;这说的是自定义异常处理。 呵呵&#xff0c;我聪明吧&#xff1f;来&#xff0c;先看一下…

如何获取ubuntu源码包里面的源码进行编译

如何获取ubuntu源码包里面的源码进行编译 1、在获取源码包之前&#xff0c;确保在软件源配置文件 /etc/apt/sources.list中添加了deb-src项 2、使用如下命令获取xxx源码包的详细信息: sudo apt-cache showsrc xxx 这用来查询当前镜像站点中是否有该源码包。 3、源码包中通常…

python 示例_带有示例的Python字典popitem()方法

python 示例字典popitem()方法 (Dictionary popitem() Method) popitem() method is used to remove random/last inserted item from the dictionary. popitem()方法用于从字典中删除随机/最后插入的项目。 Before the Python version 3.7, it removes random item and from …

优化算法的意义,之二。

前一篇分析了求质数的两个算法&#xff0c;在代码执行效率和系统开销两方面进行了比较。 这在通信系统的设计和实现中&#xff0c;是非常重要的两点。因为需要同时面对的是巨大的用户群&#xff0c;和复杂的业务应用&#xff0c;通信系统的设计经常要面临鱼与熊掌间的选择。 用…

srs配置文件分析

配置文件中的每一项都是一个SrsConfDirective对象。 例子&#xff1a;vhost 1、 整个vhost 是一个SrsConfDirective对象。 1.1、名字&#xff1a;std::string name vhost 1.2、参数&#xff1a;std::vectorstd::string args第0个值 defaultVhost 1.3、子SrsConfDirective&a…

寄存器(CPU工作原理)03 - 零基础入门学习汇编语言08

第二章&#xff1a;寄存器&#xff08;CPU工作原理&#xff09;03 让编程改变世界 Change the world by program 物理地址 CPU访问内存单元时要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间。 我们将这个唯一的地址称为物理地址。 16位结构的CPU…

判别Linux是CentOs还是Ubuntu的最简单方法

在终端执行以下两条命令即可 CentOs&#xff1a;yum -help Ubuntu&#xff1a;apt-get -help

threadgroup_Java ThreadGroup toString()方法与示例

threadgroupThreadGroup类的toString()方法 (ThreadGroup Class toString() method) toString() method is available in java.lang package. toString()方法在java.lang包中可用。 toString() method is used to returns string denotation of this thread group (i.e. this m…

240多个jQuery插件

文件上传(File upload)Ajax File Upload.jQUploader.Multiple File Upload plugin. jQuery File Style.Styling an input type file.Progress Bar Plugin.表单验证(Form Validation)jQuery Validation.Auto Help.Simple jQuery form validation.jQuery XAV - form validations…

解压缩命令

.Tar.gz 解压&#xff1a;Tar zxvf FileName.Tar.gz 压缩&#xff1a;Tar zcvf FileName.Tar.gz DirName 大致总结了一下Linux下各种格式的压缩包的压缩、解压方法。但是部分方法我没有用到&#xff0c;也就不全&#xff0c;希望大家帮我补充&#xff0c;我将随时修改完善&…

Anaconda下安装OpenCV和Tensorflow(最简洁高效的方法)

安装Tensorflow 1&#xff0c;打开Anaconda Navigator 2&#xff0c;手动创建tensorflow环境&#xff0c;这个和你的python版本号一致哈&#xff08;方法一第一步之后&#xff0c;输入python即可查看当前的版本&#xff09; 3&#xff0c;手动搜索并下载添加 4&#xff0c;…

Java System类console()方法及示例

系统类console()方法 (System class console() method) console() method is available in java.lang package. console()方法在java.lang包中可用。 console() method is used to return the console object which is uniquely associated with the current JVM(Java Virtual …

使用FD_CLOEXEC实现close-on-exec,关闭子进程无用文件描述符

我们经常会碰到需要fork子进程的情况&#xff0c;而且子进程很可能会继续exec新的程序。这就不得不提到子进程中无用文件描述符的问题&#xff01; fork函数的使用本不是这里讨论的话题&#xff0c;但必须提一下的是&#xff1a;子进程以写时复制&#xff08;COW&#xff0c;C…