这次的目标是sohu的相册,
http://pp.sohu.com,免费相册,图片公园。它的首页,美女没有yahoo的那么多,但是比163的光秃秃好点,所以作为第二位。
Sohu使用的技术向来比较成熟稳健,而又有技术含量。例如很久以前,它的新闻和论坛就使用了CMS系统搭建,非常的规范。而博客则使用了Ajax技术,异步加载页面的不同部分,速度很快。所以在对它的相册动手之前,就做好了打硬仗的心理准备。
同样,先进入专辑的第一张图片,这样页面的图片信息比较明确,方便提取,随便找个美女相册进先
http://pp.sohu.com/photo.jhtml?method=view
&id
=74529157#74529157
1. 定位图片source
照例,view page source,先看看有没有图片部分的源代码,还好,正常
<
A
id
="photoHref"
href
="javascript:gPhotoPage.nextPhoto(onPhotoChange);"
>
<
img
id
="photodisplay"
src
="http://img44.pp.sohu.com/images/2007/3/20/15/9/11205330d6b.jpg"
title
="点击图片查看下一张"
alt
="post-3-1173601339.jpg_搜狐免费相册"
border
=0
hspace
=02
vspace
=02
/>
</
A
>
没163那么变态,全部藏在javascript里面。这样还是比较简单,只要能够定位到翻页的代码,就可以比较简单和机械的提取相册图片了。
2. 定位上下页
这里遇到了麻烦,sohu的页面尽管给出很好的注释,
<!--
这里是导航区
--><!--
导航区结束
-->
<!--
这里是面包屑目录区&管理模块区
--><!--
面包屑目录区&管理模块区结束
-->
但是都没能找到上下页的地方,怎么办?看看页面,发现有Ajax的踪迹,
<
SCRIPT
LANGUAGE
="JavaScript"
type
="text/javascript"
>
<!--
function
addContact(userId) {
var
url
=
"
/relations.jhtml?m=add&userId=
"
+
userId;
var
actionItem
=
new
ActionItem();
new
Ajax.Request(url,{method:'get', onLoading:actionItem.onLoading.bind(actionItem),onComplete:actionItem.jsonBack.bind(actionItem)});
}
//
-->
</
SCRIPT
>
果然不好啃啊。
3. 下载js文件
利用firefox的CacheViewer,过滤后排序,找到了pp.sohu.com的相关十几个js文件,果然是庞大啊。但是都是那种去掉分行的, 非常的难看,回头用python写个程序,专门把它们变回远样。据称最好的js编辑器,Aptana居然没有代码排版功能,感觉有点废啊。
找了下gPhotoPage.nextPhoto,发现是都是在Photos.js中,这个js文件是个很重要的类,对photo的操作都是封装在里面,后面用到了一个地方,但是没有找到获得图片列表的操作。
4. 重新分析页面的源代码
在源代码的最后一部分,找到
<
script
type
="text/javascript"
>
function
loadPage() {
var
rpccall
=
new
JSONRpcClient(
"
/rpccall
"
,
null
,
null
,
"
photoMgr
"
);
photosList
=
rpccall.getAllPhotos(
74529157
)
gPhotoPage
=
new
Photos(photosList.list,getPhotoIdFormUrl()
!=
null
?
getPhotoIdFormUrl() :
74529157
);
gLoginUser
=
rpccall.getLoginUser();
initRequestPhoto(gPhotoPage, gPhotoPage.photo);
}
</
script
>
该部分是在body load的时候调用,呵呵,看了这里是关键了,通过JSON RPC调用,获得所有的photo列表,得来不费功夫,果然是越高级的东西就越方便。
5. JSON RPC
如果把它的javascript全部跑起来,当然可以获得一样的效果,不过太麻烦。即然用python,当然要用python来进行这个调用。
使用google,找到了jsonrpclib的开源项目,python和java一样,开源的支持就是多,这点我喜欢。
jsonrpclib只有一个py,非常小巧,但是倚赖于simplejson的包,jsonrpc主要是对json的远程调用的封装,而simplejson则是对json数据的解析,两个模块各司其责,清晰明了。
看看源代码的版权信息,寒一个
#
a port of xmlrpclib to json
.
#
#
#
The JSON-RPC client interface is based on the XML-RPC client
#
#
Copyright (c) 1999-2002 by Secret Labs AB
#
Copyright (c) 1999-2002 by Fredrik Lundh
#
Copyright (c) 2006 by Matt Harrison
原来这东西,国外99年就在研究了,自己现在才知道...
对于Ajax我没有什么经验,JSON RPC也没有,不过凭着程序员的直觉,进行使用测试。
网上例子不多,找到了两个例子
一个python例子
s
=
jsonrpclib.ServerProxy(
"
http://jsolait.net/services/test.jsonrpc
"
)
reply
=
s.echo(
"
foo bar
"
)
print
reply
一个javaScript例子
var
jsonrpc
=
new
JSONRpcClient(
"
/<%=ApplicationHelper.getWebAppName(request)%>/JSON-RPC
"
);
jsonrpc.myObject.setName(“Vince”);
仔细研究这两个例子,再参考sohu的页面代码,猜到,其实
var
rpccall
=
new
JSONRpcClient(
"
/rpccall
"
,
null
,
null
,
"
photoMgr
"
);
photosList
=
rpccall.getAllPhotos(
74529157
)
是json rpc 的一种javascript写法,可以改成
var
jsonrpc
=
new
JSONRpcClient(
"
/rpccall
"
);
photosList
=
jsonrpc.photoMgr.getAllPhotos(
74529157
)
那么换成python就是
s
=
jsonrpclib.ServerProxy(
"
http://pp.sohu.com/rpccall
"
)
reply
=
s.photoMgr.getAllPhotos(
74529157
)
当然了,这个写法也是改了好几次才得到的,结果是好长的一个dict对象,看到结果的时候,我得意的笑,得意的笑..
动态语言的优势这个时候就体现了,同为动态语言的python和javascript,都是不需要什么类型强制转换,直接获得的对象,再调用方法就可以了,换成了严谨的java,要如何处理才好呢?恐怕要写长好几句了。
得到后就简单了,分析一下reply的dict结构,提取出imgList
imglist
=
reply.get(
'
result
'
).get(
'
list
'
))
imglist是个dict的list,每个dict中,最主要的就是hosturl和imgNames属性,imgNames属性一般有3个图片名,全 部下载后,结合photos.js的_getImgeUrl方法的分析,知道最后一个非空的,就是最大的图片,这个就是我需要的啦,剩下的事情就简单了。
imgSrcs
=
[]
for
imgurl
in
imglist:
hosturl
=
imgurl[
"
hosturl
"
]
imgNames
=
imgurl[
"
imgNames
"
].split(
"
;
"
)
for
i
in
range(len(imgNames)
-
1
, 0,
-
1
):
if
imgNames[i]:
imgSrc
=
hosturl
+
imgNames[i]
break
imgSrcs.append(imgSrc)
现在sohu的图片公园也没有防止外链了,直接用SimpleDownloader下载就可以了,至此,大功告成。