网络爬虫(1)

参考:http://www.cnblogs.com/dongkuo/p/4851735.html

算法分析

我们现在从需求中提取关键词来逐步分析问题。

首先是“种子节点”。它就是一个或多个在爬虫程序运行前手动给出的URL(网址),爬虫正是下载并解析这些种子URL指向的页面,从中提取出新的URL,然后重复以上的工作,直到达到设定的条件才停止。

然后是“特定的策略”。这里所说的策略就是以怎样的顺序去请求这些URL。如下图是一个简单的页面指向示意图(实际情况远比这个复杂),页面A是种子节点,当然最先请求。但是剩下的页面该以何种顺序请求呢?我们可以采用深度优先遍历策略,通俗讲就是一条路走到底,走完一条路才再走另一条路,在下图中就是按A,B,C,F,D,G,E,H的顺序访问。我们也可以采用宽度优先遍历策略,就是按深度顺序去遍历,在下图中就是按A,B,C,D,E,F,G,H的顺序请求各页面。还有许多其他的遍历策略,如Google经典的PageRank策略,OPIC策略策略,大站优先策略等,这里不一一介绍了。我们还需要注意的一个问题是,很有可能某个页面被多个页面同时指向,这样我们可能重复请求某一页面,因此我们还必须过滤掉已经请求过的页面。          

最后是“设定的条件”,爬虫程序终止的条件可以根据实际情况灵活设置,比如设定爬取时间,爬取数量,爬行深度等。

到此,我们分析完了爬虫如何开始,怎么运作,如何结束(当然,要实现一个强大,完备的爬虫要考虑的远比这些复杂,这里只是入门分析),下面给出整个运作的流程图:

                  

 数据结构分析

根据以上的分析,我们需要用一种数据结构来保存初始的种子URL和解析下载的页面得到的URL,并且我们希望先解析出的URL先执行请求,因此我们用队列来储存URL。因为我们要频繁的添加,取出URL,因此我们采用链式存储。下载的页面解析后直接原封不动的保存到磁盘。

 技术分析

所谓网络爬虫,我们当然要访问网络,我们这里使用jsoup,它对http请求和html解析都做了良好的封装,使用起来十分方便。根据数据结构分析,我们用LinkedList实现队列,用来保存未访问的URL,用HashSet来保存访问过的URL(因为我们要大量的判断该URL是否在该集合内,而HashSet用元素的Hash值作为“索引”,查找速度很快)。

 代码

以上分析,我们一共要实现2个类:

① JsoupDownloader,该类是对Jsoup做一个简单的封装,方便调用。暴露出以下几个方法:

—public Document downloadPage(String url);根据url下载页面

—public Set<String> parsePage(Document doc, String regex);从Document中解析出匹配regex的url。

—public void savePage(Document doc, String saveDir, String saveName, String regex);保存匹配regex的url对应的Document到指定路径。

② UrlQueue,该类用来保存和获取URL。暴露出以下几个方法:

—public void enQueue(String url);添加url。

—public String deQueue();取出url。

—public int getVisitedCount();获取访问过的url的数量;

