Java爬虫(二)

  

  上一篇简单的实现了获取url返回的内容,在这一篇就要第返回的内容进行提取,并将结果保存到html中。而且这个爬虫是基于python爬虫的java语言实现,其逻辑大致相同。

一 、 需求:

  抓取主页面:百度百科Python词条   https://baike.baidu.com/item/Python/407313

  

分析上面的源码格式,便于提取:

  • 关键词分析:位于class为lemmaWgt-lemmaTitle-title的dd元素的第一个h1标签内

  •  简介分析(位于class为lemma-summary的div的text内容)

  

  • 其他相关联的标签的分析(是a标签,且href以/item/开头)

二、抓取过程流程图:

?

三、代码实现:

1.SpiderManager.java 

  构造函数中创建Url管理器,html加载器,html解析器,html输出器

  craw()方法中是爬虫的主要业务逻辑。

package cn.qlq.craw.JsoupBaike;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * 爬虫的入口
 * @author liqiang
 *
 */
public class SpiderManager {
    private UrlManager urlManager;
    private HtmlLoader htmlLoader;
    private HtmlOutputer htmlOutputer;
    private HtmlParser htmlParser;

    public SpiderManager() {
        super();
        this.urlManager =new  UrlManager();
        this.htmlLoader =new  HtmlLoader();
        this.htmlOutputer =new HtmlOutputer();
        this.htmlParser =new  HtmlParser();
    }

