您的位置:首页 > 理论基础 > 计算机网络

java实现简单的网络爬虫(爬取电影天堂电影信息)

2017-05-02 16:38 681 查看
在最开始,我们要在网上下载所用到的jar包,应为这只是一个简单的网络爬虫所以很多包里的内容没有用到。

下面几个包就可以了。并且要引入这些包。

主类Bigdata.java
import org.htmlparser.util.ParserException;

public class Bigdata {
public static void main(String[] args) throws ParserException {
String url = "http://www.dytt8.net";
System.out.println("开始爬取网页");
MyCrawler crawler = new MyCrawler(url);
crawler.crawling(new String[] { url },url);


这个雷氏负责下载成为文本文件的类:

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;

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;

public class DownLoadFile {//重命名
/**
* 根据 url 和网页类型生成需要保存的网页的文件名 去除掉 url 中非文件名字符
*/
public String getFileNameByUrl(String url, String contentType) {
// remove http:// url = url.substring(7);
// text/html类型
if (contentType.indexOf("html") != -1) {//字符串中没有html
url = url.replaceAll("[\\?/:*|<>\"]", "_") + ".txt";//将正则表达式中的那些字符替换成—
return url;
}
// 如application/pdf类型
else {
return url.replaceAll("[\\?/:*|<>\"]", "_") + "."
+ contentType.substring(contentType.lastIndexOf("/") + 1);
}
}

/**
* 保存网页字节数组到本地文件 filePath 为要保存的文件的相对地址
*/
private void saveToLocal(byte[] data, String filePath,String url) {//保存
try {
DataOutputStream out = new DataOutputStream(new FileOutputStream(
new File(filePath),true));
out.writeBytes(url+" ");
for (int i = 0; i < data.length; i++){
out.write(data[i]);
}
out.writeBytes("\r\n");
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}

/* 下载 url 指向的网页 */
public String downloadfile(String url,byte[] responseBody,String url1) {//下载
String filePath = null;
/* 1.生成 HttpClinet 对象并设置参数 */
HttpClient httpClient = new HttpClient();
// 设置 Http 连接超时 5s
httpClient.getHttpConnectionManager().getParams()
.setConnectionTimeout(2000);

/* 2.生成 GetMethod 对象并设置参数 */
GetMethod getMethod = new GetMethod(url);
// 设置 get 请求超时 5s
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 2000);
// 设置请求重试处理
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());
filePath = null;
}

/* 4.处理 HTTP 响应内容 */
// 根据网页 url 生成保存时的文件名

filePath = "d:\\spider\\"+url1 +".txt";
/*+ getFileNameByUrl(url,
getMethod.getResponseHeader("Content-Type")
.getValue());*/
saveToLocal(responseBody, filePath,url);
} catch (HttpException e) {
// 发生致命的异常,可能是协议不对或者返回的内容有问题
System.out.println("Please check your provided http address!");
e.printStackTrace();
} catch (IOException e) {
// 发生网络异常
e.printStackTrace();
} finally {
// 释放连接
getMethod.releaseConnection();
}
return filePath;
}
}
这个类是解析类工具类

import java.awt.SystemColor;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.Remark;
import org.htmlparser.Tag;
import org.htmlparser.Text;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.tags.ParagraphTag;
import org.htmlparser.tags.TitleTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.NodeVisitor;

import testchartset.Testchartset;

