一步一步教你抓数据——用.net精确提取网站数据的通用方法

具体实现思路:

1 首先用WebClient类下载网页源码

  public static string DownLoadHtml(string url)
        
{
            
string output = "";
            Encoding encode 
= Encoding.Default;
            WebClient webclient 
= new WebClient();
            
try
            
{
                webclient.Headers.Add(
"Referer", url);
                
byte[] buff = webclient.DownloadData(url);
                output 
= encode.GetString(buff);
            }

            
catch
            
{
            }

            
return output;
        }

需要注意的:
有的网页可能下不下来,有种种原因比如需要cookie,编码问题等等
这是就要具体问题具体分析比如在头部加入cookie
 webclient.Headers.Add("Cookie", cookie);
这样可能需要一些重载方法。根据需要写就可以了。

2 下一步过滤掉不必要的特殊字符,把下载下来的网页内容清干净,方便抓取(比如空格双引号)
过滤特殊字符

3最后也是最重要的即从目标字符串的第begin个字符处开始,读取以 strBegin 开头,
strEnd 结束的字符串.并返回获取到的内容,如果不存在,返回空。

public static string GetHTMLContent(string strTarget, string strBegin, string strEnd, ref int begin)
        
{
            
string result;
            
int posBegin, posEnd;
            posBegin 
= strTarget.IndexOf(strBegin, begin);
            
if (posBegin != -1)
            
{
                posEnd 
= strTarget.IndexOf(strEnd, posBegin + strBegin.Length);
                
if (posEnd > posBegin)
                
{
                    result 
= strTarget.Substring(posBegin, posEnd + strEnd.Length - posBegin);
                    begin 
= posEnd + strEnd.Length;
                    
return result;

                }

            }

            begin 
= -1;
            
return "";
        }

需要注意的:
begin是引用类型,有可能一段网页中可能会有重复的关键词
比如<!--文章标题-->,<!--文章标题-->,如果你想对第二个文章标题里的内容
操作那么begin就派上用场了,他每次抓取完都返回抓取内容的结尾,在调用
此方法就可以得到第二个<!--文章标题-->的内容。

在抓取完的数据一般都存在数据库中,如果是一个表直接存放就可以了.
如果是两个表比如新闻名称和新闻内容分开,这是你就需要用到sql server的一个全局变量
Select @@IDENTITY;它可以标识生成记录的唯一ID。以用作插入新闻内容用。(两个表以news_id关联)


