写了个抓取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下载文件,独立线程读取输出缓冲区的更多相关文章

  1. Java调用第三方dll文件的使用方法 System.load()或System.loadLibrary()

    Java调用第三方dll文件的使用方法 public class OtherAdapter { static { //System.loadLibrary("Connector") ...

  2. PHP利用Curl实现多线程抓取网页和下载文件

    PHP 利用 Curl  可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,然而因为php语言本身不支持多线程,所以开发爬虫程序效率并不高,一般采集 数据可以利用 PHPquery ...

  3. java调用dll/so文件

    大家都知道用C++编写的程序如果用于windows使用则编译为xxx.dll文件,如果是Linux使用则编译为libxxx.so文件.下面将java调用dll/so文件的方法粘出来方便下次使用.此处使 ...

  4. linux上使用wget下载文件

    首次安装的centos操作系统是没有安装wget的,所以首先需要先安装wget,然后才能使用wget下载文件. 1.第一步,保证centos能正常连网.使用命令  :yum -y install wg ...

  5. Java 调用 groovy 脚本文件,groovy 访问 MongoDB

    groovy 访问 MongoDB 示例: shell.groovy package db import com.gmongo.GMongoClient import com.mongodb.Basi ...

  6. java 的在线下载文件 .pdf

    java  的在线下载文件  .pdf 1.下载资源的本地位置 2.设置响应头 3.下载代码 1 PeriodicalResource periodicalResource = periodicalR ...

  7. 《手把手教你》系列技巧篇(五十六)-java+ selenium自动化测试-下载文件-上篇(详细教程)

    1.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件.因此宏哥就接着讲解和分享一下:自动化测试下载文件.可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就 ...

  8. 《手把手教你》系列技巧篇(五十七)-java+ selenium自动化测试-下载文件-下篇(详细教程)

    1.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件.因此宏哥就接着讲解和分享一下:自动化测试下载文件.可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就 ...

  9. 二、Delphi10.3在不下载文件情况下读取网站文件大小等信息

    一.上源码 uses TxHttp, Classes, TxCommon, Frm_WebTool, SysUtils; var m_Url: string; m_Http: TTxHttp; m_P ...

随机推荐

  1. Understanding Extension Class Loading--官方

    http://docs.spring.io/spring-amqp/docs/1.3.6.RELEASE/reference/html/sample-apps.html#d4e1285 http:// ...

  2. [转] doxygen使用总结

    doxygen [功能] 为许多种语言编写的程序生成文档的工具. [举例] *生成一个模板配置文件,模板文件中有详细的注释: $doxgen -g test 这样,会生成一个test文件,1500多行 ...

  3. 通过GitHub Pages建立个人站点(详细步骤)

    1 Git简介 2 为什么使用Github Pages 3 创建Github Pages 3.1 安装git工具. 3.2 两种pages模式 3.3 创建步骤 3.4 常用命令 4 使用Jekyll ...

  4. Android 设计模式之观察者模式(转载自:“http://blog.csdn.net/fangchongbory/article/details/7774044”)

    /* * 观察者模式 *      定义对象间的一种一个(Subject)对多(Observer)的依赖关系,当一个对象的状态发送改变时,所以依赖于它的 * 对象都得到通知并被自动更新 * * 当然, ...

  5. 请输出in.txt文件中的2 4 6 8 9 10 12行

    in.txt文件: 学号 姓名 性别 年龄 1001 张三 男 18 1002 赵四 男 19 1003 李丽 女 18 1004 刘芳 女 32 1005 王五 男 54 1006 小明 男 32 ...

  6. centos7 部署ssserver

    centos7 部署shadowsocks服务端 为什么要选centos7? 以后centos7 肯定是主流,在不重要的环境还是尽量使用新系统吧 centos7 的坑 默认可能会有firewall 或 ...

  7. windows服务(Windows Installer问题,错误5:拒绝访问)

    Windows Installer问题,错误5:拒绝访问 shillan,2006-11-03 09:40:38 现象: 使用MSI文件来安装的软件在安装和卸载时系统提示:“不能访问Windows I ...

  8. css3响应式布局

    响应式布局 分栏布局,-webkit-column-width(定义每栏的宽度,会根据每栏宽度自动分成若干栏) <style> .wrap {width: 900px; border: 1 ...

  9. C# 各版本更新简介

    经常在不同版本的.net framework下作业.于是乎想了解下,.net下各版本C#的新特性. ---------- C#2.0  针对于.net framework 2.0 新特性有: 分部类 ...

  10. 重拾C++ 基础知识总结(二)

    1.标准库string类型: 用户程序要使用string类型对象,必须包含相关头文件 #include <string> 字符串字面值与标准库string类型不是同一种类型,字符串字面值是 ...