简易Java爬虫制作

本篇csdn博客链接

##一、文章来由

本来最近任务挺多,但是今天想放松一下,正巧Bill喜欢玩英语配音,而配音都是在配音软件的云上,我想把那些都拿到,于是就写一了一个爬虫,接着就有了这篇爬虫教程~~

##二、爬虫!!爬虫!!

首先要搞清什么叫爬虫~~

网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。

当然Java本身应该不算是脚本语言(一个脚本通常是解释运行而非编译,Java是不是解释型语言现在还存在争议),但是做这么高级的事情,确实有点像脚本。

那为什么叫简易爬虫呢,因为是直接用 BufferedReader 读 URL 中的 HTML ,然后用正则表达式去匹配是不是我要的网页。这种简易爬虫,只能用在页面没有特殊限制或者反爬虫机制的地方,如果页面有登陆验证或者是异步等反爬虫,通过这个URL 是不可能一次拿到真实地址的(一般会错误被导到另外一个页面)

通常,爬虫需要一个种子页面,是一个源 URL 地址,通过这个一层层爬下去

Bill 以前用过 httpclient 的 jar 包做过稍微复杂一点的爬虫,这个 jar 包可以把 HTML 用自己的数据结构存起来,形成一棵树结构(HTML 本身就是一种树结构的标记语言),然后提供各种过滤方法,可以找到你要的特定信息。

当然还有各种爬虫框架、模拟用户输入的包,可以完成更高难度的操作,绕过诸如淘宝这类有反爬虫技术的网站,这个大家有兴趣再去研究。

##三、虫子制作过程

我提供了源码下载(当然是免金币的~~),所以,在文章中就不复制所有源代码了,只把关键代码、主要思想和关键函数讲一讲。

爬下静态网页的代码,可以很明显看到,只是直接用 BufferedReader 获取 URL 中的 HTML,而这些基本的函数和类,在Java标准类库中都有,不用额外导包。

爬虫的种子地址当然是 bill 希望去爬的趣配音web页面地址,我希望得到这些web页面里面,是我配音的、真实MP4地址~~

代码中我是用 url = “http://video.qupeiyin.cn/index.php?m=home&c=show&a=share&id=4102907”; 开始爬的~~

// 爬下静态网页
	public String getOneHtml(String htmlurl) throws IOException {
		URL url;
		String temp;
		StringBuffer sb = new StringBuffer();
		try {
			url = new URL(htmlurl);
			BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));// 读取网页全部内容
			while ((temp = in.readLine()) != null) {
				sb.append(temp);
			}
			in.close();
		} catch (MalformedURLException me) {
			System.out.println("你输入的URL格式有问题!请仔细输入");
			me.getMessage();
			throw me;
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		}

		// System.out.println(sb.toString()); //成功爬下了静态网页
		return sb.toString();
	}

下面的代码是,通配符匹配的代码:

// 通过通配符匹配
	public String getInfo(String s) {
		String regex;
		String res = "";

		// 通配符
		regex = "This is bill";
		Pattern pa = Pattern.compile(regex, Pattern.CANON_EQ);
		Matcher ma = pa.matcher(s);

		if (ma.find()) {
			// 成功找到

			regex = "http://cdn.qupeiyin.cn/.*?/.*?.mp4";
			pa = Pattern.compile(regex, Pattern.CANON_EQ);
			ma = pa.matcher(s);

			if (ma.find())
				res += (ma.group());
			
			return res;
		}

		else
			return null;

	}

关于多线程共享,这里要粘贴一段test的代码:

///模拟售票的程序,三个线程共享一个ticket
package robot;

public class Bill implements Runnable {

	public Bill() {

	}

	public Bill(String name) {
		this.name = name;
	}

	private int ticket = 10;

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			if (ticket > 0) {
				System.out.println(Thread.currentThread().getName() + "--" + this.ticket--);
			}
		}
	}

	public static void main(String[] args) {
		Bill B3 = new Bill();
		new Thread(B3, "线程1").start();
		new Thread(B3, "线程2").start();
		new Thread(B3, "线程3").start();
	}

	private String name;

}

我在代码中把,爬到的有用地址写到了txt里面。具体追加过程见附录。

关于多线程,Java提供了不止一种方法,可能你会说,其实继承 Thread 类,跟实现 Runnable 接口差不多,因为 其实Thread中的run方法调用的是Runnable接口的run方法而已。但是他们还是有相当多的不一样,详情可以参考这篇文章,写的不错,但是!!

这篇文章里面也有不少错误

,具体需要我们自己仔细分辨!!毕竟自己搞懂的才是真正自己的,如果要看,可以结合评论一起看~~

也差不多就这样了,主要还是读源码,理解,做实验~~

##四、小结

要对写出爬虫有信心,其实没那么难~~~

源代码下载地址:http://download.csdn.net/detail/scythe666/8973067

##五、附:

####Java追加文件内容的三种方法

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;

public class AppendFile {
	
	public static void method1(String file, String conent) {   
        BufferedWriter out = null;   
        try {   
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));   
            out.write(conent);   
        } catch (Exception e) {   
            e.printStackTrace();   
        } finally {   
            try {   
            	if(out != null){
            		out.close();   
                }
            } catch (IOException e) {   
                e.printStackTrace();   
            }   
        }   
    }   
  
    /**  
     * 追加文件:使用FileWriter  
     *   
     * @param fileName  
     * @param content  
     */  
    public static void method2(String fileName, String content) { 
    	FileWriter writer = null;
        try {   
            // 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件   
            writer = new FileWriter(fileName, true);   
            writer.write(content);     
        } catch (IOException e) {   
            e.printStackTrace();   
        } finally {   
            try {   
            	if(writer != null){
            		writer.close();   
            	}
            } catch (IOException e) {   
                e.printStackTrace();   
            }   
        } 
    }   
  
    /**  
     * 追加文件:使用RandomAccessFile  
     *   
     * @param fileName 文件名  
     * @param content 追加的内容  
     */  
    public static void method3(String fileName, String content) { 
    	RandomAccessFile randomFile = null;
        try {   
            // 打开一个随机访问文件流,按读写方式   
            randomFile = new RandomAccessFile(fileName, "rw");   
            // 文件长度,字节数   
            long fileLength = randomFile.length();   
            // 将写文件指针移到文件尾。   
            randomFile.seek(fileLength);   
            randomFile.writeBytes(content);    
        } catch (IOException e) {   
            e.printStackTrace();   
        } finally{
        	if(randomFile != null){
        		try {
					randomFile.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
        	}
        }
    }  

	public static void main(String[] args) {
		try{
			File file = new File("d://text.txt");
			if(file.createNewFile()){
				System.out.println("Create file successed");
			}
			method1("d://text.txt", "123");
			method2("d://text.txt", "123");
			method3("d://text.txt", "123");
		}catch(Exception e){
			System.out.println(e);
		}
	}
}

—END—


####参考文献

[1] http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html

[2] http://blog.csdn.net/liangoo7/article/details/7882773

[3] http://blog.csdn.net/malik76/article/details/6408726