4 因为是精确抓取在程序处理的过程中要用到很多正则,比如去除连接去除括号等,这当然是简单的正则了。
(见到有高手说动态生成正则还没弄懂学习之~)
  Regex pattern1 = new Regex("<a.*?</a>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
  Regex pattern2 = new Regex("(.*?)", RegexOptions.Singleline | RegexOptions.IgnoreCase);
  newscontent = pattern1.Replace(newscontent, "");
  newscontent = pattern2.Replace(newscontent, "");
程序里除了主要的方法外用得最多的就属正则表达式了,处理字符串用正则表达式那是当仁不让,

5 下面是抓取时一些特殊情况的处理:
1比如新闻只让抓当日的
  
static DateTime todaysdate = DateTime.Now.Date;
  
//date为抓取的日期
  DateTime newsday = Convert.ToDateTime(date);
  newsday 
= newsday.Date;
  TimeSpan s 
= todaysdate - newsday;
  
if (s.TotalDays.ToString() == "0")
     
{
    这里干活
     }

2过滤特殊字符比如url里有bbs关键字的不抓
  
if (newsurl.IndexOf("bbs"> 0)
  
{
      
continue;
  }

3写入新闻唯一id到文件避免同一天抓两次数据引起新闻重复(再抓的时候比较就可以了)
  FileStream fs 
= new FileStream(filepaths, FileMode.OpenOrCreate, FileAccess.Write);
  StreamWriter sw 
= new StreamWriter(fs, System.Text.Encoding.GetEncoding("gb2312"));
  sw.Flush();
  sw.BaseStream.Seek(
0, SeekOrigin.Begin);
  
//唯一id
  sw.Write(newsqid);
  sw.Close();   


6抓数据的时候可以把要抓的网站和一些关键字写入xml一边程序里直接调用
这样比较省时省力,比如:

<?xml version="1.0" encoding="utf-8" ?>
<GetDatas>
  
<MyData>
  
<CarUrl>http://www.webhost/mysite.html</CarUrl>
    
<CarFilterf><![CDATA[列表页特征字符串开始]]></Filterf>
    
<CarFilters><![CDATA[列表页特征字符串结束]]></Filters>
    
<CarDetailFilterf><![CDATA[详情页特征字符串开始]]></CarDetailFilterf>
    
<CarDetailFilters><![CDATA[详情页特征字符串结束]]></CarDetailFilters>
    
<NewsKey>种类一</NewsKey>
  
</MyData>
  
<MyData>
    
<CarUrl>http://www.webhost/mysite.html</CarUrl>
    
<Filterf><![CDATA[列表页特征字符串开始]]></CarFilterf>
    
<Filters><![CDATA[ 列表页特征字符串结束 ]]></CarFilters>
    
<DetailFilterf><![CDATA[详情页特征字符串开始]]></CarDetailFilterf>
    
<DetailFilters><![CDATA[详情页特征字符串结束]]></CarDetailFilters>
    
<NewsKey>种类二</NewsKey>
  
</CarData>
  
<MyData>
程序里根据newskey的种类写入数据库方便调用。

7最后就是写入数据库了,可以以上面xml里newskey的键值插入数据库。


posted @ 2008-01-26 11:06 早班火车 阅读(2835) 评论(22)  编辑 收藏 所属分类: asp.net2.0

  回复  引用  查看    
#1楼 2008-01-26 11:16 | 暗香浮动      
可以做的工作还挺多的。
需要登陆的情况。
有viewstate的情况。
过滤样式。
去重。
分页数据抓取。
....
关注。
  回复  引用  查看    
#2楼 2008-01-26 11:24 | format      
要达到好的适应性和广泛应用,需要一套好的过滤机制和容易操作的规则定制界面.
我认为从网页提取数据就是对非结构化的信息进行结构化处理,关键就是结构化的规则.
关注.
  回复  引用  查看    
#3楼 2008-01-26 11:59 | PointNet      
写得不错,不过现在有专门的抓包工具!
  回复  引用  查看    
#4楼 [楼主]2008-01-26 13:58 | 早班火车~      
@暗香浮动
@format
两位说得都很有道理,
这只是一个很简单网页数据抓取,
在真正项目里运用,需要考虑的问题还有很多。
继续研究中。也欢迎一块讨论。
  回复  引用    
#5楼 2008-01-26 19:51 | 帝之晓0 [未注册用户]
不错,我本来是用正则表达式来取天气预报的信息的,发现还是你这样子简单啊
  回复  引用    
#6楼 2008-01-26 20:02 | 学习学习了 [未注册用户]
非常不错 学习学习 以后采集东西可以用的到 LZ可以看下火车头 也是。NET写的
  回复  引用    
#7楼 2008-01-26 20:26 | Everett [未注册用户]
需要登录那种情况怎么处理, 另外很多是登录后经过中间页面进行跳转了, 这种怎么抓?
  回复  引用  查看    
#8楼 2008-01-26 21:38 | overred      
http://www.cnblogs.com/overred/archive/2007/12/31/cjtoo.html
想年后推出
同时征求大家意见
呵呵
  回复  引用  查看    
#9楼 [楼主]2008-01-26 21:41 | 早班火车~      
@帝之晓0
@学习学习了
呵呵,其实这个还比较简单,有很多还需要考虑,
以后会弄个更周全的和大家分享讨论~
  回复  引用  查看    
#10楼 [楼主]2008-01-26 21:42 | 早班火车~      
@overred
呵呵,期待你的大作,
要说采集数据你可是博客园的元老了:-)
学习~
  回复  引用  查看    
#11楼 2008-01-27 02:11 | cn罗克      
我也这在学习这块东西。不过我觉得采用正则提取数据,希望大家感兴趣的多多讨论,持续关注中。
  回复  引用    
#12楼 2008-01-27 09:01 | william.wu [未注册用户]
实际情况比这个复杂。一般从登录开始,抓http包回来分析,然后自己模拟发包过去,要登录,有验证码,维护cookie,目标数据嵌在多层框架里等等等。最后得到的数据我一般直接用正则匹配模式,很少像你这样获取begin和end然后多次处理。
  回复  引用  查看    
#13楼 [楼主]2008-01-27 09:41 | 早班火车~      
@william.wu
呵呵,是这样的,实际抓取的时候会遇到很多问题,
文章里也有提到,以后会考虑再来个新版本~
谢谢指教~:-)
  回复  引用  查看    
#14楼 2008-01-27 10:48 | overred      
@早班火车~
不敢当。。。。
希望一起研究啊 呵呵

希望楼主把这系列文章写下去
支持
  回复  引用    
#15楼 2008-01-28 09:27 | shanvenleo [未注册用户]
这个能采集图片吗?
  回复  引用  查看    
#16楼 2008-01-28 09:53 | 瓜牛      
现在不是有:htmlParser.Net吗?很好用的,而且是开源的!
  回复  引用  查看    
#17楼 2008-01-28 10:21 | 黯然消魂      
如果服务器用的GzIP编码呢?楼主又当如何?
  回复  引用    
#18楼 2008-01-28 10:40 | A.Z! [未注册用户]
果然这样的东西不能开源,要不然随便哪个都拿过来小改一下,世界就会一团糟。
  回复  引用    
#19楼 2008-01-28 17:15 | 现代 [未注册用户]
怎么处理由AJAX做的页面信息,想问LZ有什么想法
  回复  引用    
#20楼 2008-01-29 18:50 | Lyricbus [未注册用户]
很好的文章!謝謝!受益匪淺
  回复  引用  查看    
#21楼 2008-02-01 17:18 | 马可香蕉      
不错,收藏
  回复  引用  查看    
#22楼 2008-02-14 10:32 | 天堂的狼      
支持,关注一下