一、事出必有因
前段時間公司讓去採集一些單品的圖片,單品的圖片約清晰越好。
二、最初思路
在WebMagic沒有找到下載文件用的下載器(Downloader),一開始是在網上找的HttpClient的代碼,想要自己實現一個文件下載的Downloader,但是由於單個圖片太大,由於網絡不穩定,有時候在下載時就總是出現線程假死狀態。
/**
* 從網絡Url中下載文件(我一開始是使用這個方法去下載圖片,但是總是出問題。)
* @param urlStr
* @param fileName
* @param savePath
* @throws IOException
*/
public static void downLoadFromUrl(String urlStr,String fileName,String savePath) throws IOException{
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//設置超時間爲3秒
conn.setConnectTimeout(3*1000);
//防止屏蔽程序抓取而返回403錯誤
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
//得到輸入流
InputStream inputStream = conn.getInputStream();
//獲取自己數組
byte[] getData = readInputStream(inputStream);
//文件保存位置
File saveDir = new File(savePath);
if(!saveDir.exists()){
saveDir.mkdir();
}
File file = new File(saveDir+File.separator+fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(getData);
if(fos!=null){
fos.close();
}
if(inputStream!=null){
inputStream.close();
}
System.out.println("info:"+url+" download success");
}
(上面代碼參考:java 從網絡Url中下載文件)
三、自己實現的Java curl下載器(Pipeline)
curl是PHP的一個工具,也是linux上自帶的一個命令,簡稱神器。
$ which curl
/usr/bin/curl
1、curl簡單命令介紹
-
下載單個文件,默認將輸出打印到標準輸出中(STDOUT)中
curl https://www.baidu.com/
-
通過-o/-O選項保存下載的文件到指定的文件中
-o:將文件保存爲命令行中指定的文件名的文件中
-O:使用URL中默認的文件名保存文件到本地
1 # 將文件下載到本地並命名爲mygettext.html
2 curl -o mygettext.html http://www.gnu.org/software/gettext/manual/gettext.html
3
4 # 將文件保存到本地並命名爲gettext.html
5 curl -O http://www.gnu.org/software/gettext/manual/gettext.html
同樣可以使用轉向字符">"對輸出進行轉向輸出
-
同時獲取多個文件
curl -O URL1 -O URL2
若同時從同一站點下載多個文件時,curl會嘗試重用鏈接(connection)。
-
斷點續傳
通過使用-C選項可對大文件使用斷點續傳功能,如:
1 # 當文件在下載完成之前結束該進程
2 $ curl -O http://www.gnu.org/software/gettext/manual/gettext.html
3 ############## 20.1%
4
5 # 通過添加-C選項繼續對該文件進行下載,已經下載過的文件不會被重新下載
6 curl -C - -O http://www.gnu.org/software/gettext/manual/gettext.html
7 ############### 21.1%
-
curl 取得HTTP返回的狀態碼:-w %{http_code}
curl https://www.baidu.com/ -o ./baidu.html -w %{http_code} -s
## -w %{http_code} 控制額外輸出(小寫w)
## -s silent 模式,不輸出任何東西(小寫s)
## -o ./baidu.html 把下載的數據保存到當前文件夾下的baidu.html(小寫o)
參考:CURL常用命令
2、實現Java curl下載器
-
如果是window系統,需要先下載curl.exe(抱歉沒有用過Mac)
-
去Github下載curl。打開src/目錄,就是curl.exe
-
java curl Downloader
package com.lacerta.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class DownloadPictureUtil {
public static void main(String[] args) {
long i = System.currentTimeMillis();
boolean b = downloadPicture(
"https://www.baidu.com/img/bd_logo1.png",
"D:/curltest.jpg");
System.out.println("下載是否成功:" + b + "\t使用時間" + (System.currentTimeMillis() - i) + "ms");
}
public static boolean downloadPicture(String imgURL, String target) {
String[] cmds = { //
"C:/Users/AnXiaole/Downloads/curl-7.52.1/src/curl", //
"-o", //把下載的數據保存到指定的文件中,這裏是target(小寫o)
target, //
imgURL, //需要下載的URI網址
"-s", //silent 模式,不輸出任何東西(小寫s)
"-w \n%{http_code}"//控制額外輸出(小寫w)
};
ProcessBuilder pb = new ProcessBuilder(cmds);
pb.redirectErrorStream(true);
Process p;
BufferedReader br = null;
try {
p = pb.start();
String line = null;
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = br.readLine()) != null) {
if (line.trim().contains("200")) {
System.out.println("Download Img 200:\t" + cmds[2]);
return true;
}
if (line.trim().contains("404")) {
System.out.println("Download Img 404:\t" + cmds[2]);
return false;
}
}
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
-
和WebMagic集成的使用方法
在我自定義的FilePipeline:IntaoFilePipeline中使用DownloadPictureUtil下載器:
boolean b = DownloadPictureUtil.downloadPicture(url, path + (i + 1) + ".jpg");
package com.lacerta.intao.babyimg.designerchildrenswear;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import com.lacerta.util.DownloadPictureUtil;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.FilePipeline;
public class IntaoFilePipeline extends FilePipeline {
public IntaoFilePipeline() {
setPath("/data/webmagic/");
}
public IntaoFilePipeline(String path) {
setPath(path);
}
@Override
public void process(ResultItems resultItems, Task task) {
String path = this.path + task.getUUID()
+ PATH_SEPERATOR /* + resultItems.get("單品品牌\t") */ + resultItems.get("單品id\t") + PATH_SEPERATOR;
PrintWriter printWriter = null;
try {
printWriter = new PrintWriter(new OutputStreamWriter(
new FileOutputStream(getFile(path + "單品" + resultItems.get("單品id\t") + ".html"), true), "UTF-8"));
printWriter.println("<meta charset=\"utf-8\">");
printWriter.println("當前採集地址:\t" + resultItems.getRequest().getUrl() + "<br/>");
for (Map.Entry<String, Object> entry : resultItems.getAll().entrySet()) {
printWriter.println(entry.getKey() + ":\t" + entry.getValue() + "<br/>");
}
Object object = resultItems.get("圖片路徑\t");// 獲得WebMagic抓取到的圖片路徑list
if (object != null) {
List<String> picUrl = (List<String>) object;
for (int i = 0; i < picUrl.size(); i++) {
String url = picUrl.get(i);
for (int j = 0; j <= task.getSite().getRetryTimes(); j++) {
boolean b = DownloadPictureUtil.downloadPicture(url, path + (i + 1) + ".jpg");
if (b) {
break;
} else {
try {
Thread.sleep(task.getSite().getSleepTime());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (printWriter != null) {
printWriter.close();
}
}
}
}
-
WebMagic啓動方法
public static void main(String[] args) throws IOException {
Spider spider = Spider.create(new IntaoPageProcessor())//
.setScheduler(
new FileCacheQueueScheduler("D:/資料包/work/lacerta/intao/" + site.getDomain() + "/scheduler"))
.addPipeline(new ConsolePipeline())//
.addPipeline(new IntaoFilePipeline("D:/資料包/work/lacerta/intao"))// 加入IntaoFilePipeline
;
spider.addUrl("這裏是要下載的url,這裏就寫我當時抓取的url了");
spider.thread(60).run(); // 當時我使用了60個線程。
}