多线程爬虫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 ...
随机推荐
- STL源码剖析—stl_config
操作系统:centos 6.4STL源码版本:3.3 前言: 要看一个项目的源码,首先要选中切入点. 那么在sgi stl 标准库中,其切入点是什么呢? 答案是:stl_config ...
- 案例:我行我素购物系统 v1.1
系统逻辑结构: import java.util.Scanner; public class ShoppingSystem { public static void main(String[] arg ...
- Creating a Navigation Drawer 创建一个导航侧边栏
The navigation drawer is a panel that displays the app’s main navigation options on the left edge of ...
- C#学习第四天
今天主要学习了结构方面的知识,首先是定义,代码: struct<typeName> { <memberDeclarations> } struct route { public ...
- JS 获取元素的属性值,非内联样式
//获取样式表的属性值,IE8及以下不兼容 ,方法 window.getComputedStyle(dom对象,"伪类").style属性; //IE8及以下获取样式表的属性值 ...
- 关于myeclipse中导入的项目修改项目名使得发布到tomcat访问路径正确
我新的项目基于旧的的项目,在Myeclipse8.6里我复制了一个,但是在部署到tomcat7.0时部署过去的文件夹名还是原来的旧项目名,不是新的,但里面内容为新项目,是不是什么地方没有改.求救各位高 ...
- php创建读取 word.doc文档
创建文档; <?php $html = "this is question"; for($i=1;$i<=3;$i++){ $word = new word(); $w ...
- 使用NPOI插件读取excel模版修改数据后保存到新目录新文件中
添加引用: using System.IO; using NPOI.XSSF.UserModel; using NPOI.SS.UserModel; using NPOI.HSSF.UserModel ...
- Hive学习之七《 Sqoop import 从关系数据库抽取到HDFS》
一.什么是sqoop Sqoop是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql.postgresql...)间进行数据的传递,可以将一个关系型数据库(例如 :MySQL ...
- [jstl] forEach标签使用
在JSP的开发中,迭代是经常要使用到的操作.例如,逐行的显示查询的结果等.在早期的JSP中,通常使用Scriptlets来实现Iterator或者Enumeration对象的迭代输出.现在,通过JS ...