前段時間老師讓我們要做一個JavaEE項目,是一個電子商務網站--中國鮮花網,前台模板就用這個網站的,但是用瀏覽器直接下載來的圖片和樣式表等文件全在一個文件夾,需要給它批量替換,最要命的是瀏覽器的這個功能不能夠下載樣式表中的背景圖片,所以很多網頁都會顯示不正常,鑒於此,自己花了幾個小時研究了一下正則表達式寫了這個工具,基本上一般網頁下載都沒什么問題,就是碰到有些網頁是需要登錄的,比如說用戶中心,你直接把地址敲進去下載下來的一般都是登錄頁面,所以后來又做了個替換“瀏覽器保存的網頁”中的圖片和樣式表、JavaScript文件的小工具,就是背景圖片的功能還沒做(要做也不難),這個程序代碼放教室了,以后有時間也貼上來。
名字隨便取的,叫“網頁資源分類下載器”,界面如下:
下載后的文件夾形式如下:
原理簡要分析一下(不分析估計大部分都清楚):就是首先把網頁html下載下來,然后用正則表達式遍歷其中的圖片、樣式表、JavaScript文件,匹配出來的文件再和網址拼接出真正的下載地址(比如碰到../要退一層目錄等),然后下載到指定文件夾,保存網頁的html之前還要將各個資源的地址批量替換成新的地址。下載這樣下載下來的一般只有前景圖片,背景圖片還沒法下載,所以再遍歷下載下來的樣式表文件,用background-image匹配出符合條件的背景圖片,再根據樣式表文件的網頁相對地址和整個網頁的地址拼接出背景圖片的真實地址,然后下載下來。
目前測試中國鮮花網的幾個頁面都沒什么問題,所有資源都能正常下載,當然肯定還有很多Bug,有興趣的可以看一下源代碼。
源碼下載地址:http://vdisk.weibo.com/s/fyFYx/1350100334
主要代碼如下(界面的代碼就省掉了):
public String html="";//存放網頁HTML源代碼 public int cssCount=0;//下載成功的樣式表文件個數 public int jsCount=0;//下載成功的JavaScript文件個數 public int normalImageCount=0;//普通圖片數量 public int backgroundImageCount=0;//背景圖片數量 /** * 開始下載 */ public void startDownload() { buttonStart.setText("正在下載……"); buttonStart.setEnabled(false); File savePath=new File(txtSavePath.getText()); //計數清零 cssCount=0; jsCount=0; normalImageCount=0; backgroundImageCount=0; //創建必要的一些文件夾 if(!savePath.exists()) savePath.mkdir();//如果文件夾不存在,則創建 File css=new File(savePath+"/"+txtCss.getText()); File js=new File(savePath+"/"+txtJs.getText()); File images=new File(savePath+"/"+txtImages.getText()); if(!css.exists()) { css.mkdir(); System.out.println("css文件夾不存在,已創建!"); } if(!js.exists()) { js.mkdir(); System.out.println("js文件夾不存在,已創建!"); } if(!images.exists()) { images.mkdir(); System.out.println("images文件夾不存在,已創建!"); } //下載網頁html代碼 System.out.println("開始下載網頁HTML源代碼!"); String url=txtUrl.getText(); if(!url.startsWith("http://")) url="http://"+url; html=getHTML(url, comboBoxEncode.getSelectedItem().toString()); System.out.println("網頁HTML下載成功!"); if(checkBoxCss.isSelected()) { System.out.println("開始下載樣式表文件!"); regx("<link.*type=\"text/css\".*>", "href=\"", url,txtCss.getText()); } if(checkBoxJs.isSelected()) { System.out.println("開始下載JavaScript文件!"); regx("<script.*javascript.*>", "src=\"", url, txtJs.getText()); } if(checkBoxImages.isSelected()) { System.out.println("開始下載網頁前景圖片文件!"); regx("<img.*src.*>", "src=\"", url, txtImages.getText()); } //保存網頁HTML到文件 try { File newFile=new File(savePath.toString()+"/"+txtTitle.getText()); newFile.createNewFile(); OutputStreamWriter writer=new OutputStreamWriter(new FileOutputStream(newFile), comboBoxEncode.getSelectedItem().toString()); BufferedWriter bw=new BufferedWriter(writer); bw.write(html); bw.close(); writer.close(); } catch (IOException e) { e.printStackTrace(); } String result="恭喜,全部資源下載完畢!"; result+="累計下載css文件"+cssCount+"個;\n"; result+="累計下載JavaScript文件"+jsCount+"個;\n"; result+="累計下載前景圖片"+normalImageCount+"張;\n"; result+="累計下載背景圖片"+backgroundImageCount+"張。\n"; System.out.println(result); JOptionPane.showMessageDialog(null, result,"下載結束",JOptionPane.INFORMATION_MESSAGE); buttonStart.setText("開始下載"); buttonStart.setEnabled(true); } /** * 最核心的代碼,從網頁html中查找符合條件的圖片、css、js等文件並批量下載 * @param regx 檢索內容的一級正則表達式,結果是含開始、結束標簽的整個字符串,例如:<script.*javascript.*> * @param head 已經檢索出的標簽塊中要提取的字符的頭部,包含前面的雙引號,如:src=" * @param url 要下載的網頁完整地址,如:http://www.hua.com * @param folderName 文件夾名,如:css */ public void regx(String regx,String head,String url,String folderName) { //下載某種資源文件 Pattern pattern=Pattern.compile(regx);//新建一個正則表達式 Matcher matcher=pattern.matcher(html);//對網頁源代碼進行查找匹配 while(matcher.find())//對符合條件的結果逐條做處理 { Matcher matcherNew=Pattern.compile(head+".*\"").matcher(matcher.group()); if(matcherNew.find()) { //對於CSS匹配,查找出的結果形如:href="skins/default/css/base.css" rel="stylesheet" type="text/css" String myUrl=matcherNew.group(); myUrl=myUrl.replaceAll(head, "");//去掉前面的頭部,如:href:" myUrl=myUrl.substring(0,myUrl.indexOf("\""));//從第一個引號開始截取真正的內容,如:skins/default/css/base.css String myName=getUrlFileName(myUrl);//獲取樣式表文件的文件名,如:base.css html=html.replaceAll(myUrl, folderName+"/"+myName);//替換html文件中的資源文件 myUrl=joinUrlPath(url, myUrl);//得到最終的資源文件URL,如:http://www.hua.com/skins/default/css/base.css //System.out.println("發生地健康:"+myUrl); //去掉文件名不合法的情況,不合法的文件名字符還有好幾個,這里只隨便舉例幾個 if(!myName.contains("?")&&!myName.contains("\"")&&!myName.contains("/")) { downloadFile(myUrl, txtSavePath.getText()+"/"+folderName+"/"+myName);//開始下載文件 System.out.println("成功下載文件:"+myName); if(regx.startsWith("<img"))//如果是下載前景圖片文件 normalImageCount++; if(regx.startsWith("<script"))//如果是下載JS文件 jsCount++; if(regx.startsWith("<link"))//如果是下載css文件 { cssCount++; //將剛剛下載的CSS文件實例化 File cssFile=new File(txtSavePath.getText()+"/"+folderName+"/"+myName); String txt=readFile(cssFile,"gb2312");//讀取CSS文件的內容,這里用默認的gb2312編碼 //開始匹配背景圖片 Matcher matcherUrl=Pattern.compile("background:url\\(.*\\)").matcher(txt); while (matcherUrl.find()) { //去掉前面和后面的標記,得到的結果如:../images/ico_4.gif String temp=matcherUrl.group().replaceAll("background:url\\(", "").replaceAll("\\)", ""); //拼接出真正的圖片路徑,如:http://www.hua.com/skins/default/images/ico_4.gif String backgroundUrl=joinUrlPath(myUrl, temp); //獲取背景圖片的文件名,如:ico_4.gif String backgroundFileName=getUrlFileName(backgroundUrl); //背景圖片要保存的路徑,如:c:/users\lxa\desktop\網頁\images\ico_4.gif String backgroundFilePath=txtSavePath.getText()+"/"+txtImages.getText()+"/"+backgroundFileName; if(!new File(backgroundFilePath).exists())//如果不存在同名文件 { if(downloadFile(backgroundUrl, backgroundFilePath))//開始下載背景圖片 { backgroundImageCount++;//計數加1 System.out.println("成功下載背景圖片:"+backgroundFileName); } } else { System.out.println("指定文件夾已存在同名文件,已為您自動跳過:"+backgroundFilePath); } } } } } } } /** * 根據指定的URL下載html代碼 * @param pageURL 網頁的地址 * @param encoding 編碼方式 * @return 返回網頁的html內容 */ public String getHTML(String pageURL, String encoding) { StringBuffer pageHTML = new StringBuffer(); try { URL url = new URL(pageURL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("User-Agent", "MSIE 9.0");//設置客戶的瀏覽器為IE9 BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), encoding)); String line = null; while ((line = br.readLine()) != null) { pageHTML.append(line); pageHTML.append("\r\n"); } connection.disconnect(); } catch (Exception e) { e.printStackTrace(); } return pageHTML.toString(); } /** * 根據URL下載某個文件 * @param fileURL 下載地址 * @param filePath 存放的路徑 */ public boolean downloadFile(String fileURL,String filePath) { try { File file=new File(filePath); file.createNewFile(); StringBuffer sb = new StringBuffer(); URL url = new URL(fileURL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("User-Agent", "MSIE 9.0");//設置客戶的瀏覽器為IE9 byte[] buffer = new byte[1024]; InputStream is = connection.getInputStream(); FileOutputStream fos = new FileOutputStream(file); int len = 0; while ((len = is.read(buffer)) != -1) fos.write(buffer, 0, len); fos.close(); is.close(); connection.disconnect(); return true; } catch (IOException e) { System.out.println("該文件不存在:"+fileURL); return false; } } /** * 讀取某個文本文件的內容 * @param file 要讀取的文件 * @param encode 讀取文件的編碼方式 * @return 返回讀取到的內容 */ public String readFile(File file,String encode) { try { InputStreamReader read = new InputStreamReader (new FileInputStream(file),encode); BufferedReader bufread=new BufferedReader(read); StringBuffer sb=new StringBuffer(); String str=""; while ((str = bufread.readLine()) != null) sb.append(str+"\n"); String txt=new String(sb); return txt; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 獲取URL中最后面的真實文件名 * @param url 如:http://www.hua.com/bg.jpg * @return 返回bg.jpg */ public String getUrlFileName(String url) { return url.split("/")[url.split("/").length-1]; } /** * 獲取URL不帶文件名的路徑 * @param url 如:http://www.hua.com/bg.jpg * @return 返回 http://www.hua.com */ public String getUrlPath(String url) { return url.replaceAll("/"+getUrlFileName(url), ""); } /** * 拼接URL路徑和文件名,注意:以../或者/開頭的fileName都要退一層目錄 * @param url 如:http://www.hua.com/product/9010753.html * @param fileName 如:../skins/default/css/base.css * @return http://www.hua.com/skins/default/css/base.css */ public String joinUrlPath(String url,String fileName) { //System.out.println("url:"+url); //System.out.println("fileName:"+fileName); if(fileName.startsWith("http://")) return fileName; //如果去掉“http://”前綴后還包含“/”符,說明要退一層目錄,即去掉當前文件名 if(url.replaceAll("http://","").contains("/")) url=getUrlPath(url); if(fileName.startsWith("../")||fileName.startsWith("/")) { //只有當前URL包含多層目錄才能后退,如果只是http://www.hua.com,想后退都不行 if(url.replaceAll("http://","").contains("/")) url=getUrlPath(url); fileName=fileName.substring(fileName.indexOf("/")+1); } //System.out.println("return:"+url+"/"+fileName); return url+"/"+fileName; }