追求新生活 名字2008

活着的意义

C#抓取网页数据、分析(例如抓取天气预报)-091016有更新091110update again

先看这位大哥的blog,比较好

首先将网页内容整个抓取下来,数据放在byte[]中(网络上传输时形式是byte),进一步转化为String,以便于对其操作,实例如下:
private static string GetPageData(string url)
{
    if (url == null || url.Trim() == "")
        return null;
    WebClient wc = new WebClient();//定义
    wc.Credentials = CredentialCache.DefaultCredentials;
    Byte[] pageData = wc.DownloadData(url);
    return Encoding.Default.GetString(pageData);//.ASCII.GetString
}
 
得到了数据的字符串形式,然后可以对网页进行解析了(其实就是对字符串的各种操作和正则表达式的应用):
// 解析页面,查找链接
// 此处尚需扩展,还有某些形式的链接不被识别
string strRef = @"(href|HREF|src|SRC|action|ACTION|Action)[ ]*=[ ]*[""'][^""'#>]+[""']";
MatchCollection matches = new Regex(strRef).Matches(strResponse);
strStatus += "找到: "+matches.Count+" 个链接\r\n";

上面的例子将网页中的链接解析出来,strRef变量表示了正则表达式的模式,变量matches表示符合匹配的项目的集合,后面的Regex(strRef).Matches(strResponse)就是创建正则规则使得strResponse里符合strRef模式的字符串都返回。然后调用matches的变量就可以取得各种信息了。
当然,这里只能识别一些基本的链接形式,像script中的链接和一些不带“”的链接都没有被支持,这个的扩展还是比较简单的。

常用的的解析还有以下几种:
//获取标题
Match TitleMatch = Regex.Match(strResponse, "<title>([^<]*)</title>", RegexOptions.IgnoreCase | RegexOptions.Multiline);
title = TitleMatch.Groups[1].Value;

//获取描述信息
Match Desc = Regex.Match(strResponse, "<Meta name=\"DESCRIPTION\" content=\"([^<]*)\">", RegexOptions.IgnoreCase | RegexOptions.Multiline);
strdesc = Desc.Groups[1].Value;

//获取网页的大小
size = strResponse.Length;

//去除Html标签

private string StripHtml(string strHtml)
{
 Regex objRegExp = new Regex("<(.|\n)+?>");
 string strOutput = objRegExp.Replace(strHtml, "");
 strOutput = strOutput.Replace("<", "&lt;");
 strOutput = strOutput.Replace(">", "&gt;");
 return strOutput;
}     
有些例外会使得去除不干净,所以建议连续两次转化。这样将Html标签转化为了空格。太多连续的空格会影响之后对字符串的操作。所以再加入这样的语句:
//把所有空格变为一个空格
Regex r = new Regex(@"\s+");
wordsOnly = r.Replace(strResponse, " ");
wordsOnly.Trim();
写的不简单,但我还可以看的明白,注意using System.Text;
using System.Text.RegularExpressions;要写上
实际应用中:我做测试,获取一个区的气象部门的天气预报情况

 private static string GetPageData(string url)//获取url字符串的方法
    {
        if (url == null || url.Trim() == "")
            return null;
        WebClient wc = new WebClient();
        wc.Credentials = CredentialCache.DefaultCredentials;
        Byte[] pageData = wc.DownloadData(url);
        return Encoding.Default.GetString(pageData);//.ASCII.GetString
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        string strResponse = GetPageData(TextBox1 .Text );
        string strRef = @"(href|HREF|src|SRC|action|ACTION|Action)[ ]*=[ ]*[""'][^""'#>]+[""']";
        MatchCollection matches = new Regex(strRef).Matches(strResponse);//在strResponse匹配的字符串
        TextBox2.Text = "找到: " + matches.Count + " 个链接\r\n";
   //以上为仿照学习例子,测试是否成功,果然成功,下面为应用
       string strRef2 = @"MARQUEE";//定义关键字符串,我原来在要获得数据的网页上看到的关键字,看html源就行
         TextBox3.Text = strResponse.Substring(strResponse.IndexOf(strRef2,2800)+120, 135).ToString();
  //strResponse字符串中截取从strResponse.IndexOf(strRef2,2800)+120开始的 135个字符。
    }
测试:我在TextBox1 .Text 中输入:http://www.py121.com/weathe.jsp,那么,立刻可以获得天气情况的报道文字了,这就是最基本的c#抓取类了。
最后还需要小小修改哦。因为有些时候在字符不确定性就要做调整,譬如该天气情况,有时候没有这么多字符显示,你硬要显示,则会出错哦。我的源码段
//抓天气情况
            string strResponse = GetPageData("http://www.py121.com/weathe.jsp");
            string strRef2 = @"MARQUEE";
            string str_last_index = strResponse.Substring(strResponse.IndexOf(strRef2, 2800) + 113, 70).Trim().ToString();
            if (str_last_index.IndexOf('时') > 1)//091110发现始终不能解决两个 '时' 出现的问题,所以10行后有修改正版
            {
                get_weathe = strResponse.Substring(strResponse.IndexOf(strRef2, 2800) + 113, str_last_index.IndexOf('时') + 2).Trim().ToString();//字符串从IndexOf(strRef2, 2800) + 113开始,'时'为标志结束
            }
            else
            {
                get_weathe = strResponse.Substring(strResponse.IndexOf(strRef2, 2800) + 113, 60).Trim().ToString();
            }
这次主要利用了indexof判断 '时'在字符出现的位置,解决了有时候不能显示的漏洞
get_weathe为全局变量,到时候就可以在前台用javascript调用了。ok

