前段时间在公司系统(
电联协同管理软件)中需要天气预报信息。据此就写了一个天气预报抓取的类。今天说一下我抓取的思路和方法,并共享一下源代码。
刚开始做的时候,在网上找了一下代码。发现WebXml.com.cn上面有直接可调用的webServices地址为
http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx。不过他对免费调用的用户有一定限制,请看
http://www.webxml.com.cn/zh_cn/web_services_user.aspx。所以我打算自己写。
我发现很多网站包括WebXml都是从中国天气网(
http://www.weather.com.cn/)抓取的。经过分析,发现可以有2总方法或去数据:
1.直接掉用它的JS获取数据的方式。
这种方法获取简单,但数据量不多。
它本身就一个地址可直接获取JSON数据:
http://m.weather.com.cn/data/天气代码.html。获取到的数据如下:
{
"
weatherinfo
"
:{
"
city
"
:
"
北京
"
,
"
date_y
"
:
"
2009年7月22日
"
,
"
date
"
:
"
六月初一
"
,
"
week
"
:
"
星期三
"
,
"
fchh
"
:
"
08
"
,
"
cityid
"
:
"
101010100
"
,
"
temp1
"
:
"
33℃~24℃
"
,
"
temp2
"
:
"
27℃~20℃
"
,
"
temp3
"
:
"
31℃~21℃
"
,
"
temp4
"
:
"
35℃~21℃
"
,
"
weather1
"
:
"
阴转雷阵雨
"
,
"
weather2
"
:
"
雷阵雨转阵雨
"
,
"
weather3
"
:
"
多云
"
,
"
weather4
"
:
"
晴
"
,
"
img1
"
:
"
2
"
,
"
img2
"
:
"
4
"
,
"
img3
"
:
"
4
"
,
"
img4
"
:
"
3
"
,
"
img5
"
:
"
1
"
,
"
img6
"
:
"
99
"
,
"
img7
"
:
"
0
"
,
"
img8
"
:
"
99
"
,
"
img_single
"
:
"
2
"
,
"
img_title1
"
:
"
阴
"
,
"
img_title2
"
:
"
雷阵雨
"
,
"
img_title3
"
:
"
雷阵雨
"
,
"
img_title4
"
:
"
阵雨
"
,
"
img_title5
"
:
"
多云
"
,
"
img_title6
"
:
"
多云
"
,
"
img_title7
"
:
"
晴
"
,
"
img_title8
"
:
"
晴
"
,
"
img_title_single
"
:
"
阴
"
,
"
wind1
"
:
"
微风
"
,
"
wind2
"
:
"
微风
"
,
"
wind3
"
:
"
微风
"
,
"
wind4
"
:
"
微风
"
,
"
fl1
"
:
"
小于3级
"
,
"
fl2
"
:
"
小于3级
"
,
"
fl3
"
:
"
小于3级
"
,
"
fl4
"
:
"
小于3级
"
,
"
index
"
:
"
炎热
"
,
"
index_d
"
:
"
天气炎热,建议着短衫、短裙、短裤、薄型T恤衫、敞领短袖棉衫等清凉夏季服装。
"
,
"
index_uv
"
:
"
弱
"
,
"
index_xc
"
:
"
不宜
"
,
"
index_tr
"
:
"
较不宜
"
,
"
index_co
"
:
"
较不舒适
"
}}
要获取他,先建立一个实体类WeatherSimpleInfo:
///
<summary>
简单天气实体
</summary>
public
class
WeatherSimpleInfo
{
///
<summary>
城市
</summary>
public
string
city {
get
;
set
; }
///
<summary>
时间
</summary>
public
string
date_y {
get
;
set
; }
///
<summary>
农历时间
</summary>
public
string
date {
get
;
set
; }
///
<summary>
ID
</summary>
public
string
cityid {
get
;
set
; }
///
<summary>
温度
</summary>
public
string
temp1 {
get
;
set
; }
public
string
temp2 {
get
;
set
; }
///
<summary>
天气
</summary>
public
string
weather1 {
get
;
set
; }
public
string
weather2 {
get
;
set
; }
///
<summary>
图片
</summary>
public
string
img1 {
get
;
set
; }
public
string
img2 {
get
;
set
; }
public
string
img3 {
get
;
set
; }
public
string
img4 {
get
;
set
; }
///
<summary>
风
</summary>
public
string
wind1 {
get
;
set
; }
public
string
wind2 {
get
;
set
; }
///
<summary>
穿衣简单指数
</summary>
public
string
index {
get
;
set
; }
///
<summary>
穿衣指数
</summary>
public
string
index_d {
get
;
set
; }
///
<summary>
紫外线简单指数
</summary>
public
string
index_uv {
get
;
set
; }
///
<summary>
洗车简单指数
</summary>
public
string
index_xc {
get
;
set
; }
public
string
index_tr {
get
;
set
; }
public
override
string
ToString()
{
var sb
=
new
StringBuilder();
sb.AppendFormat(
"
城市:{0}\r\n
"
,
this
.city);
sb.AppendFormat(
"
时间:{0}\r\n
"
,
this
.date_y);
sb.AppendFormat(
"
天气:{0}\r\n
"
,
this
.weather1);
sb.AppendFormat(
"
温度:{0}\r\n
"
,
this
.temp1);
sb.AppendFormat(
"
风:{0}\r\n
"
,
this
.wind1);
sb.AppendFormat(
"
穿衣指数:({0})。{1}\r\n
"
,
this
.index,
this
.index_d);
sb.AppendFormat(
"
紫外线指数:{0}\r\n
"
,
this
.index_uv);
sb.AppendFormat(
"
洗车指数:{0}\r\n
"
,
this
.index_xc);
return
sb.ToString();
}
}
抓取类:WeatherSimple:
///
<summary>
简单天气预报数据操作(直接从JSON获取)
</summary>
public
class
WeatherSimple
{
private
const
string
__Url
=
"
http://m.weather.com.cn/data/{0}.html
"
;
public
WeatherSimple() { }
public
WeatherSimpleInfo weatherinfo {
get
;
set
; }
///
<summary>
获取简单天气预报
</summary>
///
<param name="name">
城市代码
</param>
///
<returns>
天气预报
</returns>
public
static
WeatherSimpleInfo Get(
string
code)
{
using
(WebClient wc
=
new
WebClient()){
wc.Encoding
=
Encoding.UTF8;
var url
=
string
.Format(__Url,code);
string
str
=
wc.DownloadString(url);
return
SerializationHelper.JsonDeserialize
<
WeatherSimple
>
(str).weatherinfo;
}
}
}
2.抓取Html并分析提取数据的方式。
这种方法获取复杂,但可以获取较多天气数据。
天气网的http://www.weather.com.cn/html/weather/天气代码.shtml地址可以显示相关地区的天气信息。
用WebClient抓取数据后提取。很多人提取数据是使用正则表达式的方式,不过本人正则表达式用的比较烂,就选择XML的方式了。
众所周知,html本身是不标准的xml。不能直接通过XmlDocumentl来读取。这里我借助了SgmlReaderDll。他可以把Html转换成xml。
转换成功后我们就可以用xpath的方式来提取数据了,更具需求还可以提取不同的天气指数。具体提取请看原代码。
这样就完成了天气预报的提取。但这还不够,还必须有获取城市代码的方法.
获取城市代码可以直接使用http://www.weather.com.cn/data/listinfo/city上级城市代码.xml。去获取到的数据如下
01|北京,02|上海,03|天津,04|重庆,05|黑龙江,06|吉林,07|辽宁
我们可以通过Split他提取成包括名称和代码的城市数组。
有了城市代码后我们还需要通过城市名称获取代码的方法,所以我把它的所有城市和代码都下载到WeatherCity.Xml文件内了。见原代码。
按照公司的需求,还要有通过访问者IP获取天气的功能。这里我就想到了纯真IP库。用它直接获取IP所在的城市,但纯真IP库里面的城市是比较复杂的,他包括县、城市、区、甚至某个网吧的信息。这里我们要分析他,具体代码如下
///
<summary>
更具IP获取城市代码
</summary>
///
<param name="ipDataPath">
IP库地址
</param>
///
<param name="ip">
IP地址
</param>
///
<returns>
城市代码
</returns>
public
static
string
GetCityCodeByIp(
string
ipDataPath,
string
ip){
IPLocation.IPLocation.IPLocate(ipDataPath,ip);
var country
=
IPLocation.IPLocation.Country;
var regex
=
new
Regex(
"
([^市省]+)市
"
);
var match
=
regex.Match(country);
var area
=
string
.Empty;
var province
=
string
.Empty;
var city
=
string
.Empty;
var matCity
=
Regex.Matches(country,
"
([^市省]+)市
"
);
var matArea
=
Regex.Matches(country,
"
([^市省区]+)区
"
);
var matProvince
=
Regex.Matches(country,
"
([^省]+)省
"
);
if
(matArea.Count
>
0
){
area
=
matArea[
0
].Result(
"
$1
"
);
}
if
(matProvince.Count
>
0
)
{
province
=
matProvince[
0
].Result(
"
$1
"
);
}
if
(matCity.Count
>
0
)
{
city
=
matCity[matCity.Count
-
1
].Result(
"
$1
"
);
}
var code
=
string
.Empty;
if
(
!
string
.IsNullOrEmpty(area))
{
code
=
GetCityCodeByName(area);
if
(
!
string
.IsNullOrEmpty(code))
{
return
code;
}
}
if
(
!
string
.IsNullOrEmpty(city))
{
code
=
GetCityCodeByName(city);
if
(
!
string
.IsNullOrEmpty(code))
{
return
code;
}
}
if
(
!
string
.IsNullOrEmpty(province))
{
code
=
GetCityCodeByName(province);
if
(
!
string
.IsNullOrEmpty(code))
{
return
code;
}
}
if
(
string
.IsNullOrEmpty(code)){
code
=
GetCityCodeByName(country.Substring(
0
,
2
));
if
(
!
string
.IsNullOrEmpty(code))
{
return
code;
}
}
return
string
.Empty;
}
如此整个天气预报获取就完成了,请下载代码Flashado.Weather.WInForm工程是测试程序,下载后大家可直接用它测试。
天气预报源代码 http://www.dyinfor.com/files/Flashado.Weather.rar
转:http://www.cnblogs.com/ailiangwu/archive/2009/07/22/1528342.html