一、 目标

获取网页中的超链接及链接名,如从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

基于HtmlParser的网络爬虫相关推荐

  1. 基于WebKit的网络爬虫

    2019独角兽企业重金招聘Python工程师标准>>> https://github.com/emyller/webkitcrawler 一个开源的项目,可以快速入门. http:/ ...

  2. python网络爬虫课程设计题目_山东建筑大学计算机网络课程设计《基于Python的网络爬虫设计》...

    山东建筑大学计算机网络课程设计<基于Python的网络爬虫设计> 山东建筑大学 课 程 设 计 成 果 报 告 题 目: 基于Python的网络爬虫设计 课 程: 计算机网络A 院 (部) ...

  3. python网络爬虫_python小知识,基于Python 的网络爬虫技术分析

    在现阶段大数据的时代中,想要实现对数据的获取和分析,要先具备足够的数据源,网络爬虫技术就为其数据获取提供了良好的条件,且还能够实现对数据源的目的性采集. 在网络爬虫技术应用中,Python 脚本语言的 ...

  4. 爬虫技术python流程图_基于Python的网络爬虫技术研究

    基于 Python 的网络爬虫技术研究 王碧瑶 [摘 要] 摘要:专用型的网络爬虫能够得到想要的返回结果 , 本文就以拉勾网作 为例子 , 对基于 Python 的网络爬虫技术进行研究和分析. [期刊 ...

  5. python网络爬虫的流程图_基于Python的网络爬虫的设计与实现

    龙源期刊网 http://www.qikan.com.cn 基于 Python 的网络爬虫的设计与实现 作者:高祖彦 来源:<商情> 2020 年第 33 期 [摘要]一个爬虫从网上爬取数 ...

  6. 【OpenCV图像处理入门学习教程六】基于Python的网络爬虫与OpenCV扩展库中的人脸识别算法比较

    OpenCV图像处理入门学习教程系列,上一篇第五篇:基于背景差分法的视频目标运动侦测 一.网络爬虫简介(Python3) 网络爬虫,大家应该不陌生了.接下来援引一些Jack-Cui在专栏<Pyt ...

  7. 基于python的网络爬虫编程_基于Python的网络爬虫程序设计

    程序设计 ●Program Design 基于 Python的网络爬虫程序设计 网络 信 息量 的迅 猛 增 长,对 如何从海量的信息中准确的搜索 到用户需要的信息提 出了极大的 挑战.网络爬 虫具有 ...

  8. 基于Thinkphp5+phpQuery 网络爬虫抓取数据接口,统一输出接口数据api

    TP5_Splider 一个基于Thinkphp5+phpQuery 网络爬虫抓取数据接口 统一输出接口数据api.适合正在学习Vue,AngularJs框架学习 开发demo,需要接口并保证接口不跨 ...

  9. 基于python的网络爬虫技术_基于python的网络爬虫技术的研究

    龙源期刊网 http://www.qikan.com.cn 基于 python 的网络爬虫技术的研究 作者:刘文辉 李丽

最新文章

  1. 如何保证工业相机工作的精准与稳定?
  2. ClickHouse之Distributed Query Execution
  3. 将SimCLR应用于NLP预训练模型,提升句子语义表征效果
  4. python自定义全局异常_flask中主动抛出异常及统一异常处理代码示例
  5. EasyRTMP CPU占用问题调优(一)
  6. C. Minimum Grid Path(思维)
  7. 玩JDK 12的Switch表达式
  8. sc925 文档服务器,dell服务器磁盘阵列配置手册.docx
  9. mysql的本地id可以随便设置马_Mysql主从复制原理及搭建
  10. 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。
  11. 怎样完整的转换PDF格式文件
  12. SPPnet论文总结
  13. 【分享】GIS领域论坛社区
  14. python监测网页变化_Python利用Last-Modified实现监控网页变化
  15. vim 配置(ma6174 + YCM)
  16. RabbitMQ heartbeat原理
  17. C语言实现扫雷小游戏
  18. javalang 生成抽象语法树AST ----python源码分析
  19. PlatoFarm推出正式版游戏经济模型的特点分析
  20. 局域网内的计算机拒绝访问,win10系统局域网拒绝访问的解决方法

热门文章

  1. 扩大缩小Linux物理分区大小
  2. Memcached入门指南
  3. 王成录华为鸿蒙系统,华为手机销量仍在增长!华为王成录:手机会是鸿蒙OS系统的中心...
  4. 使用openocd调试Linux内核,openocd安装与调试
  5. 神经网络最常用的10个激活函数,一文详解数学原理及优缺点
  6. spark官方文档_这些未在 Spark SQL 文档中说明的优化措施,你知道吗?
  7. php把1拆分成三份,【php】位运算如何拆分
  8. Bash脚本教程之脚本除错
  9. 计算机dos通讯,PC双机通信DOS
  10. skywalking使用方法_skywalking 6.2配置相关和使用