为什么我们要爬取数据
在大数据时代,我们要获取更多数据,就要进行数据的挖掘、分析、筛选,比如当我们做一个项目的时候,需要大量真实的数据的时候,就需要去某些网站进行爬取,有些网站的数据爬取后保存到数据库还不能够直接使用,需要进行清洗、过滤后才能使用,我们知道有些数据是非常珍贵的。
今天我们使用Jsoup爬取整个页面数据。
什么是Jsoup?
jsoup 是一款 Java 的HTML 解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于JQuery的操作方法来取出和操作数据。该版本包含一个支持 HTML5 的解析器分支,可确保跟现在的浏览器一样解析 HTML 的方法,同时降低了解析的时间和内存的占用。
JSOUP主要功能
-
从一个URL,文件或字符串中解析HTML;
-
使用DOM或CSS选择器来查找、取出数据;
-
可操作HTML元素、属性、文本
详细介绍参考https://www.cnblogs.com/zhangyinhua/p/8037599.html
接下来我们编写代码。
一、首先我们先添加pom.xml(创建项目略过)
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.9.2</version>
</dependency>
二、以搜狐网址为例,首先我们先获取页面**
RequestAndResponseTool,请求当前url的html页面,并封装到我们自己定义的Page对象中
package com.etoak.crawl.page;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import java.io.IOException;
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
package com.etoak.crawl.page;
import com.etoak.crawl.util.CharsetDetector;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.UnsupportedEncodingException;
/*
* page
* 1: 保存获取到的响应的相关内容;
* */
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 ;}
public void setContent(byte[] content) {
this.content = 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;
}
}
}
储存网页数据,FileTool
package com.etoak.crawl.util;
import com.etoak.crawl.page.Page;
import com.etoak.crawl.page.PageParserTool;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/* 本类主要是 下载那些已经访问过的文件*/
public class FileTool {
private static String dirPath;
/**
* getMethod.getResponseHeader("Content-Type").getValue()
* 根据 URL 和网页类型生成需要保存的网页的文件名,去除 URL 中的非文件名字符
*/
private static String getFileNameByUrl(String url, String contentType) {
//去除 http://
//text/html 类型
if (contentType.indexOf("html") != -1) {
url = url.replaceAll("[\\?/:*|<>\"]", "_") + ".html";
return url;
}else{
int i = url.lastIndexOf("/");
url = url.substring(i+1,url.length());
return url;
}
}
/*
* 生成目录
* */
private static void mkdir() {
if (dirPath == null) {
dirPath = Class.class.getClass().getResource("/").getPath() + "temp\\";
}
File fileDir = new File(dirPath);
if (!fileDir.exists()) {
fileDir.mkdir();
}
}
/**
* 保存网页字节数组到本地文件,filePath 为要保存的文件的相对地址
*/
public static void saveToLocal(Page page) {
mkdir();
String fileName = getFileNameByUrl(page.getUrl(), page.getContentType()) ;
String filePath = dirPath + fileName ;
byte[] data = page.getContent();
try {
DataOutputStream out = new DataOutputStream(new FileOutputStream(new File(filePath)));
for (int i = 0; i < data.length; i++) {
out.write(data[i]);
}
out.flush();
out.close();
System.out.println("文件:"+ fileName + "已经被存储在"+ filePath );
} catch (IOException e) {
e.printStackTrace();
}
}
}
方法入口,抓取页面过程,及配置需要抓取的页面。
package com.etoak.crawl.main;
import com.etoak.crawl.link.LinkFilter;
import com.etoak.crawl.link.Links;
import com.etoak.crawl.page.Page;
import com.etoak.crawl.page.PageParserTool;
import com.etoak.crawl.page.RequestAndResponseTool;
import com.etoak.crawl.util.FileTool;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.Set;
public class MyCrawler {
/**
* 使用种子初始化 URL 队列
*
* @param seeds 种子 URL
* @return
*/
private void initCrawlerWithSeeds(String[] seeds) {
for (int i = 0; i < seeds.length; i++){
Links.addUnvisitedUrlQueue(seeds[i]);
}
}
/**
* 抓取过程
*
* @param seeds
* @return
*/
public void crawling(String[] seeds) {
//初始化 URL 队列
initCrawlerWithSeeds(seeds);
//定义过滤器,提取以 http://www.baidu.com 开头的链接
LinkFilter filter = new LinkFilter() {
public boolean accept(String url) {
if (url.startsWith("http://www.baidu.com"))
return true;
else
return false;
}
};
//循环条件:待抓取的链接不空且抓取的网页不多于 1000
while (!Links.unVisitedUrlQueueIsEmpty() && Links.getVisitedUrlNum() <= 1000) {
//先从待访问的序列中取出第一个;
String visitUrl = (String) Links.removeHeadOfUnVisitedUrlQueue();
if (visitUrl == null){
continue;
}
//根据URL得到page;
Page page = RequestAndResponseTool.sendRequstAndGetResponse(visitUrl);
//将保存文件
FileTool.saveToLocal(page);
//将已经访问过的链接放入已访问的链接中;
Links.addVisitedUrlSet(visitUrl);
//得到超链接
Set<String> links = PageParserTool.getLinks(page,"link");
for (String link : links) {
Links.addUnvisitedUrlQueue(link);
System.out.println("新增爬取路径: " + link);
}
Set<String> links2 = PageParserTool.getLinks(page,"img");
for (String link2 : links2) {
Links.addUnvisitedUrlQueue(link2);
System.out.println("新增爬取路径: " + link2);
}
}
}
//main 方法入口
public static void main(String[] args) {
MyCrawler crawler = new MyCrawler();
crawler.crawling(new String[]{"http://www.baidu.com/"});
}
}
总结:
这样我们就可以完成对于java爬虫的一个demo练习,较为简单,里面主要对于Jsoup的一些使用。(每天进步一点点。)