public class HtmlParserTool {
// 获取一个网站上的链接,filter 用来过滤链接
public static Set<String> extracLinks(String url, LinkFilter filter,String url2) {

Set<String> links = new HashSet<String>();
try {
Parser parser = new Parser(url);
parser.setEncoding(Testchartset.dectedCode(url));
//parser.setEncoding("gb2312");
/*NodeFilter filtercode = new TagNameFilter("META");
NodeFilter filtercode1 = new TagNameFilter("meta");
OrFilter linkFilter1 = new OrFilter(filtercode, filtercode1);
NodeList node = parser.extractAllNodesThatMatch(linkFilter1);
Node matanode = (Node) node.elementAt(0);
String code = matanode.getText();
int start1 = code.indexOf("charset=");
code = code.substring(start1);
//int end1 = code.indexOf(" ");
//if (end1 == -1)
//end1 = code.index
c029
Of(">");
String encode = code.substring(8, code.length() - 1);
System.out.println(encode);
parser.setEncoding(encode);*/

// 过滤 <frame >标签的 filter,用来提取 frame 标签里的 src 属性所表示的链接
NodeFilter frameFilter = new NodeFilter() {
public boolean accept(Node node) {
if (node.getText().startsWith("frame src=")) {
//System.out.println("true");
return true;
} else {
//System.out.println("flase");
return false;
}
}
};
// OrFilter 来设置过滤 <a> 标签,和 <frame> 标签
OrFilter linkFilter = new OrFilter(new NodeClassFilter(
LinkTag.class), frameFilter);
// 得到所有经过过滤的标签
NodeList list = parser.extractAllNodesThatMatch(linkFilter);
for (int i = 0; i < list.size(); i++) {
Node tag = list.elementAt(i);
if (tag instanceof LinkTag)// <a> 标签
{
//System.out.println(tag.getText());
LinkTag link = (LinkTag) tag;
String linkUrl = link.getLink();// url
if(linkUrl.startsWith(url2)){

}
else{
if(linkUrl.startsWith("ftp")){
linkUrl=null;
}else{
linkUrl = url2+linkUrl;
}
}
//System.out.println(linkUrl);
//System.out.println(linkUrl);
//if (filter.accept(linkUrl))
//System.out.println(linkUrl);
if(linkUrl!=null)
links.add(linkUrl);
} else// <frame> 标签
{
// 提取 frame 里 src 属性的链接如 <frame src="test.html"/>
String frame = tag.getText();
//System.out.println(frame);
int start = frame.indexOf("src=");
//System.out.println(start);
frame = frame.substring(start);
int end = frame.indexOf(" ");
if (end == -1)
end = frame.indexOf(">");
String frameUrl = frame.substring(5, end - 1);
if (filter.accept(frameUrl))
links.add(frameUrl);
}
}
} catch (ParserException e) {
e.printStackTrace();
}
return links;
}
public static byte[] paerserhtml(String url, LinkFilter filter){
//byte[] content = null;
String context = null;
Parser parser;
Pattern p = Pattern.compile("\\s*|\t|\r|\n");

try {
parser = new Parser(url);
//parser.setEncoding("gb2312");
parser.setEncoding(Testchartset.dectedCode(url));
NodeFilter filter1 = new TagNameFilter ("title");
//NodeList nodes = parser.extractAllNodesThatMatch(filter1);
NodeFilter filter2 = new TagNameFilter ("div");
OrFilter linkFilter = new OrFilter(filter1, filter2);
NodeList nodes = parser.extractAllNodesThatMatch(linkFilter);
if(nodes!=null) {
for (int i = 0; i < nodes.size(); i++) {
Node textnode = (Node) nodes.elementAt(i);
String line = textnode.getText();
//System.out.println(line);
String text = textnode.toPlainTextString();
//System.out.println(text);
if(line.contains("Zoom")){
//System.out.println(line.replaceAll(" ", "").replaceAll("\n", ""));
Matcher m = p.matcher(text);
String t = m.replaceAll("");
context += t;
context += "\n";
}/*else{
context += text;
}*/

// if(textnode.getText()
//context += textnode.toPlainTextString();
//context += textnode.getText();
}
//System.out.println(context);
}
/*NodeFilter filter2 = new TagNameFilter ("div");
NodeFilter filter3 = new TagNameFilter ("img");
OrFilter linkFilter = new OrFilter(filter3, filter2);
NodeList nodes_context = parser.extractAllNodesThatMatch(linkFilter);
if(nodes_context!=null) {
for (int i = 0; i < nodes_context.size(); i++) {
Node textnode_context = (Node) nodes_context.elementAt(i);
context += textnode_context.toPlainTextString();
context += textnode_context.getText();
}
}
else{
System.out.println("null");
}*/

} catch (ParserException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}finally{
}
if(context==null){
context = "页面找不到了";
return context.getBytes();
}else{
public interface LinkFilter {
public boolean accept(String url);
}


return context.getBytes(); } } }

定义一个接口
public interface LinkFilter {
public boolean accept(String url);
}



队列类
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.Queue;

public class LinkQueue {
// 已访问的 url 集合
private static Set visitedUrl = new HashSet();
// 待访问的 url 集合
private static Queue unVisitedUrl = new PriorityQueue();

// 获得URL队列
public static Queue getUnVisitedUrl() {
return unVisitedUrl;
}

// 添加到访问过的URL队列中
public static void addVisitedUrl(String url) {
visitedUrl.add(url);
}

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

// 未访问的URL出队列
public static Object unVisitedUrlDeQueue() {
return unVisitedUrl.poll();
}

// 保证每个 url 只被访问一次
public static void addUnvisitedUrl(String url) {
if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
&& !unVisitedUrl.contains(url))
unVisitedUrl.add(url);
}

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

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

}



爬虫类
import java.util.Set;

import org.htmlparser.util.ParserException;

