利用Javascript快速保存当前网页中的所有图片

 
利用Javascript快速保存当前网页中的所有图片

 
      工作中,我经常会用到Windows Script, 因此在我的Blog上有这个专栏,把平时学到看到的记录下来,但是我自己一直没有发表过真正有用的脚本,工作中的那些由于涉及到公司机密,不方便也不能在这里发表,一直觉得很遗憾。周末闲的无聊,上了很多网站浏览,看到好的图片,总想保存下来,但是使用浏览器自带的保存功能时,觉的非常麻烦。如果有能够自动保存当前网页中所有图片的小程序该多好,只需要按个快捷键,图片就自动保存到指定文件夹里了,还不干扰用户浏览。我当时用google搜索了一下,似乎这样的东西很少,如果选择安装一个重量级的软件又不值得,而且还可能有后门。俗话说,自己动手,丰衣足食,我萌生了写这个脚本的想法,也希望对大家能有所帮助。说了这么多废话,赶快开始吧。
 
先分析一下我们可能面临的问题:

1)如何将当前网页的地址传递给脚本?

2)如何使用脚本读取网页的内容?

3)如何处理不同编码的网页?

4)如何从网页内容将所有的图片的URL解析出来?

5)如何读取图片?

6)如何保存图片到本地目录?

7) 补充说明
 
一、如何将当前网页的地址传递给脚本?
 
Windows Script有一个很好的功能就是能够模拟键盘操作,如果我们能够模拟键盘操作,将地址栏激活,然后把地址栏里的内容Copy到剪贴板里,再通过剪贴板获取该内容,不就实现我们的目的了吗?
Windows Script 读取剪贴板内容是通过借助Internet Explorer来完成的。大家可以参看MSDN:

http://www.microsoft.com/technet/scriptcenter/resources/qanda/dec04/hey1215.mspx

 
代码:
 

// ===============================================================================
//
Function Name: GetUrl
//
Description: 获取当前浏览器地址栏网页URL
//
Parameter(s): 无
//
Return: null 表示失败
//
===============================================================================

function  GetUrl() {
    
var oIE;
    
var oShell;
    
var url;
    
try {
        oShell
=new ActiveXObject("WScript.Shell");
        oShell.SendKeys(
"{F6}");//发送系统按键F6,激活浏览器地址栏,幸运的是FireFox等其他浏览器也支持此操作
        oShell.SendKeys("^c"); //发送系统按键Ctrl+C,这个地球人都知道是做什么的
        oIE = new ActiveXObject("InternetExplorer.Application"); // 创建 Internet Explorer application 对象.
        oIE.navigate("about:blank");  // 创建空文本
        oIE.left=50;            // 弹出窗口位置
        oIE.top = 100;          // 弹出窗口位置
        oIE.height = 150;       
        oIE.width 
= 450;
        oIE.menubar 
= 0;        // 不显示菜单
        oIE.toolbar = 0;        // 不显示工具栏
        oIE.statusbar = 0;      // 不显示状态栏
        if (SILENT) {
            oIE.Visible 
= false;
        }
 else {
            oIE.Visible 
= true// 如果是非安静模式,将此窗口显示
        }

        
while (oIE.Busy) {;}      // 非常重要: 一直等待到oIE对象准备好.
        obj = oIE.document.parentWindow.clipboardData;
        url 
= oIE.document.parentWindow.clipboardData.getData("text"); //将地址栏的内容从剪贴板里取出来
        if (!SILENT) {
            
//以下代码创建一个假的进度条,不是本程序的重点,关于如何创建一个真的进度条,我还没有想好,如有时间,可能会在以后的版本中更新,欢迎网友讨论。
            oIE.document.write("<HTML> "+
                                
"<HEAD> "+
                                
"<TITLE></TITLE> "+
                                
"<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"> "+
                                
"</HEAD> "+
                                
"<BODY BGCOLOR="#EAEAEA"> "+
                                
"<form name=loading> "+
                                
"<table border=0 cellpadding=0 cellspacing=0 width="100%" height="100%"> "+
                                
"<tr> "+
                                
"<TD align="center" valign="top"> "+
                                
"<p><FONT color="000000" face="Arial"><BR> "+
                                
"正在保存图片...</FONT></p> "+
                                
"<p><input type=text name=chart size=46 style="font-family:Arial; font-weight:bolder; color:black; background-color:#EAEAEA; padding:0px; border-style:none;"> "+
                                
"<br> "+
                                
"<input type=text name=percent size=46 style="font-family:Arial; color:black; background-color:#EAEAEA;text-align:center; border-width:medium; border-style:none;"> "+
                                
"<script>var bar = 0  "+
                                
"var line = "||"  "+
                                
"var amount ="||"  "+
                                
"count(); "+
                                
"function count(){  "+
                                
"bar= bar+2  "+
                                
"amount =amount + line  "+
                                
"document.loading.chart.value=amount  "+
                            
//    "document.loading.percent.value=bar+"%"  "+
                                "if (bar<99)  "+
                                
"{setTimeout("count()",200);}  "+
                                
"else  "+
                                
"{;} "+
                                
"} "+
                                
"</script></p></TD> "+
                                
"</tr> "+
                                
"</table> "+
                                
"</FORM> "+
                                
" "+
                                
" "+
                                
"</BODY> "+
                                
"</HTML> "
                                );
        oShell.AppActivate(
"Internet Explorer");
    }

    }
 catch (e){
        WScript.Echo(
"出现异常: " + e+" " + "描述: " + e.description+" ");
    }

    
if (SILENT) {
        oIE.Quit();
    }

    
//WScript.Echo(url);
    return url;
}


 
我相信看完代码,大家应该非常清楚我们是如何将当前网页的网址获取得了。扯点远的,之所以我用javascript来写脚本,原因是我以前做过Java 程序员,对Java 风格的代码比较有感觉。网上脚本基本上是Vbscript,有一阵也很苦恼,呵呵,大家现在可能也有我的感觉了。
 
