上一篇简单的实现了获取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. 常用的Redis客户端的并发模型(转)

      伪代码模型 # get lock : timestamp = current Unix time + lock = SETNX lock.foo timestamp or (now() > ...

  2. android Eclipse there no select

    点mainactivity类 右键  run as 进行 配置 就可运行

  3. vue & $data & data

    vue & $data & data vm.a === vm.$data.a https://vuejs.org/v2/api/#data https://flaviocopes.co ...

  4. Java对象空间分配流程

    对象空间分配流程如下:   针对这个流程,分别解释一下每一个选项的使用场景. 栈上分配: 栈上分配的基础在于逃逸分析,逃逸分析可以得到三种对象的逃逸状态. 全局逃逸:一个对象的引用逃出了方法或者线程. ...

  5. echarts tooltip 自定义formatter怎么设置颜色?

    formatter: function(params) { var result = ''; params.forEach(function (item) { result += item.marke ...

  6. 英文报道:China challenged Australian warships in South China Sea, reports say

    学习地道新闻英语表达,以下文章来自CNN By Ben Westcott and Jamie Tarabay, CNN Updated 0830 GMT (1630 HKT) April 20, 20 ...

  7. 关于upper、lower bound 的探讨

    lower_bound(A, A+n, x) - A  返回第一个大于等于x的数的下标 lower_bound(A, A+n, x) - A - 1 返回最后一个小于x的数的下标 upper_boun ...

  8. 关于qt中的tr()函数

    关于qt中的tr()函数 在论坛中漂,经常遇到有人遇到tr相关的问题.用tr的有两类人: (1)因为发现中文老出问题,然后搜索,发现很多人用tr,于是他也开始用tr (2)另一类人,确实是出于国际化的 ...

  9. 【比赛】HNOI2018 游戏

    考试的时候线段树区间查询的return条件打成了l==r....于是光荣爆20(线段树都不会打了?) 看膜博士的题解 #include<bits/stdc++.h> #define ui ...

  10. POJ.2251 Dungeon Master (三维BFS)

    POJ.2251 Dungeon Master (三维BFS) 题意分析 你被困在一个3D地牢中且继续寻找最短路径逃生.地牢由立方体单位构成,立方体中不定会充满岩石.向上下前后左右移动一个单位需要一分 ...