多线程爬虫Java调用wget下载文件,独立线程读取输出缓冲区
写了个抓取appstore的,要抓取大量的app,本来是用httpclient,但是效果不理想,于是直接调用wget下载,但是由于标准输出、错误输出的原因会导致卡住,另外wget也会莫名的卡住。
所以我采用:
一、独立线程读取输出信息;
二、自己实现doWaitFor方法来代替api提供的waitFor()方法,避免子进程卡死。
三、设置超时,杀死wget子进程,没有正确返回的话,重试一次,并把超时时间加倍;
有了以上操作,wget不会卡死,就算卡住了也会因为超时被干掉再重试一次,所以绝大部分的app可以被抓取下来。
import com.google.common.io.Files;
import com.xxx.appstore.service.crawler.CalcMD5Service;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class CrawlerUtils { public static final String APK_DOWNLOAD_PATH = "/data/appstore/category/";
private static Logger LOGGER = LoggerFactory.getLogger(CrawlerUtils.class); /**
* 使用wget下载文件
*
* @param displayName appName
* @param category 分类
* @param download_url 下载地址
* @return 成功返回文件路径,失败返回null
*/
public static String downloadFileByWget(String displayName, String category, String download_url) {
if (StringUtils.isBlank(displayName) || StringUtils.isBlank(category) || StringUtils.isBlank(download_url)) {
LOGGER.info("downloadFileByWget ERROR, displayName:{}, category:{}, download_url:{}", new Object[]{displayName, category, download_url});
return null;
}
String fileName = CalcMD5Service.encoder(displayName + RandomUtils.nextInt(1000));
String seed = CalcMD5Service.encoder(category);
String midPath = StringUtils.left(seed, 10);
String filePath = APK_DOWNLOAD_PATH + midPath + "/" + fileName + ".apk";
File file = new File(filePath);
try {
Files.createParentDirs(file);
} catch (IOException e) {
LOGGER.warn("IOException", e);
return null;
}
int retry = 2;
int res = -1;
int time = 1;
while (retry-- > 0) {
ProcessBuilder pb = new ProcessBuilder("wget", download_url, "-t", "2", "-T", "10", "-O", filePath);
LOGGER.info("wget shell: {}", pb.command());
Process ps = null;
try {
ps = pb.start();
} catch (IOException e) {
LOGGER.error("IOException", e);
}
res = doWaitFor(ps, 30 * time++);
if (res != 0) {
LOGGER.warn("Wget download failed...");
} else {
break;
}
}
if (res != 0) {
return null;
}
return filePath;
} /**
* @param ps sub process
* @param timeout 超时时间,SECONDS
* @return 正常结束返回0
*/
private static int doWaitFor(Process ps, int timeout) {
int res = -1;
if (ps == null) {
return res;
}
List<String> stdoutList = new ArrayList<>();
List<String> erroroutList = new ArrayList<>();
boolean finished = false;
int time = 0;
ThreadUtil stdoutUtil = new ThreadUtil(ps.getInputStream(), stdoutList);
ThreadUtil erroroutUtil = new ThreadUtil(ps.getErrorStream(), erroroutList);
//启动线程读取缓冲区数据
stdoutUtil.start();
erroroutUtil.start();
while (!finished) {
time++;
if (time >= timeout) {
LOGGER.info("Process wget timeout 30s, destroyed!");
ps.destroy();
break;
}
try {
res = ps.exitValue();
finished = true;
} catch (IllegalThreadStateException e) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) { }
}
}
return res;
}
}
import org.apache.commons.io.Charsets; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List; public class ThreadUtil implements Runnable {
// 设置读取的字符编码
private String character = Charsets.UTF_8.displayName();
private List<String> list;
private InputStream inputStream; public ThreadUtil(InputStream inputStream, List<String> list) {
this.inputStream = inputStream;
this.list = list;
} public void start() {
Thread thread = new Thread(this);
thread.setDaemon(true);//将其设置为守护线程
thread.start();
} public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(inputStream, character));
String line = null;
while ((line = br.readLine()) != null) {
list.add(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//释放资源
inputStream.close();
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} }
多线程爬虫Java调用wget下载文件,独立线程读取输出缓冲区的更多相关文章
- Java调用第三方dll文件的使用方法 System.load()或System.loadLibrary()
Java调用第三方dll文件的使用方法 public class OtherAdapter { static { //System.loadLibrary("Connector") ...
- PHP利用Curl实现多线程抓取网页和下载文件
PHP 利用 Curl 可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,然而因为php语言本身不支持多线程,所以开发爬虫程序效率并不高,一般采集 数据可以利用 PHPquery ...
- java调用dll/so文件
大家都知道用C++编写的程序如果用于windows使用则编译为xxx.dll文件,如果是Linux使用则编译为libxxx.so文件.下面将java调用dll/so文件的方法粘出来方便下次使用.此处使 ...
- linux上使用wget下载文件
首次安装的centos操作系统是没有安装wget的,所以首先需要先安装wget,然后才能使用wget下载文件. 1.第一步,保证centos能正常连网.使用命令 :yum -y install wg ...
- Java 调用 groovy 脚本文件,groovy 访问 MongoDB
groovy 访问 MongoDB 示例: shell.groovy package db import com.gmongo.GMongoClient import com.mongodb.Basi ...
- java 的在线下载文件 .pdf
java 的在线下载文件 .pdf 1.下载资源的本地位置 2.设置响应头 3.下载代码 1 PeriodicalResource periodicalResource = periodicalR ...
- 《手把手教你》系列技巧篇(五十六)-java+ selenium自动化测试-下载文件-上篇(详细教程)
1.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件.因此宏哥就接着讲解和分享一下:自动化测试下载文件.可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就 ...
- 《手把手教你》系列技巧篇(五十七)-java+ selenium自动化测试-下载文件-下篇(详细教程)
1.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件.因此宏哥就接着讲解和分享一下:自动化测试下载文件.可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就 ...
- 二、Delphi10.3在不下载文件情况下读取网站文件大小等信息
一.上源码 uses TxHttp, Classes, TxCommon, Frm_WebTool, SysUtils; var m_Url: string; m_Http: TTxHttp; m_P ...
随机推荐
- cocos2d-x项目过程记录(跨平台iOS和Android)
(原创作品,欢迎转载,注明出处,谢谢:http://www.cnblogs.com/binxindoudou/admin/EditPosts.aspx?postid=3205249) 1.配置环境,重 ...
- 深入浅出hive-hive简介
1. 什么是hive •Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能. •本质是将HQL转换为MapReduce程序 2. 为什么 ...
- HTML中常见的各种位置距离以及dom中的坐标讨论
最近在学习JavaScript,特意买了一本犀牛角书来看看,尼玛一千多页,看的我头昏脑涨,翻到DOM这章节,突然记起平常在使用DOM时,碰到了好多的这个dom里面的各种宽度,高度,特意在此写一写,写的 ...
- 使用日期工具类:DateUtil
利用java开发,避免不了String.Date转换,前一天.后一天等问题.给出一个工具类,仅供学习交流. import java.text.DateFormat; import java.text. ...
- MWEB+七牛 上传图片
MWEB+七牛 上传图片 博客之前的图片也都用的七牛,但编辑和上传分离还是很麻烦,所以一直很心水meb, 上周mweb降到¥50,感觉短期内应该不会再降了,于是果断入手,今天在和使用图床功能遇到了一些 ...
- css样式float造成的浮动“塌陷”问题的解决办法
什么是CSS Float? 定义: float 属性定义元素浮动到左侧或右侧.以往这个属性总应用于图像,使文本围绕在图像周围,不过在 CSS 中,任何元素都可以浮动.浮动元素会生成一个块级元素,而不论 ...
- 关于Linux下面msyql安装后并未设置初始密码,但是登录报错“Access denied for user 'root'@'localhost' (using password: NO)”的解决方案
如上图:首先我安装mysql的时候并没有设置密码,但是就是登不进去,百度了一下,解决方案如下: 解决方案地址:http://zhidao.baidu.com/link?url=7QvuOKtfRdMT ...
- ie下面兼容性问题的一些总结
最后一次搞ie兼容性问题,以后都可以不管了0.0 1.浮动兼容性 1.1IE6下的双边距BUG 在IE6下,块元素有浮动和横向margin的时候,最边上元素的横向margin值会被放大成两倍 解决办法 ...
- 黑马程序员-ReadInt
判断输入的字符串是否为数字. namespace 读入一个整数 { class Program { static void Main(string[] args) { Console.WriteLin ...
- Ajax数据格式,html,xml,json
1. 2. 3. 4. 5. 6. 7. 8. 9.