使用Java爬取随机图片api

大多数随机图片api都是采用直接跳转的方式,最常见的就是直接跳转到一些免费的图床,比如新浪图床,当然也有一些并没有进行302/301重定向直接返回图片的,本文仅针对跳转的api


随机图片,未免会随机到一点相同的,因此要进行重复判断,最简单的办法就是将重定向后的网址保存到本地,之后爬取都读取一下之前的记录,发现有重复的就不再下载。

先编写好一些文件操作的实用方法:

 /**
 * 输出文件到本地目录
 *
 * @param image
 */
public static void out(BufferedImage image, String filepath, String formatName) {
    File outputfile = new File(filepath);
    if (!outputfile.getParentFile().exists()) {
        outputfile.getParentFile().mkdirs();
    }
    try {
        ImageIO.write(image, formatName, outputfile);
    } catch (IOException e) {
        e.printStackTrace();
    }
}


public static void appendTxt(String str, String filepath) {
    FileWriter fw = null;
    try {
        //如果文件存在,则追加内容;如果文件不存在,则创建文件
        File f = new File(filepath);
        fw = new FileWriter(f, true);
    } catch (IOException e) {
        e.printStackTrace();
    }
    PrintWriter pw = new PrintWriter(fw);
    pw.println(str);
    pw.flush();
    try {
        fw.flush();
        pw.close();
        fw.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * 读取一个文本 一行一行读取
 *
 * @param path
 * @return
 * @throws IOException
 */
public static List<String> readFile(String path) throws IOException {
    File file = new File(path);
    if (!file.exists()) {
        file.createNewFile();
    }
    // 使用一个字符串集合来存储文本中的路径 ,也可用String []数组
    List<String> list = new ArrayList<>();
    FileInputStream fis = new FileInputStream(path);
    // 防止路径乱码   如果utf-8 乱码  改GBK     eclipse里创建的txt  用UTF-8,在电脑上自己创建的txt  用GBK
    InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
    BufferedReader br = new BufferedReader(isr);
    String line = "";
    while ((line = br.readLine()) != null) {
        list.add(line);
    }
    br.close();
    isr.close();
    fis.close();
    return list;
}
/**
 * 获取后缀
 *
 * @return
 */
public static String getFileHouZhui(String filename) {
    String[] strArray = filename.split("\\.");
    if (strArray.length <= 1) {
        return filename;
    }
    return strArray[strArray.length - 1];
}

public static String getUrlHOUZHUI(String url) {
    String[] split = url.split("/");
    return split[split.length - 1];
}

然后获取重定向后的网址,这边提供两种方式,以防止一种获取失效:

public static String getRealUrl(String urlstr) throws IOException {
    URL url = new URL(urlstr);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.getResponseCode();
    String realUrl = conn.getURL().toString();
    conn.disconnect();
    return realUrl;
}
public static String getRealUrl2(String urlstr) throws IOException {
    HttpClient client = new HttpClient();
    HttpMethod method = new GetMethod(urlstr);
    HttpParams params = client.getParams();
    params.setParameter(AllClientPNames.HANDLE_REDIRECTS, false);
    client.executeMethod(method);
    return method.getURI().getURI();
}

编写main主方法逻辑,偷懒就全写一个类里了...

public static void main(String[] args) {
    int methodHttp = 1; //设置默认爬取方式
    try {
        for (int i = 0; i < NUM; i++) {
            String realUrl = null;
            //实现爬取方案自动切换
            if (methodHttp == 1){
                realUrl = getRealUrl(CATCHURL);
                if (realUrl.equals(CATCHURL)){
                    methodHttp = 2;
                    continue;
                }
            }else if (methodHttp == 2){
                realUrl = getRealUrl2(CATCHURL);
                if (realUrl.equals(CATCHURL)){
                    methodHttp = 1;
                    continue;
                }
            }
            //遍历获取记录文件每一行的内容
            List<String> strings = readFile(PATH + TXTNAME);
            boolean flag = false;
            for (String string : strings) {
                //对于新浪图床,有多个服务器,因此需要取网址后缀文件名进行判断
                if (getUrlHOUZHUI(string).equals(getUrlHOUZHUI(realUrl))) {
                    System.err.println("相同文件,取消下载!");
                    flag = true;
                    break;
                }
            }
            if (flag) {
                continue;
            }
            System.out.println((i + 1) + "/" + NUM + "开启爬取:" + realUrl);
            BufferedImage image = ImageIO.read(new URL(realUrl));
            //获取url后缀以重命名文件
            String urlHOUZHUI = getUrlHOUZHUI(realUrl);
            //获取文件后缀以输出对应的文件
            String fileHouZhui = getFileHouZhui(urlHOUZHUI);
            if (fileHouZhui.equals(urlHOUZHUI)){
                fileHouZhui = "jpg";
                urlHOUZHUI = urlHOUZHUI + ".jpg";
            }
            out(image, PATH + urlHOUZHUI, fileHouZhui);
            appendTxt(realUrl, PATH + TXTNAME);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

注意:对于新浪图床这种有多个服务器的网站,需要判断网址后面的文件名称而不是整个网址!
例如https://tva2.sinaimg.cn/large/0072Vf1pgy1fodqoshzhbj31d60ytawh.jpghttps://tva1.sinaimg.cn/large/0072Vf1pgy1fodqoshzhbj31d60ytawh.jpg是同一张图片

声明下变量

private final static String TXTNAME = "result.txt";
private final static String PATH = "D:\\下载\\catchpic\\";
private final static String CATCHURL = "http://www.dmoe.cc/random.php";
private final static int NUM = 500;

由于有时候爬取会出现这种场景,因此建议在代码中加入一个小判断

if (getUrlHOUZHUI(realUrl).equals(".jpg")){
   continue;
}

idea64_TjboXh5miJ.png

针对于另一种返回html页面的接口,经过分析发现中间仅有一个img标签,因此取img标签的src属性即可,参考代码如下:

//需要引入Jsonip的依赖,当然也可以用正则或者字符串的方法等
//针对返回一个html页面的接口
HttpResponse httpResponse = HttpUtils.doGet(CATCHURL, null, HttpUtils.getHeaders(), null);
String s = EntityUtils.toString(httpResponse.getEntity());
Document document = Jsoup.parse(s);
Elements img = document.select("img");
//realUrl = "https:" + img.attr("src");
realUrl = img.attr("src");
评论区
头像