网络采集器Demo:Jsoup+Java多线程实现[爬虫](下)
2015-11-14 20:53
429 查看
ailab-mltk:http://blog.csdn.net/qdhy199148/article/details/49403585
下半部分主要是介绍Java的多线程编程。
我们得到了所有的有效链接和获取各个链接页面有效内容的方法,帮助大家回忆一下:
1.
2.
这里要介绍的多线程编程模式是Java1.5之后推出的callable+future,Java1.5的这个多线程API相比之前老旧的runable还是提升了不少,主要有两点:
1.支持每个子线程能够传回returne值
2.加入了Java自己非常强大的线程池模式
所以啊,各位师弟今后再从什么《Java编程那些事儿》、《Java开发实战经典》这种老书中看到关于runable复杂的描述,就不必纠结下去了。
很多废话,接下来说一下callable+future多线程编程的两个重要部分
首先是线程部分,也就是以前rubable中的继承类和run方法,关于上面的爬虫问题,我们将每个子线程设计成爬取单个有效链接内部的有效文本内容,并存入指定路径下的文件里。线程类的代码如下:
我们来分析一下,由于call方法不能传入参数,所以如果需要传入必要的参数,需要以构造方法初始化线程类成员变量的形式完成(~_~)。另外注意:call方法是可以有返回值的,返回值类型需要在两个地方声明,一个是传统的方法名前面,另一个在声明实现<span style="font-family: Arial, Helvetica, sans-serif;">callable<></span>接口中的方括号里,完成单线程全部的任务之后,返回要返回的值即可,如果没什么返回的,随便返回一个什么,接受的时候不要管他就行了。
线程类call方法中调用的WriteContentIntoFile方法如下:
Java I/O,看看就可以了,没什么可说的。
基本的线程类编写好了,接下来我们需要调用他,调用刚才说明的线程类的代码:
载入全部有效链接、爬取内容的磁盘存放路径作为传入子线程的准备,接下来是调用多线程的关键,这里需要初始化一个ExecutorService对象,我们暂且称为exes,没有exes就不能正常地调用callable线程。exec的初始化有多种形式,介绍如下:
1.Executors.newCachedThreadPool()(Java线程池动态分配线程)
2.Executors.newFixedThreadPool(int);(Java线程池分配固定数量的同时运行线程数,其余排队)
3.Executors.newScheduledThreadPool(int);(Java线程池分配固定大小的线程占用资源数,超过排队,用的很少)
4.Executors.newSingleThreadExecutor();(Java线程池每次只分配一个激活的线程,其余排队,相当于单线程)
接下来,我们设计了一个线程集合,用于存储新建的线程任务。集合的类型是Future<Boolean>,Future就是之前提到的callable+future中的future,代表一个运行的线程,也可以理解为用来接收子线程call方法返回值工具,<>方括号中的变量类型就是在子线程中声明的返回值类型。
循环中,新建线程任务后,通过exes的submit方法提交线程(注意,此刻线程就开始运行了,一定要注意线程的生命周期)
后一个循环以线程集合中的每个线程作为基本单位,并用future变量接受各个子线程的返回值。这里也要注意,不要以为没什么可返回的就忽略了这一步,接受返回值的目的不但是接收所谓的子线程返回值,也有线程等待的功能(关注线程安全的童鞋应该清楚线程等待的重要性),全部线程结束后,你可以再次自有地进行线程操作。
好了,师弟们,结合上一节试试编写一个简单的多线程爬虫。
下半部分主要是介绍Java的多线程编程。
我们得到了所有的有效链接和获取各个链接页面有效内容的方法,帮助大家回忆一下:
1.
public Set<ExactLinks> filterUrl(String seedUrl)
2.
public String getParagraphContent(String pageUrl)
这里要介绍的多线程编程模式是Java1.5之后推出的callable+future,Java1.5的这个多线程API相比之前老旧的runable还是提升了不少,主要有两点:
1.支持每个子线程能够传回returne值
2.加入了Java自己非常强大的线程池模式
所以啊,各位师弟今后再从什么《Java编程那些事儿》、《Java开发实战经典》这种老书中看到关于runable复杂的描述,就不必纠结下去了。
很多废话,接下来说一下callable+future多线程编程的两个重要部分
首先是线程部分,也就是以前rubable中的继承类和run方法,关于上面的爬虫问题,我们将每个子线程设计成爬取单个有效链接内部的有效文本内容,并存入指定路径下的文件里。线程类的代码如下:
public class FetchPageContentThread implements Callable<Boolean> { private ExactLinks links; private String folderPath; public FetchPageContentThread(ExactLinks links, String folderPath) { super(); this.links = links; this.folderPath = folderPath; } @Override public Boolean call() throws Exception { System.out.println(this.links.getUrl()); String content = new FetchPaContentFromPage() .getParagraphContent(this.links.getUrl()); Boolean flagSucc = false; if (!content.equals("") && content != null) { String filePath = this.folderPath + links.getUrlMD5() + ".txt"; flagSucc = new WriteContentIntoFile().writeContent(filePath, content); } return flagSucc; } }
我们来分析一下,由于call方法不能传入参数,所以如果需要传入必要的参数,需要以构造方法初始化线程类成员变量的形式完成(~_~)。另外注意:call方法是可以有返回值的,返回值类型需要在两个地方声明,一个是传统的方法名前面,另一个在声明实现<span style="font-family: Arial, Helvetica, sans-serif;">callable<></span>接口中的方括号里,完成单线程全部的任务之后,返回要返回的值即可,如果没什么返回的,随便返回一个什么,接受的时候不要管他就行了。
线程类call方法中调用的WriteContentIntoFile方法如下:
public boolean writeContent(String filePath, String content) { try { File file = new File(filePath); if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); fw.write(content); fw.close(); return true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } }
Java I/O,看看就可以了,没什么可说的。
基本的线程类编写好了,接下来我们需要调用他,调用刚才说明的线程类的代码:
public class ExecFetchSinaNewsContent { private Set<ExactLinks> links; private String folderPath; public ExecFetchSinaNewsContent() { super(); // TODO Auto-generated constructor stub } public ExecFetchSinaNewsContent(Set<ExactLinks> links, String folderPath) { super(); this.links = links; this.folderPath = folderPath; } public void initSinaLinks() { FetchLinksFromPage fetchObj = new FetchLinksFromPage(); setLinks(fetchObj.filterUrl(CrawlSeedParam.SINA_NEWS)); } public void initFileFolderPath() { setFolderPath("./file/crawler_test/"); } public void execFetchSinaContentThread() { // TODO 改成手动传入参数 this.initSinaLinks(); this.initFileFolderPath(); // 创建线程池 ExecutorService exes = Executors.newFixedThreadPool(5); Set<Future<Boolean>> setThreads = new java.util.HashSet<Future<Boolean>>(); for (ExactLinks links : getLinks()) { // 创建线程任务 FetchPageContentThread fetchThread = new FetchPageContentThread( links, getFolderPath()); // 提交线程任务 setThreads.add(exes.submit(fetchThread)); } // 执行多线程任务 for (Future<Boolean> future : setThreads) { try { Boolean flagSucc = future.get(); // TODO delete print System.out.println(flagSucc); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public Set<ExactLinks> getLinks() { return links; } public void setLinks(Set<ExactLinks> links) { this.links = links; } public String getFolderPath() { return folderPath; } public void setFolderPath(String folderPath) { this.folderPath = folderPath; } }
载入全部有效链接、爬取内容的磁盘存放路径作为传入子线程的准备,接下来是调用多线程的关键,这里需要初始化一个ExecutorService对象,我们暂且称为exes,没有exes就不能正常地调用callable线程。exec的初始化有多种形式,介绍如下:
1.Executors.newCachedThreadPool()(Java线程池动态分配线程)
2.Executors.newFixedThreadPool(int);(Java线程池分配固定数量的同时运行线程数,其余排队)
3.Executors.newScheduledThreadPool(int);(Java线程池分配固定大小的线程占用资源数,超过排队,用的很少)
4.Executors.newSingleThreadExecutor();(Java线程池每次只分配一个激活的线程,其余排队,相当于单线程)
接下来,我们设计了一个线程集合,用于存储新建的线程任务。集合的类型是Future<Boolean>,Future就是之前提到的callable+future中的future,代表一个运行的线程,也可以理解为用来接收子线程call方法返回值工具,<>方括号中的变量类型就是在子线程中声明的返回值类型。
循环中,新建线程任务后,通过exes的submit方法提交线程(注意,此刻线程就开始运行了,一定要注意线程的生命周期)
后一个循环以线程集合中的每个线程作为基本单位,并用future变量接受各个子线程的返回值。这里也要注意,不要以为没什么可返回的就忽略了这一步,接受返回值的目的不但是接收所谓的子线程返回值,也有线程等待的功能(关注线程安全的童鞋应该清楚线程等待的重要性),全部线程结束后,你可以再次自有地进行线程操作。
好了,师弟们,结合上一节试试编写一个简单的多线程爬虫。
相关文章推荐
- HttpClient在Android网络通信中的应用
- 10010---JavaWeb基础--HttpServlet
- MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006
- iOS9的网络请求简述
- “App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. T
- Android网络优化3--基本方法
- Tinyhttpd源码剖析(二)
- 简单的网络编程--服务器,客户端呼应
- Tinyhttpd源码剖析(一)
- CentOS7 最小化安装后,安装配置并启动httpd的步骤
- Ubuntu 桌面设置之网络环境的设置
- Ubuntu通过修改配置文件进行网络配置
- android 获取手机上面的IMSI序列号,IMEI,MAC地址,IP地址,当前时间,当前经纬度,获取网络类型
- java快速教程-http://www.cnblogs.com/vamei/archive/2013/03/31/2991531.html
- 黑马程序员——网络编程(一)
- 映射网络盘
- Http些许己见
- 网络设备将网口切换到业务板以后为什么会报各种不正确的信息?
- Android网络优化2--HttpClinet
- cenos7.1 安装openstack kilo 心得之三添加网络时间协议