091110更新为

if (str_last_index.IndexOf('时') > 1)
            {
                if (str_last_index.IndexOf('时') < 50)//判断是否 '时' 在字符串中 大于1索引,但又不止一个 '时'
                {
                   get_weathe = strResponse.Substring(strResponse.IndexOf(strRef2, 2800) + 113, str_last_index.LastIndexOf ('时') + 10).Trim().ToString();//字符串从IndexOf(strRef2, 2800) + 113开始,'时'为标志结束       
                }
                else
                {
                    get_weathe = strResponse.Substring(strResponse.IndexOf(strRef2, 2800) + 113, str_last_index.IndexOf('时') + 2).Trim().ToString();//字符串从IndexOf(strRef2, 2800) + 113开始,'时'为标志结束
                }
            }
            else
            {
                get_weathe = strResponse.Substring(strResponse.IndexOf(strRef2, 2800) + 113, 60).Trim().ToString();
            }
当然,我们还可以抓取某网站数据的变化更新,这要用到output&&input 数据库.
该例子的关键还是:对字符串的各种操作和正则表达式的应用


简单介绍一下WebClient:
    WebClient 类提供向 URI 标识的任何本地、Intranet 或 Internet 资源发送数据以及从这些资源接收数据的公共方法。
    WebClient 类使用 WebRequest 类提供对资源的访问。WebClient 实例可以通过任何已向 WebRequest.RegisterPrefix 方法注册的 WebRequest 子代访问数据。
    注意
    默认情况下,.NET Framework 支持以 http:、https:、ftp:、和 file: 方案标识符开头的 URI。 

 下面描述用于将数据上载到资源的 WebClient 方法: 
    OpenWrite  检索一个用于将数据发送到资源的 Stream。
    OpenWriteAsync  检索 Stream,它在不阻止调用线程的情况下将数据发送到资源。
    UploadData  将字节数组发送到资源,并返回包含任何响应的 Byte 数组。
    UploadDataAsync  在不阻止调用线程的情况下,将 Byte 数组发送到资源。
    UploadFile  将本地文件发送到资源,并返回包含任何响应的 Byte 数组。
    UploadFileAsync  在不阻止调用线程的情况下,将本地文件发送到资源。
    UploadValues  将 NameValueCollection 发送到资源,并返回包含任何响应的 Byte 数组。
    UploadValuesAsync  在不阻止调用线程的情况下,将 NameValueCollection 发送到资源,并返回包含任何响应的 Byte 数组。
    UploadString  在不阻止调用线程的情况下,将 String 发送到资源。
    UploadStringAsync  在不阻止调用线程的情况下,将 String 发送到资源。 

    下面描述从资源下载数据的 WebClient 方法: 
    OpenRead  从资源以 Stream 的形式返回数据。
    OpenReadAsync  在不阻止调用线程的情况下,从资源返回数据。
    DownloadData 从资源下载数据并返回 Byte 数组。
    DownloadDataAsync  在不阻止调用线程的情况下,从资源下载数据并返回 Byte 数组。
    DownloadFile  从资源将数据下载到本地文件。
    DownloadFileAsync  在不阻止调用线程的情况下,将数据从资源下载到本地文件。
    DownloadString  从资源下载 String 并返回 String。
    DownloadStringAsync  在不阻止调用线程的情况下,从资源下载 String。 

    您可以使用 CancelAsync 方法取消尚未完成的异步操作。
    默认情况下,WebClient 实例不发送可选的 HTTP 报头。如果您的请求需要可选报头,必须将该报头添加到 Headers 集合。例如,要在响应中保留查询,必须添加用户代理报头。此外,如果用户代理标头丢失,服务器可能返回 500(内部服务器错误)。
    在 WebClient 实例中,AllowAutoRedirect 设置为 true。
    给继承者的说明 派生类应调用 WebClient 的基类实现,以确保派生类按预期方式工作。
例如:

C# 利用WebClient类盗取站点(http://www.ip138.com)手机号信息

首先我们每个人都想查对方的手机所在地及属于什么类型卡,那我们利用C#封装的类WebClient,NameValueCollection,Regex等分别属于的命名空间是using System.Net、using System.Text、using System.Collections.Specialized、using System.Text.RegularExpressions。是在Vs2005环境下测试,下面代码测试如下:
private void InitWeaOne()
     {
 3       WebClient wb = new WebClient();
 4       NameValueCollection myNameValueCollection = new NameValueCollection();
 5
 6       myNameValueCollection.Add("mobile""13777483912");
 7       myNameValueCollection.Add("action""mobile");
 8       byte[] pagedata = wb.UploadValues  (http://www.ip138.com:8080/search.asp, myNameValueCollection);
 9       string result = Encoding.Default.GetString(pagedata);
10       string pat = "tdc2>([^<]*)</TD>";
11       Regex r = new Regex(pat, RegexOptions.IgnoreCase);
12       Match m = r.Match(result);
13       string[] strInfo = new string[3"""""" };
14       int i = 0;
15       while (m.Success)
16       {
17           if (i < strInfo.Length)
18           {
19               strInfo[i] = m.ToString().Substring(5);
20           }

21           m = m.NextMatch();
22           i++;
23       }

24       string a = strInfo[0].ToString();
25       string g = strInfo[1].ToString();
26       string f = strInfo[2].ToString();
27   
28  }

posted on 2009-08-12 12:27  pyman  阅读(4204)  评论(0编辑  收藏  举报

导航