package com.hjzgg.spider;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.SocketTimeoutException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.jsoup.Connection;
import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class JsoupDownloader {

    public static final String DEFAULT_SAVE_DIR = "c:/download/";
    private static JsoupDownloader downloader;

    private JsoupDownloader() {
    }

    public static JsoupDownloader getInstance() {
        if (downloader == null) {
            synchronized (JsoupDownloader.class) {
                if (downloader == null) {
                    downloader = new JsoupDownloader();
                }
            }
        }
        return downloader;
    }

    public Document downloadPage(String url) {
        try {
            System.out.println("正在下载" + url);
            Connection connection = Jsoup.connect(url);
            connection.timeout(1000);
            connection.followRedirects(false);//默认是true,也就是连接遵循重定向!设置为false,对重定向的地址进行筛选
            Response response = connection.execute();
            Map<String, String> headers = response.headers();
            System.out.println(response.statusCode() + " " + response.statusMessage());
            if(response.statusCode()==301 || response.statusCode()==302){////重定向地址,位于信息头header中
                Main.urlQueue.enQueue(headers.get("Location"));
            } else if(response.statusCode() == 404){
                //或者一些其他的错误信息,直接将改地址丢弃
                return null;
            }
            for(String name : headers.keySet())//在这里可以查看http的响应信息头信息
                System.out.println(name + " : " + headers.get(name));
            return connection.get();
        } catch(SocketTimeoutException e){//对于连接超时的url我们可以重新将其放入未访问url队列中
            Main.urlQueue.enQueueUrlTimeOut(url);
        }catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Set<String> parsePage(Document doc, String regex) {
        Set<String> urlSet = new HashSet<String>();
        if (doc != null) {
            Elements elements = doc.select("a[href]");
            for (Element element : elements) {
                String url = element.attr("href");
                if (url.length() > 6 && !urlSet.contains(url)) {
                    if (regex != null && !url.matches(regex)) {
                        continue;
                    }
                    if(!url.contains("http"))
                        url = doc.baseUri()+url;
                    urlSet.add(url);
                }
            }
        }
        return urlSet;
    }

    public void savePage(Document doc, String saveDir, String saveName, String regex) {
        if (doc == null) {
            return;
        }
        if (regex != null && doc.baseUri() != null && !doc.baseUri().matches(regex)) {
            return;
        }
        saveDir = saveDir == null ? DEFAULT_SAVE_DIR : saveDir;
        saveName = saveName == null ? doc.title().trim().replaceAll("[\\?/:\\*|<>\" ]", "_") + System.nanoTime() + ".html" : saveName;
        File file = new File(saveDir + "/" + saveName);
        File dir = file.getParentFile();
        if (!dir.exists()) {
            dir.mkdirs();
        }
        PrintWriter printWriter;
        try {
            printWriter = new PrintWriter(file);
            printWriter.write(doc.toString());
            printWriter.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
package com.hjzgg.spider;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Set;

public class UrlQueue {

    private Set<String> visitedSet;// 用来存放已经访问过多url
    private LinkedList<String> unvisitedList;// 用来存放未访问过多url

    public UrlQueue(String[] seeds) {
        visitedSet = new HashSet<String>();
        unvisitedList = new LinkedList<String>();
        unvisitedList.addAll(Arrays.asList(seeds));
    }

    /**
     * 添加url
     *
     * @param url
     */
    public void enQueue(String url) {
        if (url != null && !visitedSet.contains(url)) {
            unvisitedList.addLast(url);
        }
    }

    /**
     * 添加访问超时的url
     *
     * @param url
     */
    public void enQueueUrlTimeOut(String url) {
        if (url != null) {
            visitedSet.remove(url);
            unvisitedList.addLast(url);
        }
    }

    /**
     * 添加url
     *
     * @param urls
     */
    public void enQueue(Collection<String> urls) {
        for (String url : urls) {
            enQueue(url);
        }
    }

    /**
     * 取出url
     *
     * @return
     */
    public String deQueue() {
        try {
            String url = unvisitedList.removeFirst();
            while(visitedSet.contains(url)) {
                url = unvisitedList.removeFirst();
            }
            visitedSet.add(url);
            return url;
        } catch (NoSuchElementException e) {
            System.err.println("URL取光了");
        }
        return null;
    }

    /**
     * 得到已经请求过的url的数目
     *
     * @return
     */

    public int getVisitedCount() {
        return visitedSet.size();
    }
}
package com.hjzgg.spider;

import java.util.Set;

import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Main {
    static UrlQueue urlQueue = new UrlQueue(new String[] { "http://192.168.1.201:8080/HZML/" });
    public static void main(String[] args) {
        JsoupDownloader downloader = JsoupDownloader.getInstance();
        long start = System.currentTimeMillis();
        while (urlQueue.getVisitedCount() < 1000) {
            String url = urlQueue.deQueue();
            if (url == null) {
                break;
            }
            Document doc = downloader.downloadPage(url);
            if (doc == null) {
                continue;
            }
            Set<String> urlSet = downloader.parsePage(doc, "userRequest\\?userRequest=showNoParticipateTask&taskid=\\d{1,2}|http://www.cnblogs.com/hujunzheng/(p|default|archive/\\d{4}/\\d{2}/\\d{2}/).*");
            urlQueue.enQueue(urlSet);
            downloader.savePage(doc, "I:\\博客园-hjzgg", null, "userRequest\\?userRequest=showNoParticipateTask&taskid=\\d{1,2}|http://www.cnblogs.com/hujunzheng/(p|default|archive/\\d{4}/\\d{2}/\\d{2}/).*");
            System.out.println("已请求" + urlQueue.getVisitedCount() + "个页面");
        }
        long end = System.currentTimeMillis();
        System.out.println(">>>>>>>>>>抓取完成,共抓取" + urlQueue.getVisitedCount() + "到个页面,用时" + ((end - start) / 1000) + "s<<<<<<<<<<<<");
    }
}

注:userRequest\\?userRequest=showNoParticipateTask&taskid=\\d{1,2} 是本地的网站中网页里的地址,当然种子地址也是本地网站!

在请求这个地址后会产生地址重定向,到博客园地址http://www.cnblogs.com/hujunzheng/,并完成相应的下载!

1.这是请求超时的情况

2.正常访问或者重定向访问

时间: 2024-08-10 16:59:57

网络爬虫(1)的相关文章

简谈-网络爬虫的几种常见类型

众所周知,网络爬虫(或称为网络爬虫.网络蜘蛛.机器人)是搜索引擎最上游的一个模块,是负责搜索引擎内容索引的第一关. 很多人为了提高自己网站的索引量,都是去网上随便找一些爬虫工具来使用.但是很多人不知道,这些抓取网站的小爬虫是有各种各样的不同性格的. 常见的优秀网络爬虫有以下几种类型: 1.批量型网络爬虫:限制抓取的属性,包括抓取范围.特定目标.限制抓取时间.限制数据量以及限制抓取页面,总之明显的特征就是受限: 2.增量型网络爬虫(通用爬虫):与前者相反,没有固定的限制,无休无止直到抓完所有数据.

[No000090]微信公众号网络爬虫接口分析1

一直想做个公众号的网络爬虫,网上的好多接口都依赖于"瘦狗",不过微信接口一直在变,现在"瘦狗"也只允许查看10条历史记录,所以今天趁着下雨,研究了一下apk内部的东西,但是怕微信又改,我透漏的不能太多,有兴趣可以私下交流. 从微信上复制出来的url:https://mp.weixin.qq.com/mp/getmasssendmsg?__biz=MzI4OTA5MDgxNw==#wechat_webview_type=1&wechat_redirect 用浏

基于Nutch+Hadoop+Hbase+ElasticSearch的网络爬虫及搜索引擎

网络爬虫架构在Nutch+Hadoop之上,是一个典型的分布式离线批量处理架构,有非常优异的吞吐量和抓取性能并提供了大量的配置定制选项.由于网络爬虫只负责网络资源的抓取,所以,需要一个分布式搜索引擎,用来对网络爬虫抓取到的网络资源进行实时的索引和搜索. 搜 索引擎架构在ElasticSearch之上,是一个典型的分布式在线实时交互查询架构,无单点故障,高伸缩.高可用.对大量信息的索引与搜索都可以在近 乎实时的情况下完成,能够快速实时搜索数十亿的文件以及PB级的数据,同时提供了全方面的选项,可以对

网络爬虫技术入门_Python基础与爬虫技术

Python基础与爬虫技术  课程学习地址:http://www.xuetuwuyou.com/course/195 课程出自学途无忧网:http://www.xuetuwuyou.com 课程简介 本作为一种便捷地收集网上信息并从中抽取出可用信息的方式,网络爬虫技术变得越来越有用.使用Python这样的简单编程语言,你可以使用少量编程技能就可以爬取复杂的网站. <Python 基础与爬虫技术>讲解了从静态页面爬取数据的方法以及使用缓存来管理服务器负载的方法.此外,本课程还介绍了如何使用AJA

网络爬虫小案例_2017/3/10

今晚,了解了一下网络爬虫,那么什么是网络爬虫呢? 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常被称为网页追逐者),是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本,已被广泛应用于互联网领域.搜索引擎使用网络爬虫抓取Web网页.文档甚至图片.音频.视频等资源,通过相应的索引技术组织这些信息,提供给搜索用户进行查询.网络爬虫也为中小站点的推广提供了有效的途径,网站针对搜索引擎爬虫的优化曾风靡一时. 网络爬虫的基本工作流程如下:     1.首先选取一部分精心挑选的种子U

使用网络爬虫的一丝心得

因为参与了创新计划,所以懵懵懂懂的接触到了网络爬虫. 爬数据使用工具,因此了解到Python.asp.net等可以用来抓数据. 想想在学习.net的时候根本没有想到会使用在这个上面--书本上的知识都是死的,那学习的基础知识只能通过不断的拓展使用领域才能在更好的得到深化.应用! 进入一个陌生的领域,从入门到精通的路真的是需要用汗水积累起来的--没有真正的天才,只有自感聪明的蠢材.(自我审视) 有句话说的特别的好:"世界上两种聪明的人:一种是从来不认为自己聪明的聪明人:而另一种是自以为自己很聪明的'

网络爬虫之selenium(综述)

   现阶段网络爬虫的工具主要是有scrapy.selenium(第二版)等.总的来说各有好处,scrapy最大 的好处是爬取的速度快而selenium的好处是能爬去的网站种类多.详细点的解释是:scrapy在爬取网页时不 用点击开页面(selenium似乎需要页面全部加载完才可以查找定位),selenium可以爬取动态网页和静态网页 (模拟浏器操作)而scrapy可以很顺利的爬取静态网页(但是很难或者不能爬取动态网页).由于爬取知网 的需要,本人学习了selenium,现分享一下个人的心得(如

下载大数据实战课程第一季Python基础和网络爬虫数据分析

python语言近年来越来越被程序相关人员喜欢和使用,因为其不仅简单容易学习和掌握,而且还有丰富的第三方程序库和相应完善的管理工具:从命令行脚本程序到gui程序,从B/S到C/S,从图形技术到科学计算,软件开发到自动化测试,从云计算到虚拟化,所有这些领域都有python的身影:python已经深入到程序开发的各个领域,并且会越来越多的人学习和使用. 大数据实战课程第一季Python基础和网络爬虫数据分析,刚刚入手,转一注册文件,视频的确不错,可以先下载看看:链接:http://pan.baidu

网络爬虫: 从allitebooks.com抓取书籍信息并从amazon.com抓取价格(2): 抓取allitebooks.com书籍信息及ISBN码

这一篇首先从allitebooks.com里抓取书籍列表的书籍信息和每本书对应的ISBN码. 一.分析需求和网站结构 allitebooks.com这个网站的结构很简单,分页+书籍列表+书籍详情页. 要想得到书籍的详细信息和ISBN码,我们需要遍历所有的页码,进入到书籍列表,然后从书籍列表进入到每本书的详情页里,这样就能够抓取详情信息和ISBN码了. 二.从分页里遍历每一页书籍列表 通过查看分页功能的HTML代码,通过class="current"可以定位当前页码所在span标签,此s

网络爬虫

网络爬虫 概述 这是一个网络爬虫学习的技术分享,主要通过一些实际的案例对爬虫的原理进行分析,达到对爬虫有个基本的认识,并且能够根据自己的需要爬到想要的数据.有了数据后可以做数据分析或者通过其他方式重新结构化展示. 什么是网络爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕虫.via 百度百科网络爬虫网络蜘蛛(Web spider)也叫网络爬