public class MyCrawler {
/**
* 使用种子初始化 URL 队列
*
* @return
* @param seeds
*            种子URL
*/
private	String url1 = null;
private void initCrawlerWithSeeds(String[] seeds) {
for (int i = 0; i < seeds.length; i++)
LinkQueue.addUnvisitedUrl(seeds[i]);
}
public MyCrawler(String url){//构造函数
this.url1 = url.replaceAll("[\\?/:*|<>\"]", "_");
}
/**
* 抓取过程
*
* @return
* @param seeds
* @throws ParserException
*/
public void crawling(String[] seeds,final String url2) throws ParserException { // 定义过滤器,提取以http://www.lietu.com开头的链接
LinkFilter filter = new LinkFilter() {
public boolean accept(String url) {
if (url.startsWith(url2))
return true;
else
return false;
}
};
// 初始化 URL 队列
initCrawlerWithSeeds(seeds);
// 循环条件:待抓取的链接不空且抓取的网页不多于1000
while (!LinkQueue.unVisitedUrlsEmpty()
&& LinkQueue.getVisitedUrlNum() <= 10000000) {
// 队头URL出队列
String visitUrl = (String) LinkQueue.unVisitedUrlDeQueue();
byte[] context = HtmlParserTool.paerserhtml(visitUrl, filter);
System.out.println(LinkQueue.getVisitedUrlNum());
if (visitUrl == null)
continue;
DownLoadFile downLoader = new DownLoadFile();
// 下载网页

//System.out.println(context.toString());
downLoader.downloadfile(visitUrl,context,url1);
// 该 url 放入到已访问的 URL 中
LinkQueue.addVisitedUrl(visitUrl);
// 提取出下载网页中的 URL
Set<String> links = HtmlParserTool.extracLinks(visitUrl, filter,url2);
// 新的未访问的 URL 入队
for (String link : links) {
LinkQueue.addUnvisitedUrl(link);
}
}
}
}



下面这个类有点特殊也有不完善的地方,是一个解析网页编码的类获得网页到底是什么编码的,然后在告诉下载器解析器以什么编码解析下载中文。
应为电影天堂网站所有网页都是gb2312编码的所以我就为了成功率注释掉了代码改为只返回gb2312编码。以后改好了是可以适应任何一种编码的。
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.tags.BodyTag;
import org.htmlparser.tags.Html;
import org.htmlparser.util.NodeIterator;
import org.htmlparser.util.NodeList;

public class Testchartset{
private static final String oriEncode = "UTF-8,utf-8,gb2312,gbk,iso-8859-1";

public static void main(String [] args){

String url="http://www.dytt8.net/";
String decode=dectedCode(url);
System.out.println(decode);

}
/**
* 检测字符级
* @param url
* @return
*/
/*public static String dectedEncode(String url) {
String[] encodes = oriEncode.split(",");
for (int i = 0; i < encodes.length; i++) {
if (dectedCode(url, encodes[i])) {
return encodes[i];
}
}
return null;
}*/

public static String dectedCode(String url) {
/*try {
Parser parser = new Parser(url);
//parser.setEncoding(encode);
NodeFilter filtercode = new TagNameFilter("META");
NodeFilter filtercode1 = new TagNameFilter("meta");
OrFilter linkFilter = new OrFilter(filtercode, filtercode1);
NodeList node = parser.extractAllNodesThatMatch(linkFilter);
Node matanode = (Node) node.elementAt(0);
String code = matanode.getText();
//System.out.println(code);
int start = code.indexOf("charset=");
code = code.substring(start);
//System.out.println(code);
int end = code.indexOf(" ");
//System.out.println(end);
if(end==-1){
end=code.length();
}
String encode1 = code.substring(8, end-1);
String encode2 = code.substring(9,end-1);
//System.out.println(encode1+"chkc"+encode2);
String[] encodes = oriEncode.split(",");
if(encode1.equals(encodes[0])||encode2.equals(encodes[0])||encode1.equals(encodes[1])||encode2.equals(encodes[1]))
return "utf-8";
if(encode1.equals(encodes[2])||encode2.equals(encodes[2]))
return "gb2312";
if(encode1.equals(encodes[3])||encode2.equals(encodes[3]))
return "gbk";
if(encode1.equals(encodes[4])||encode2.equals(encodes[4]))
return "iso-8859-1";
} catch (Exception e) {

}*/

return "gb2312";
}
}



经过爬取了上万个页面这个程序停了下来
可以看到爬下来的内容我都是以网址开头空格之后就是电影的各项内容。这样爬取页面也是为了在hadoop中做数据分析。我的确做了一个数据分析电影明星哪一个成就最高,
但是因为数据集的原因结果不是很理想。在之后的博客中我可能会把分析步骤发出来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: