Java爬虫Get校花网所有美女图片

前言

作为一个宅男,每天看看美女图是必修课。那么——作为一个程序猿加宅男,如何收藏更多的美女图片呢?这就要用到爬虫了,哈哈,我仿佛看到了无穷无尽的美女在向我招手——怎么感觉写下这段话的时候自己略有一丝猥琐呢?啊呸,相当之猥琐!

我们的重点是学习写爬虫,嗯!

网络爬虫是做什么的?

他的主要工作就是 跟据指定的url地址 去发送请求,获得响应, 然后解析响应 , 一方面从响应中查找出想要查找的数据,另一方面从响应中解析出新的URL路径。

然后继续访问,继续解析;继续查找需要的数据和继续解析出新的URL路径

这就是网络爬虫主要干的工作. 下面是流程图:

通过上面的流程图 能大概了解到 网络爬虫 干了哪些活 ,根据这些 也就能设计出一个简单的网络爬虫出来。

一个简单的爬虫 必需的功能:

  1. 发送请求和获取响应的功能
  2. 解析响应的功能
  3. 过滤出的数据进行存储的功能
  4. 对解析出来的URL路径处理的功能

先看看运行效果

下面是包结构

核心代码

RequestAndResponseTool 类: 主要方法: 发送请求 返回响应 并把 响应 封装成 page 类

public class RequestAndResponseTool {


    public static Page sendRequstAndGetResponse(String url) {
        Page page = null;
        // 1.生成 HttpClinet 对象并设置参数
        HttpClient httpClient = new HttpClient();
        // 设置 HTTP 连接超时 5s
        httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
        // 2.生成 GetMethod 对象并设置参数
        GetMethod getMethod = new GetMethod(url);
        // 设置 get 请求超时 5s
        getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
        // 设置请求重试处理
        getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
        // 3.执行 HTTP GET 请求
        try {
            int statusCode = httpClient.executeMethod(getMethod);
            // 判断访问的状态码
            if (statusCode != HttpStatus.SC_OK) {
                System.err.println("Method failed: " + getMethod.getStatusLine());
            }
            // 4.处理 HTTP 响应内容
            byte[] responseBody = getMethod.getResponseBody();// 读取为字节 数组
            String contentType = getMethod.getResponseHeader("Content-Type").getValue(); // 得到当前返回类型
            page = new Page(responseBody, url, contentType); //封装成为页面
        } catch (HttpException e) {
            // 发生致命的异常,可能是协议不对或者返回的内容有问题
            System.out.println("Please check your provided http address!");
            e.printStackTrace();
        } catch (IOException e) {
            // 发生网络异常
            e.printStackTrace();
        } finally {
            // 释放连接
            getMethod.releaseConnection();
        }
        return page;
    }
}

page 类: 主要作用: 保存响应的相关内容 对外提供访问方法

/*
* 保存获取到的响应的相关内容;
*/
public class Page {

    private byte[] content;
    private String html;  //网页源码字符串
    private Document doc;//网页Dom文档
    private String charset;//字符编码
    private String url;//url路径
    private String contentType;// 内容类型


    public Page(byte[] content, String url, String contentType) {
        this.content = content;
        this.url = url;
        this.contentType = contentType;
    }

    public String getCharset() {
        return charset;
    }

    public String getUrl() {
        return url;
    }

    public String getContentType() {
        return contentType;
    }

    public byte[] getContent() {
        return content;
    }

    /**
     * 返回网页的源码字符串
     *
     * @return 网页的源码字符串
     */
    public String getHtml() {
        if (html != null) {
            return html;
        }
        if (content == null) {
            return null;
        }
        if (charset == null) {
            charset = CharsetDetector.guessEncoding(content); // 根据内容来猜测 字符编码
        }
        try {
            this.html = new String(content, charset);
            return html;
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    /*
    *  得到文档
    * */
    public Document getDoc() {
        if (doc != null) {
            return doc;
        }
        try {
            this.doc = Jsoup.parse(getHtml(), url);
            return doc;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }


}

PageParserTool: 类 主要作用 提供了 根据选择器来选取元素 属性 等方法

public class PageParserTool {

    /* 通过选择器来选取页面的 */
    public static Elements select(Page page, String cssSelector) {
        return page.getDoc().select(cssSelector);
    }

    /*
     *  通过css选择器来得到指定元素;
     *
     *  */
    public static Element select(Page page, String cssSelector, int index) {
        Elements eles = select(page, cssSelector);
        int realIndex = index;
        if (index < 0) {
            realIndex = eles.size() + index;
        }
        return eles.get(realIndex);
    }

    /**
     * 获取满足选择器的元素中的链接 选择器cssSelector必须定位到具体的超链接
     * 例如我们想抽取id为content的div中的所有超链接,这里
     * 就要将cssSelector定义为div[id=content] a
     * 放入set 中 防止重复;
     *
     * @param cssSelector
     * @return
     */
    public static Set<String> getLinks(Page page, String cssSelector) {
        Set<String> links = new HashSet<String>();
        Elements es = select(page, cssSelector);
        Iterator iterator = es.iterator();
        while (iterator.hasNext()) {
            Element element = (Element) iterator.next();
            if (element.hasAttr("href")) {
                links.add(element.attr("abs:href"));
            } else if (element.hasAttr("src")) {
                links.add(element.attr("abs:src"));
            }
        }
        return links;
    }

    /**
     * 获取网页中满足指定css选择器的所有元素的指定属性的集合
     * 例如通过getAttrs("img[src]","abs:src")可获取网页中所有图片的链接
     *
     * @param cssSelector
     * @param attrName
     * @return
     */
    public static ArrayList<String> getAttrs(Page page, String cssSelector, String attrName) {
        ArrayList<String> result = new ArrayList<String>();
        Elements eles = select(page, cssSelector);
        for (Element ele : eles) {
            if (ele.hasAttr(attrName)) {
                result.add(ele.attr(attrName));
            }
        }
        return result;
    }
}

Links 类: 两个属性: 一个是存放 已经访问的url集合的set ; 一个是存放待访问url集合的 queue

/*
*  Link主要功能;
*  存储已经访问过的URL路径 和 待访问的URL 路径;
*/
public class Links {

    //已访问的 url 集合  已经访问过的 主要考虑 不能再重复了 使用set来保证不重复;
    private static Set visitedUrlSet = new HashSet();

    //待访问的 url 集合  待访问的主要考虑 1:规定访问顺序;2:保证不提供重复的带访问地址;
    private static LinkedList unVisitedUrlQueue = new LinkedList();

    //获得已经访问的 URL 数目
    public static int getVisitedUrlNum() {
        return visitedUrlSet.size();
    }

    //添加到访问过的 URL
    public static void addVisitedUrlSet(String url) {
        visitedUrlSet.add(url);
    }

    //移除访问过的 URL
    public static void removeVisitedUrlSet(String url) {
        visitedUrlSet.remove(url);
    }


    //获得 待访问的 url 集合
    public static LinkedList getUnVisitedUrlQueue() {
        return unVisitedUrlQueue;
    }

    // 添加到待访问的集合中  保证每个 URL 只被访问一次
    public static void addUnvisitedUrlQueue(String url) {
        if (url != null && !url.trim().equals("") && !visitedUrlSet.contains(url) && !unVisitedUrlQueue.contains(url)) {
            unVisitedUrlQueue.add(url);
        }
    }

    //删除 待访问的url
    public static Object removeHeadOfUnVisitedUrlQueue() {
        return unVisitedUrlQueue.removeFirst();
    }

    //判断未访问的 URL 队列中是否为空
    public static boolean unVisitedUrlQueueIsEmpty() {
        return unVisitedUrlQueue.isEmpty();
    }


}

LinkFilter 接口: 可以起过滤作用

public interface LinkFilter {
    public boolean accept(String url);
}

原文发布于微信公众号 - java工会(javagonghui)

原文发表时间:2018-06-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小工匠技术圈

【小工匠聊密码学】-- Base64算法

1163
来自专栏云霄雨霁

SpringMVC--Json数据交互笔记

1353
来自专栏大内老A

ASP.NET MVC的Razor引擎:IoC在View激活过程中的应用

在《ASP.NET MVC的Razor引擎:RazorView》介绍BuildManagerCompiledView的时候,我们谈到默认使用的ViewPageA...

1859
来自专栏一个会写诗的程序员的博客

第9章 文件IO操作、正则表达式与多线程第9章 文件IO操作、正则表达式与多线程

我们在《第6章 扩展函数与属性》中已经介绍过Kotlin中的类扩展的特性。使用Kotlin的扩展函数功能,我们可以直接为 String 类实现一个 inc() ...

773
来自专栏编程坑太多

lambda与函数式

1352
来自专栏技术博客

Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.h...

893
来自专栏noteless

[十四]JavaIO之PrintStream

551
来自专栏Google Dart

AngularDart Material Design 复选框 顶

用户可以点击该复选框以选中或取消选中它。 通常使用复选框允许用户从一组中选择多个选项。如果您有一个ON/OFF选项,请避免使用单个复选框并使用material-...

504
来自专栏从流域到海域

《Java程序设计基础》 第3章手记

《Java程序设计基础》 第3章手记 本章主要内容: 1. 数据类型 2. 变量 3. 基本类型变量 4. 数据类型的转换规则 ...

1916
来自专栏刘君君

Validator 使用总结

1896

扫码关注云+社区