上一篇简单的实现了获取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

Java爬虫(二)的更多相关文章

  1. Java爬虫系列二:使用HttpClient抓取页面HTML

    爬虫要想爬取需要的信息,首先第一步就要抓取到页面html内容,然后对html进行分析,获取想要的内容.上一篇随笔<Java爬虫系列一:写在开始前>中提到了HttpClient可以抓取页面内 ...

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

    之前就有网友在博客里留言,觉得webmagic的实现比较有意思,想要借此研究一下爬虫.最近终于集中精力,花了三天时间,终于写完了这篇文章.之前垂直爬虫写了一年多,webmagic框架写了一个多月,这方 ...

  3. 爬虫6:多页面增量Java爬虫-sina主页

    之前写过很多单页面python爬虫,感觉python还是很好用的,这里用java总结一个多页面的爬虫,迭代爬取种子页面的所有链接的页面,全部保存在tmp路径下. 1 序言 实现这个爬虫需要两个数据结构 ...

  4. JAVA爬虫 WebCollector

    JAVA爬虫 WebCollector 爬虫简介: WebCollector是一个无须配置.便于二次开发的JAVA爬虫框架(内核),它提供精简的的API,只需少量代码即可实现一个功能强大的爬虫. 爬虫 ...

  5. 爬虫入门 手写一个Java爬虫

    本文内容 涞源于  罗刚 老师的 书籍 << 自己动手写网络爬虫一书 >> ; 本文将介绍 1: 网络爬虫的是做什么的?  2: 手动写一个简单的网络爬虫; 1: 网络爬虫是做 ...

  6. Java 爬虫学习

    Java爬虫领域最强大的框架是JSoup:可直接解析具体的URL地址(即解析对应的HTML),提供了一套强大的API,包括可以通过DOM.CSS选择器,即类似jQuery方式来取出和操作数据.主要功能 ...

  7. java 爬虫

    由于项目需求,综合了几种考虑方案,准备使用java 爬虫进行数据的获取,不用自己去费劲的想逻辑的实现 使用java爬虫之前,我们必须要掌握的知识: 1. 对前端HTML的元素有一定的认识 2. 使用h ...

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

    此文章是webmagic 0.1.0版的设计手册,后续版本的入门及用户手册请看这里:https://github.com/code4craft/webmagic/blob/master/user-ma ...

  9. java爬虫入门

    本文内容 涞源于  罗刚 老师的 书籍 << 自己动手写网络爬虫一书 >> ; 本文将介绍 1: 网络爬虫的是做什么的?  2: 手动写一个简单的网络爬虫; 1: 网络爬虫是做 ...

随机推荐

  1. smokping的部署使用

    本文是介绍如何的使用smokeping来监控idc机房的网络质量情况,从监控图上的延时与丢包能分辨出你机房的网络是否稳定,是否为多线,是否为BGP机房,到各城市的3个运行商网络各是什么情况,如果出现问 ...

  2. iOS OC语言原生开发的IM模块--RChat

    iOS OC语言原生开发的IM模块,用于项目中需要原生开发IM的情况,具备发送文字.表情.语音.图片.视频等完整功能,包含图片预览视频播放等功能,此项目将会长期更新如有问题可以提出,我的邮箱:fshm ...

  3. Codeforces 618D Hamiltonian Spanning Tree(树的最小路径覆盖)

    题意:给出一张完全图,所有的边的边权都是 y,现在给出图的一个生成树,将生成树上的边的边权改为 x,求一条距离最短的哈密顿路径. 先考虑x>=y的情况,那么应该尽量不走生成树上的边,如果生成树上 ...

  4. 【bzoj5157】[Tjoi2014]上升子序列 树状数组

    题目描述 求一个数列本质不同的至少含有两个元素的上升子序列数目模10^9+7的结果. 题解 树状数组 傻逼题,离散化后直接使用树状数组统计即可.由于要求本质不同,因此一个数要减去它前一次出现时的贡献( ...

  5. 洛谷 P1495 曹冲养猪

    这是一道标准的孙子定理的题,题意浅显,思路明确 然后我就交了整整16遍啊,欺负人啊,题解暴力就能过,我就TLE ..悲惨的提交记录 下面是题面 题目描述 自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干 ...

  6. 【CodeChef PREFIXOR】Prefix XOR

    https://odzkskevi.qnssl.com/f0fbdb108ec813b1294f8f714805963b?v=1502083692 网上搜到的题解: http://blog.csdn. ...

  7. Glide加载图片并保存到本地返回file,bitmap

    有很多朋友都想要知道32位和64位的区别是什么,因为大家都拿不准自己要装32位系统还是64位系统,因此彷徨是必然的.那么到底区别是啥咧?如果大家想要知道的话,下面就让小编给大家带来32位和64位的区别 ...

  8. 20165218 2017-2018-2 《Java程序设计》课程总结

    20165218 2017-2018-2 <Java程序设计>课程总结 一.每周作业链接汇总 20165218 我期望的师生关系 20165218 学习基础和C语言基础调查 2016521 ...

  9. Linux 查询命令

    which       查看可执行文件的位置 whereis    查看文件的位置 locate       配合数据库查看文件位置 find          实际搜寻硬盘查询文件名称 (find也 ...

  10. Hbase(一)基础知识

    一.Hbase数据库介绍 1.简介 HBase 是 BigTable 的开源 java 版本.是建立在 HDFS 之上,提供高可靠性.高性能.列存储. 可伸缩.实时读写 NoSQL 的数据库系统. N ...