多线程爬虫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();
            }
        }
    }

}
时间: 2024-10-13 12:45:55

多线程爬虫Java调用wget下载文件,独立线程读取输出缓冲区的相关文章

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

PHP 利用 Curl  可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,然而因为php语言本身不支持多线程,所以开发爬虫程序效率并不高,一般采集 数据可以利用 PHPquery类来采集数据库,在此之外也可以用 Curl ,借助Curl 这个功能实现并发多线程的访问多个url地址以实现并发多线程抓取网页或者下载文件. 至于具体实现过程,请参考下面几个例子: 1.实现抓取多个URL并将内容写入指定的文件 $urls = array( '路径地址', '路径地址', '路径地址

linux上使用wget下载文件

首次安装的centos操作系统是没有安装wget的,所以首先需要先安装wget,然后才能使用wget下载文件. 1.第一步,保证centos能正常连网.使用命令  :yum -y install wget   如下图,稍等一会即可安装完成. 2.使用wget下载文件,以下载jdk为例. 首先到官网下载地址页面.https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 复制*.tar.g

利用wget下载文件,并保存到指定目录

利用WGET下载文件,并保存到指定目录 [email protected] ~/下载 $ wget -P /home/cbx/下载/PDF https://www.linuxmint.com/documentation/user-guide/Cinnamon/chinese_16.0.pdf https://www.linuxmint.com/documentation.php wget是Linux上一个非常不错的下载指令,而其指令的内容虽然说是非常简单,但内藏许多的参数,也算是Linux工作者

Java 通过 HTTP 下载文件

1. [代码]Download.java   package core.spider; import java.io.*;import java.net.*;import java.util.*; // This class downloads a file from a URL.class Download extends Observable implements Runnable {         // Max size of download buffer.    private st

JAVA调用C++ dll文件之JNI接口研究——分布函数计算

分别对VS平台运行GNU库和JAVA调用VS2010 x64位系统做过一定研究,接下来将试图生成一个调用概率统计函数的dll,将其嵌入JAVA程序中运行. 当准备生成带有正态分布函数的dll文件时,出现如下错误: 1>     正在创建库 d:\documents\visual studio 2010\Projects\GetNrompdf\x64\Debug\GetNrompdf.lib 和对象 d:\documents\visual studio 2010\Projects\GetNromp

java 的在线下载文件 .pdf

java  的在线下载文件  .pdf 1.下载资源的本地位置 2.设置响应头 3.下载代码 1 PeriodicalResource periodicalResource = periodicalResourceService.get(id); 2 String filePath = periodicalResource.getAttachment();//获取资源位置 3 File file = new File(periodicalBaseDir + filePath);//本地资源位置

用wget下载文件

wget使用文档:https://www.gnu.org/software/wget/manual/wget.html 最开始常用的比如: wget -O  /e/movie.mp4 http://www.com/m.mp4输出到e盘的movie.mp4文件:可是 - O 默认会覆盖文件!所以在最后面加个 -c  表示继续下载.wget -O  /e/movie.mp4 -c wget -P /e/ http://www.com/m.mp4下载到e盘,文件名保持和url上的一样(m.mp4):-

java 从网上下载文件的几种方式 (转)

package com.github.pandafang.tool; import java.io.BufferedOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.URL;import java.nio.channels.Channels;import java.nio.cha

linux下用wget下载文件

>>>linux下可以用wget来下载文件,wget支持http.https和ftp协议, 而且还支持通过http代理从http从web服务器上下载文件. wget支持断点续传.可以下载url中里面多个链接,进行递归下载 例:wget https://imgsa.baidu.com/forum/w%3D580/sign=62b665f76c59252da3171d0c049a032c/495877d3572c11df12a338ce6f2762d0f603c26b.jpg wget -b