二、如何使用脚本读取网页的内容?
 
相信10个人有9个会想到Ajax,没错,我们就是用它来解决这个问题。
有兴趣的同志可以看看下面的文章:

http://www.ibm.com/developerworks/web/library/wa-ajaxintro2/
 
代码:
 

                 var  oHTTP  =   false ;
                
try   {
                                oHTTP 
= new XMLHttpRequest();
                }
  catch  (trymicrosoft)  {
                                
try {
                                                oHTTP 
= new ActiveXObject("Msxml2.XMLHTTP");
                                }
 catch (othermicrosoft) {
                                                
try {
                                                                oHTTP 
= new ActiveXObject("Microsoft.XMLHTTP");
                                                }
 catch (failed) {
                                                                oHTTP 
= false;
                                                }

                                }

                }

                
if  ( ! oHTTP) {
                                WScript.Echo(
"初始化XMLHttpRequest对象出错!");
                                index 
= false;
                }

                oHTTP.open(
" GET " , sSrcUrl,  false );
                oHTTP.send();
                
if  (oHTTP.status  !=   200 {
                                WScript.Echo(
"未知状态: " + oHTTP.status + " 原因:"+ oHTTP.statusText);
                                index 
= false;
                }

                
// WScript.Echo(oHTTP.responseBody);
                 // WScript.Echo(oHTTP.responseText);
                 // WScript.Echo(oHTTP.responseXML);


通过上面的代码,我们就把网页的内容取回来了。如果返回的是XML,可以使用oHTTP.responseXML,因为是网页,所以我们只能选择使用oHTTP.responseText,和oHTTP.responseBody。下面大家会看到我们其实分别使用了oHTTP.responseText,和oHTTP.responseBody 解决了不同的问题。
 
三、如何处理不同编码的网页?
 
要回答这个问题,首先需要获取当前网页正确的字符集。
 
代码:
 

// ===============================================================================
//
Function Name: CharSetDetector
//
Description: 根据HTML网页内容判断该网页使用的字符集
//
Parameter(s): oText HTML网页内容
//
Return: 使用的字符集
//
===============================================================================
 
function  CharSetDetector(oText) {
                
var charset;
                
//自动判断编码开始
                 var charSets = oText.match(/charset=(S+)"/i);
                 if (charSets != null){
                     charset = charSets[1];
                }else{
                                charset = 
"UTF-8" //缺省采用UTF-8编码
                } 
                //自动判断编码结束
                return charset;
}


通过代码大家可以了解到,使用一个很简单的正则表达式,我们很容易就把当前网页的字符集解析出来了。如果大家对正则表达式不了解,可以看看我blog里的相关文章:http://www.the3gwireless.com
 
大家可能要问,为什么要获得正确的字符集呢?

大家可以用以下代码输出一下网页内容:

 

                WScript.Echo(oHTTP.responseText);


如果该网页是UTF8编码,而你的机器是简体中文系统,那么可能在终端上看到的是包括乱码的网页。那么如何得到正确编码的网页内容呢?答案是使用oHTTP.responseBody。足够幸运的是,我们已经通过oHTTP.responseText得到了正确的字符集,因为英文字符是不会出现乱码的。
 
代码:
 

// ===============================================================================
//
Function Name: BytesToBSTR
//
Description: 将Raw数据根据制定字符集编码后返回正确的HTML网页内容
//
Parameter(s): body 二进制数据,charset 字符集
//
Return: null 编码后的HTML网页内容
//
===============================================================================
 
function  BytesToBSTR(body,charset) {
                
var objstream;
                objstream 
= new ActiveXObject("adodb.stream");
                objstream.Type 
= 1;
                objstream.Mode 
= 3;
                objstream.Open();
                objstream.Write(body);
                objstream.Position 
= 0;
                objstream.Type 
= 2;
                objstream.Charset 
= charset;
                
var bytesToBSTR = objstream.Readtext;
                objstream.Close;
                
return(bytesToBSTR);
}


一切都这么简单,通过借助adodb.stream对象,我们可以很容易得到正确编码后的网页内容。

关于ADODB.STREAM对象,大家可以参考这里:

http://www.devguru.com/Technologies/ado/quickref/stream.html
 
四、如何从网页内容将所有的图片的URL解析出来?

答案还是正则表达式,如果是XML,我们会考虑使用XMLDOM对象,但这里我们需要处理的是HTML内容。

代码:

  var  sUrls  =  sSrcUrl.match( / (http: / / . *? / ) / i); 
        
var  sUrl; // 将网站的URL解析出来,以后使用
         if  (sUrls != null ) {
            sUrl 
= sUrls[1];
        }
else   {
            sUrl 
= sSrcUrl;
        }

        
var  imagesArray  =  oText.match( /< img . *?>/ ig); // 使用正则表达式将<img .*?>选中
         if  (imagesArray != null ) {
            
for (i=0;i<imagesArray.length;i++){
                imagesArray[i] 
= imagesArray[i].substring(imagesArray[i].indexOf("src=")+4,imagesArray[i].length-1);
                
var tmp = imagesArray[i].indexOf(" ");
                
if (tmp >0{
                    imagesArray[i] 
= imagesArray[i].substring(0,tmp);
                }
 
                
//WScript.Echo(imagesArray[i]);
                imagesArray[i] = imagesArray[i].replace(/"/g,"");
                imagesArray[i] = imagesArray[i].replace(/'/g,
"");
                //以下开始整理图片的路径,这里考虑到了相对路径和绝对路径等各种情况,很可能还不完全,网友可以自己补充
                if (imagesArray[i].indexOf(
"http://")==0){
                    //标准的路径,什么也不做
                }
else{
                    
if (imagesArray[i].indexOf("../")==0){
                        
if (sUrl.charAt(sUrl.length-1)=="/"){
                            imagesArray[i].replace(
"../",sUrl);
                        }
else {
                            imagesArray[i].replace(
"..",sUrl);
                        }

                    }
else {
                        
if (sUrl.charAt(sUrl.length-1)=="/"){
                            
if (imagesArray[i].charAt(0)!="/"){
                                imagesArray[i] 
= sUrl+imagesArray[i];
                            }
 else {
                                imagesArray[i] 
= sUrl + imagesArray[i].substring(1);
                            }

                        }

                        
else {
                            
if (imagesArray[i].charAt(0)!="/"){
                                imagesArray[i] 
= sUrl+"/"+imagesArray[i];
                            }
 else {
                                imagesArray[i] 
= sUrl + imagesArray[i];
                            }

                        }

                    }

                }
//整理图片路径结束
            }

        }


现在,我们终于得到了我们想要得到的图片URL数组了。

注:本文只针对静态图片“<img .*?>”这种模式作了处理,但是我们知道,很多图片网站是使用各种各样的方法来显示图片的。需要对这些网站返回的网页数据进行分析后,才能写出适合这些网站的图片抓取脚本。那么,可不可能写出一个通用的图片抓取脚本呢?答案应该是肯定的,我现在考虑可以让脚本去访问当前网页上所有的对象,然后根据返回的MIME类型判断该对象是否为图片,如果是,则保存到本地文件夹里。有时间我会把这一功能实现,也欢迎大家发表意见。

 
五、如何读取图片?
 
跟读取网页的代码相同,不过这次我们只需要直接使用oHTTP.responseBody二进制数据

代码:

var  oHTTP  =   false ;
                
try   {
                                oHTTP 
= new XMLHttpRequest();
                }
  catch  (trymicrosoft)  {
                                
try {
                                                oHTTP 
= new ActiveXObject("Msxml2.XMLHTTP");
                                }
 catch (othermicrosoft) {
                                                
try {
                                                                oHTTP 
= new ActiveXObject("Microsoft.XMLHTTP");
                                                }
 catch (failed) {
                                                                oHTTP 
= false;
                                                }

                                }

                }

                
if  ( ! oHTTP) {
                                WScript.Echo(
"初始化XMLHttpRequest对象出错!");
                                WScript.Quit();
                }

                
try {
                                oHTTP.open(
"GET", argSrcUrl, false);
                                oHTTP.send();
                                
if (oHTTP.status != 200{
                                                WScript.Echo(
"未知状态: " + oHTTP.status + " 原因:"+ oHTTP.statusText);
                                                errors
++;
                                                index 
= false;
                                }

 
六、如何保存图片到本地目录?
 
同样,使用adodb.stream对象,我们可以很容易地将我们读取到的二进制文件保存到指定目录中。
 
代码:
 

 


                
if  (index) {
                                                
var oStream = new ActiveXObject("adodb.stream");
                                                
var adTypeBinary = 1;
                                                
var adSaveCreateOverWrite = 2;
                                                oStream.type 
= adTypeBinary;
                                                oStream.open();
                                                oStream.write(oHTTP.responseBody);
                                                
//WScript.Echo(argDestFolder+argImageFile);
                                                oStream.savetofile(argDestFolder+argImageFile,adSaveCreateOverWrite);
                                                oStream 
= null;
                                                oHTTP 
= null;
                                                
return index;
                                }
  else   {
                                                
return index;
                                }

 
 
我们已经解决了所有的问题,现在看一看运行情况:
 
首先请大家下载源代码

http://www.the3gwireless.com/apps/GetImages.rar

 其次,为了能够使用快捷键操控此脚本,请下载并安装Hoekey,并设定一个快捷键(譬如Win+0)

http://www.the3gwireless.com/apps/HoeKey113Inst.rar

在浏览网页时,摁下快捷键就可以调用此脚本保存当前网页所有图片了。

七、补充说明:

为保证脚本正确运行,如果当前机器缺省的脚本引擎被设置成CScript,请在命令行下使用下边命令将脚本引擎恢复为WScript:

CScript.exe  // H:WScript


本脚本包括安静模式设定,如果你希望整个脚本在后台运行,请设定

SILENT  =   true ;


否则,请设定:

SILENT  =   false ;


用户可以看到如下的界面,和结果通知。是不是很有趣?
 
 

今天在Vista上试了一下,由于Vista系统安全的提高,似乎对IE访问剪贴板内容有所限制,会弹出一个窗口请你确认,因此此脚本在安静模式下,不能执行成功。请看下图:

因此为了使此脚本在Vista 上正确运行,需要时间再研究一下,也希望网友们讨论,谢谢。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值