Flex实现QQ网页提取天气信息
以前用C#实现了从QQ天气网页提取天气信息显示,最近了解Flex,随便测试了一下Flex版的QQ天气抓取,效果还不错。
1、预备知识
Flex是Adobe提供的基于Flash环境的RIA展现技术,主要采用MXML+Action Script来编写程序、界面,然后通过编译器编译成flash格式的SWF文件发布到网站上。
实现该功能需要安装Flex SDK,可以从Adobe网站上面下载,或者安装Flex Builder plugin+Eclipse来开发。
同时需要具备简单的正则表达式知识。
2、界面布局、代码实现
文件:QQWeather.mxml
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="213" height="234" creationComplete="initApp()" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#F6FBFC, #3FEEDC]">
<mx:Style>
.myfont{font-size:12pt;font-family:'宋体';}
</mx:Style>
<mx:Script source="QQWeather.as"></mx:Script>
<mx:Label x="10" y="53" text="选择城市:" styleName="myfont"/>
<mx:ComboBox id="cmbCity" x="66" y="49" width="133" rowCount="6" change="changeHandler(event);" styleName="myfont"></mx:ComboBox>
<mx:Image x="10" y="10" source="@Embed('../title.gif')" width="189" />
<mx:Image x="10" y="106" source="@Embed('../tem1.png')" width="57" height="13"/>
<mx:Image x="10" y="132" source="@Embed('../tem2.png')" width="57" height="13"/>
<mx:Image x="10" y="178" source="@Embed('../tem3.png')" width="57" height="13"/>
<mx:Image x="10" y="154" source="@Embed('../tem5.png')" width="57" height="13"/>
<mx:Label x="75" y="103.5" text="" width="110" styleName="myfont" id="t1"/>
<mx:Label x="75" y="129.5" text="" width="110" styleName="myfont" id="t2"/>
<mx:Label x="75" y="151.5" text="" width="110" styleName="myfont" id="t3"/>
<mx:Label x="75" y="175.5" text="" width="110" styleName="myfont" id="t4"/>
<mx:Label x="34" y="202" text="Label" width="169" styleName="myfont" textAlign="right" id="lbToday"/>
<mx:Label x="10" y="80" width="193" id="t0" styleName="myfont" fontWeight="normal"/>
</mx:Application>
后台脚本:QQWeather.as
2 import flash.events.Event;
3 import flash.events.SecurityErrorEvent;
4
5 import mx.collections.ArrayCollection;
6 import mx.controls.Alert;
7 import mx.utils.Base64Encoder;
8
9 private var loader:URLStream;
10 private var htmldata:String;
11 private var ifloaded:Boolean=false;
12 private var dataUrl:String="http://weather.qq.com/inc/ss*.htm";
13
14 public function initApp():void{
15 var t:Date=new Date();
16 lbToday.text=t.fullYear+"-"+(t.month+1)+"-"+t.date+" "+t.hours+":"+t.minutes+":"+t.seconds;
17 loadUrl("125",parseCityInfo);
18 }
19 private function loadUrl(cityno:String,callback:Function):void{
20 loader= new URLStream();
21 loader.addEventListener(Event.COMPLETE, callback);
22 loader.addEventListener("securityError",onSecurityError );
23 var addr:String=dataUrl.replace(/\*/,cityno);
24 trace("Loading",addr);
25 var request:URLRequest = new URLRequest(addr);
26 //var enc:Base64Encoder=new Base64Encoder();
27 //enc.encodeUTFBytes(addr);
28 //var request:URLRequest = new URLRequest("http://localhost/httpproxy/webservlet?data="+enc.toString());
29 try {
30 loader.load(request);
31 } catch (error:Error) {
32 Alert.show("不能连接服务器:"+addr,"Tips");
33 }
34 }
35 private function onSecurityError(event:SecurityErrorEvent):void{
36 Alert.show("安全错误:"+event.text,"Tips");
37 }
38 private function parseCityInfo(event:Event):void {
39 var sm:URLStream=event.target as URLStream;
40 htmldata= sm.readMultiByte(sm.bytesAvailable, "gb2312");
41 trace("completeHandler: " + htmldata );
42 if(!ifloaded){
43 var pattern:RegExp =new RegExp("value=\"(\\d{1,3})\">([^<]*)</option>","g");
44 var result:Object = pattern.exec(htmldata);
45 var data:ArrayCollection = new ArrayCollection();
46 var item:Object;
47 while (result != null) {
48 trace(result[1],result[2]);
49 item=new Object();
50 item.label=result[2];
51 item.data=result[1];
52 data.addItem(item);
53 result = pattern.exec(htmldata);
54 }
55 cmbCity.dataProvider=data;
56 ifloaded=true;
57 }
58 parseWeather();
59 }
60 private function parseWeather():void{
61 //分析天气信息
62 var item:Object=cmbCity.selectedItem;
63 var pattern:RegExp =new RegExp("align=\"center\">([^<]*)</td>","g");
64 var result:Object = pattern.exec(htmldata);
65 var data:Array=new Array();
66 while (result != null) {
67 data.push(trimString(result[1]));
68 trace(result[1]);
69 result = pattern.exec(htmldata);
70 }
71 t0.text=item.label+": "+data[0];
72 t1.text=data[1];
73 t2.text=data[2];
74 t3.text=data[3];
75 t4.text=data[4];
76 }
77 private function changeHandler(event:Event):void{
78 var item:Object=ComboBox(event.target).selectedItem;
79 trace(item.data);
80 loadUrl(item.data,parseCityInfo);
81 }
82 private function trimString(s:String):String{
83 return s.replace(/(^\s*)|(\s*$)/g, "");
84 }
用mxmlc命令编译之后,运行效果如下:
3、注意事项
根据flash的sandbox安全模型要求,A域名下的flash文件只能从A域名中获取服务端数据,如果需要从B域名中提取数据,需要B域名配置一个crossdomain.xml的文件,允许A从B
下载数据,但是我们不可能有这样的公关能力要求腾讯在http://weather.qq.com/下面放一个xml文件,允许其他网站访问,因此只能另辟蹊径:
方案1:A域名下自己实现一个代理,从B域名网页下载数据返回,这样就回避了sandbox的安全性错误,Line 26-28注释的代码就是我用java Servlet实现的一个代理。
方案2:建立代理服务器,例如squid或者Apache代理
方案3:采用adobe提供的开源软件bladeDS,该软件采用J2EE War方式发布,可以提供RPC、HTTP Proxy的代理功能
方案4:采用Adobe的企业级解决方案Adobe LiveCycle ES ,这个东西试用版就2.5G,真是庞然大物,受不了了,没见过这样的软件,一个Oracle数据库也就几百M而已,想不通。