利用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 上正确运行,需要时间再研究一下,也希望网友们讨论,谢谢。
介绍了一个使用JavaScript编写的脚本,该脚本能自动保存当前网页中的所有图片到指定文件夹,涉及网页内容读取、字符集处理、图片URL解析及图片下载保存等内容。
2752

被折叠的 条评论
为什么被折叠?