    /**
     * 爬虫的主要逻辑
     * @param url    需要爬的网站地址
     */
    public void craw(String url){
        if(url == null || "".equals(url)){
            return;
        }
        int count = 0;//记录爬取了几个页面
        urlManager.addNewUrl(url);
        while (urlManager.hasNewUrl()){
            try {
                String newUrl = urlManager.getNewUrl();//获取需要爬取的网站url
                String htmlContent = htmlLoader.loadUrl(newUrl);//爬取网站内容
                Map<String,Object> datas = htmlParser.parseHtml(newUrl,htmlContent);//提取到爬到的网页中需要的信息
                urlManager.addNewUrls((List<String>) datas.get("urls"));//将提取到的url信息保存到urlManager
                htmlOutputer.collectData(datas);//将提取到的数据收集起来
                if(count == 10){
                    break;
                }
                count++;
            } catch (Exception e) {
                System.out.println("发生异常"+e.getMessage());
            }
        }
        try {
            htmlOutputer.outputDatas();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public UrlManager getUrlManager() {
        return urlManager;
    }
    public void setUrlManager(UrlManager urlManager) {
        this.urlManager = urlManager;
    }
    public HtmlLoader getHtmlLoader() {
        return htmlLoader;
    }
    public void setHtmlLoader(HtmlLoader htmlLoader) {
        this.htmlLoader = htmlLoader;
    }

    public HtmlOutputer getHtmlOutputer() {
        return htmlOutputer;
    }
    public void setHtmlOutputer(HtmlOutputer htmlOutputer) {
        this.htmlOutputer = htmlOutputer;
    }

    public HtmlParser getHtmlParser() {
        return htmlParser;
    }
    public void setHtmlParser(HtmlParser htmlParser) {
        this.htmlParser = htmlParser;
    }
}

2.UrlManager.java

  维护两个list,一个用于存放未被爬取的url地址

        一个用于存储已经爬取的url地址,并且两者不能有重复元素

package cn.qlq.craw.JsoupBaike;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
 * url管理器
 * @author liqiang
 *
 */
@SuppressWarnings("all")
public class UrlManager {

    /**
     * 存放未被访问的url的list
     */
    private List<String> new_urls;
    /**
     * 存放已经访问过的url的list
     */
    private List<String> old_urls;
    public UrlManager() {
        this.new_urls = new LinkedList<String>();
        this.old_urls = new LinkedList<String>();
    }

    /**
     * 添加一个url到list中
     * @param url
     */
    public void addNewUrl(String url) {
        if(url == null || "".equals(url)){
            return;
        }
        if(!new_urls.contains(url) && !old_urls.contains(url) ){
            new_urls.add(url);
        }
    }

    /**
     * 判断是否有新的url
     * @return
     */
    public boolean hasNewUrl() {
        return new_urls.size()>0;
    }

    /**
     * 弹出一个新的url
     * @return
     */
    public String getNewUrl() {
        if(new_urls.size() == 0){
            return null;
        }
        String newUrl = new_urls.get(0);//从未访问的集合中获取一个数据
        new_urls.remove(0);//移除第一个元素
        old_urls.add(newUrl);//将移除的数据添加到旧的已经访问过的集合中
        return newUrl;
    }

    /**
     * 批量添加url
     * @param urls    需要添加的url集合
     */
    public void addNewUrls(List<String> urls) {
        if(urls == null || urls.size()==0){
            return;
        }
        for(String url:urls){
            this.addNewUrl(url);
        }
    }

    public List<String> getNew_urls() {
        return new_urls;
    }

    public void setNew_urls(List<String> new_urls) {
        this.new_urls = new_urls;
    }

    public List<String> getOld_urls() {
        return old_urls;
    }

    public void setOld_urls(List<String> old_urls) {
        this.old_urls = old_urls;
    }

}

3.HtmlLoader.java

  主要就是利用JSoup去读取url的内容

package cn.qlq.craw.JsoupBaike;

import java.io.IOException;

import org.jsoup.Jsoup;
/**
 * 读取url的内容
 * @author liqiang
 *
 */
public class HtmlLoader {

    public String loadUrl(String newUrl) {
        try {
            return Jsoup.connect(newUrl).post().toString();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "";
    }

}

4.HtmlParser.java

  主要就是提取页面的主要内容(a标签,标题和简介)

package cn.qlq.craw.JsoupBaike;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
/**
 * 解析器
 * @author liqiang
 *
 */
public class HtmlParser {
    /**
     * 提取网站信息
     *
     * @param newUrl
     * @param htmlContent
     * @return map中应该包含该网页上提取到的url地址(set集合)和关键字
     */
    public Map<String, Object> parseHtml(String newUrl, String htmlContent) {
        // 0.将返回的htmlContent转换成DOM树
        // Document document = Jsoup.parse(htmlContent);
        // 1.获取到所有的a标签,且a标签的href属性包含/item
        List<String> urls = this.getUrls(newUrl, htmlContent);
        // 2.获取指定的标题和介绍
        Map<String, Object> titleAndSummary = this.getTitleAndSummary(newUrl, htmlContent);
        // 创建一个map,将提取到的urls和标题和简介装到map中返回去
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("urls", urls);
        result.put("titleAndSummary", titleAndSummary);
        return result;
    }

    /**
     * 获取标题和简介
     *
     * @param newUrl
     *            传下来的访问的url
     * @param htmlContent
     *            传下来的获取到的html内容
     * @return
     */
    private Map<String, Object> getTitleAndSummary(String newUrl, String htmlContent) {
        Document document = Jsoup.parse(htmlContent);// 转换成DOM文档
        // 1.获取标题
        // first查找下面的第一个h1元素,get(index)可以获取指定位置的标签
        Element title_ele = document.select("dd.lemmaWgt-lemmaTitle-title").select("h1").first();
        String title_text = title_ele.text();
        // 2.获取简介
        Element summary_ele = document.select("div.lemma-summary").first();
        String summary_text = summary_ele.text();

        //将3.数据加入map返回
        Map<String, Object> titleAndSummary = new HashMap<String, Object>();
        titleAndSummary.put("title", title_text);
        titleAndSummary.put("summary", summary_text);
        titleAndSummary.put("url", newUrl);
        return titleAndSummary;
    }

    /**
     * 获取到所有的a标签,且a标签的href属性包含/item
     *
     * @param newUrl
     * @param htmlContent
     * @return
     */
    private List<String> getUrls(String newUrl, String htmlContent) {
        Document document = Jsoup.parse(htmlContent);// 转换成DOM文档
        Elements elements = document.select("a[href^=‘/item‘]");// 查找以/item开头的元素的标签
        List<String> urls = new ArrayList<String>();
        for (Element ele : elements) {
            String url = newUrl.substring(0, newUrl.indexOf("/item")) + ele.attr("href");
            urls.add(url);
        }
        return urls;
    }

}

5.HtmlOutputer.java

  主要作用有两个:一个是保存每次提取的信息

         一个是最后爬虫完毕将提取的信息输出到html中。 

package cn.qlq.craw.JsoupBaike;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 输出器
 *
 * @author liqiang
 *
 */
public class HtmlOutputer {

    private List<Map<String, Object>> collected_datas;

    public HtmlOutputer() {
        this.collected_datas = new ArrayList<Map<String, Object>>();
    }

    /**
     * 收集数据
     *
     * @param datas
     */
    public void collectData(Map<String, Object> datas) {
        collected_datas.add(datas);// 将数据 添加到集合中
    }

    /**
     * 最后处理所有的数据,写出到html或者保存数据库
     *
     * @throws IOException
     */
    public void outputDatas() throws IOException {
        if (collected_datas != null && collected_datas.size() > 0) {
            File file = new File("C:\\Users\\liqiang\\Desktop\\实习\\python\\JavaCraw\\out.html");
            // 如果文件不存在就创建文件
            if (!file.exists()) {
                file.createNewFile();
            }
            // 构造FileWriter用于向文件中输出信息(此构造方法可以接收file参数,也可以接收fileName参数)
            FileWriter fileWriter = new FileWriter(file);
            // 开始写入数据
            fileWriter.write("<html>");
            fileWriter.write("<head>");
            fileWriter.write("<title>爬取结果</title>");
            fileWriter
                    .write("<style>table{width:100%;table-layout: fixed;word-break: break-all; word-wrap: break-word;}"
                            + "table td{border:1px solid black;width:300px}</style>");
            fileWriter.write("</head>");
            fileWriter.write("<body>");
            fileWriter.write("<table cellpadding=‘0‘ cellspacing=‘0‘>");
            for (Map<String, Object> datas : collected_datas) {
                @SuppressWarnings("unchecked")
                Map<String, Object> data = (Map<String, Object>) datas.get("titleAndSummary");
                String url = (String) data.get("url");
                String title = (String) data.get("title");
                String summary = (String) data.get("summary");
                fileWriter.write("<tr>");
                fileWriter.write("<td><a href=" + url + ">" + url + "</a></td>");
                fileWriter.write("<td>" + title + "</td>");
                fileWriter.write("<td>" + summary + "</td>");
                fileWriter.write("</tr>");

            }
            fileWriter.write("</table>");
            fileWriter.write("</body>");
            fileWriter.write("</html>");
            // 关闭文件流
            fileWriter.close();
        }
    }

}

至此代码编写基本完成,下面进行测试:

package cn.qlq.craw.JsoupBaike;

import java.io.FileWriter;

public class MainClass {

    public static void main(String[] args) {
        String url = "https://baike.baidu.com/item/Python/407313";
        SpiderManager sm = new SpiderManager();
        sm.craw(url);
    }

}

结果:(会生成out.html)

Jsoup中文API网址:http://www.open-open.com/jsoup/

python同样功能的爬虫实现:http://www.cnblogs.com/qlqwjy/p/8877705.html

原文地址:https://www.cnblogs.com/qlqwjy/p/8877928.html

时间: 2024-11-07 09:29:38

Java爬虫(二)的相关文章

Java爬虫实战(二):抓取一个视频网站上2015年所有电影的下载链接

前言:这是Java爬虫实战的第二篇文章,在第一篇文章仅仅只是抓取目标网站的链接的基础上,进一步提高难度,抓取目标页面上我们所需要的内容并保存在数据库中.这里的测试案例选用了一个我常用的电影下载网站(http://www.80s.la/).本来是想抓取网站上的所有电影的下载链接,后来感觉需要的时间太长,因此改成了抓取2015年电影的下载链接. 注:文末有我抓取到的整个列表的下载链接(包括:电影名称和迅雷下载链接) 一 原理简介 其实原理都跟第一篇文章差不多,不同的是鉴于这个网站的分类列表实在太多,

JAVA爬虫 WebCollector

爬虫简介: WebCollector是一个无须配置.便于二次开发的JAVA爬虫框架(内核),它提供精简的的API,只需少量代码即可实现一个功能强大的爬虫. 爬虫内核: WebCollector致力于维护一个稳定.可扩的爬虫内核,便于开发者进行灵活的二次开发.内核具有很强的扩展性,用户可以在内核基础上开发自己想要的爬虫.源码中集成了Jsoup,可进行精准的网页解析. 量级: WebCollector最常用的爬取器BreadthCrawler使用2^24的布隆过滤器进行URL管理,可处理2^24量级

webmagic的设计机制及原理-如何开发一个Java爬虫 转

此文章是webmagic 0.1.0版的设计手册,后续版本的入门及用户手册请看这里:https://github.com/code4craft/webmagic/blob/master/user-manual.md 之前就有网友在博客里留言,觉得webmagic的实现比较有意思,想要借此研究一下爬虫.最近终于集中精力,花了三天时间,终于写完了这篇文章.之前垂直爬虫写了一年多,webmagic框架写了一个多月,这方面倒是有一些心得,希望对读者有帮助. webmagic的目标 一般来说,一个爬虫包括

Java 爬虫工具Jsoup解析

Jsoup是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址.HTML 文本内容.它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据. jsoup 的主要功能如下: 1. 从一个 URL,文件或字符串中解析 HTML: 2. 使用 DOM 或 CSS 选择器来查找.取出数据: 3. 可操作 HTML 元素.属性.文本: jsoup 是基于 MIT 协议发布的,可放心使用于商业项目. jsoup 可以从包括字符串.URL

爬虫6:多页面队列Java爬虫

之前写过很多单页面python爬虫,感觉python还是很好用的,这里用java总结一个多页面的爬虫,迭代爬取种子页面的所有链接的页面,全部保存在tmp路径下. 1 序言 实现这个爬虫需要两个数据结构支持,unvisited队列(priorityqueue:可以适用pagerank等算法计算出url重要度)和visited表(hashset:可以快速查找url是否存在):队列用于实现宽度优先爬取,visited表用于记录爬取过的url,不再重复爬取,避免了环.java爬虫需要的工具包有httpc

爬虫入门 手写一个Java爬虫

本文内容 涞源于  罗刚 老师的 书籍 << 自己动手写网络爬虫一书 >> ; 本文将介绍 1: 网络爬虫的是做什么的?  2: 手动写一个简单的网络爬虫; 1: 网络爬虫是做什么的?  他的主要工作就是 跟据指定的url地址 去发送请求,获得响应, 然后解析响应 , 一方面从响应中查找出想要查找的数据,另一方面从响应中解析出新的URL路径, 然后继续访问,继续解析;继续查找需要的数据和继续解析出新的URL路径  . 这就是网络爬虫主要干的工作.  下面是流程图: 通过上面的流程图

Java爬虫系列之实战:爬取酷狗音乐网 TOP500 的歌曲

在前面分享的两篇随笔中分别介绍了HttpClient和Jsoup以及简单的代码案例: Java爬虫系列二:使用HttpClient抓取页面HTML Java爬虫系列三:使用Jsoup解析HTML 今天就来实战下,用他们来抓取酷狗音乐网上的 Top500排行榜音乐.接下来的代码中除了会用到HttpClient和Jsoup之外,还会用到log4j和ehcache,分别用来记录日志和实现缓存,如果看官对这两个不是很熟悉的话,请自行百度,现在网上的入门实例有很多,我就不专门记笔记了. 那为什么会想到爬取

Spark用Java实现二次排序的自定义key

本人在研究Spak,最近看了很多网上的对于SPARK用Java实现二次排序的方法,对于自定义key的做法 基本上都是实现Ordered<>接口,重写$greater.$greater$eq.$less.$less$eq.compare.compareTo方法,定义hashCode.equals····· 感觉好麻烦,其实我们自定义key只是用了里面的compareTo方法,其他的$greater.$greater$eq.$less.$less$eq.compare 不用做任何改动,hashCo

福利贴——爬取美女图片的Java爬虫小程序代码

自己做的一个Java爬虫小程序 废话不多说,先上图. 文件夹命名是用标签缩写,如果大家看得不顺眼可以等下载完成后手动改一下,比如像有强迫症的我一样... 这是挂了一个晚上下载的总大小,不过还有很多因为一些问题没有遍历下载到,而且会产生很多空文件,最下面我附带了一个递归删除空文件夹的小程序代码. 接下来是文件夹内部~ 图片存放位置默认为d:\picture,可在程序中更改,main函数的开头就是,有注释.爬取的网站为http://www.mmonly.cc/,大家有更好的资源网站可以私我. 爬虫源