巧用IronPython做更灵活的网页爬虫

2011-02-23 09:48 胡浩 胡浩的博客 我要评论(0) 字号:T | T
巧用IronPython做更灵活的网页爬虫
如果有了IronPython,可以把抓取和分析的逻辑做成Python脚本,如果对方页面结构变了,只需修改脚本就行了,不需重新编译软件,这样可以用c#做交互和界面部分,用Python封装预期经常变化的部分。 AD:

由于各种原因,我们经常需要去别的网站采集一些信息,.net下所有相关的技术都已经非常成熟,用Webrequest抓取页面,既支持自定义Reference头,又支持cookie,解析页面一般都是用正则,而且对方网站结构一变,还得重新改代码,重新编译,发布。

如果有了IronPython,可以把抓取和分析的逻辑做成Python脚本,如果对方页面结构变了,只需修改脚本就行了,不需重新编译软件,这样可以用c#做交互和界面部分,用Python封装预期经常变化的部分。

安装好IronPython和vs.net 2010后,还需要下载一个SGMLReader(见参考链接),这个组件可以把格式不是很严格的HTML转换成格式良好的XML文件,甚至还能增加DTD的验证

我们以抓取百度贴吧页面为例,新建一个Console项目,引用IronPython,Microsoft.Dynamic,Microsoft.Scripting,SgmlReaderDll这些组件,把SGMLReader里的Html.dtd复制到项目目录下,如果没有这个,它会根据doctype去网络上找dtd,然后新建baidu.py的文件,最后在项目属性的生成事件里写上如下代码,把这两个文件拷贝到目标目录里

  1. copy$(ProjectDir)\*.py$(TargetDir)
  2. copy$(ProjectDir)\*.dtd$(TargetDir)

在baidu.py里首先引用必要的.net程序集

  1. importclr,sys
  2. clr.AddReference("SgmlReaderDll")
  3. clr.AddReference("System.Xml")

完了导入我们需要的类

  1. fromSgmlimport*
  2. fromSystem.Netimport*
  3. fromSystem.IOimportTextReader,StreamReader
  4. fromSystem.Xmlimport*
  5. fromSystem.Text.UnicodeEncodingimportUTF8

利用SgmlReader写一个把html转换成xml的函数,注意SystemLiteral属性必须设置,否则就会去网上找dtd了,浪费时间

  1. deffromHtml(textReader):
  2. sgmlReader=SgmlReader()
  3. sgmlReader.SystemLiteral="html.dtd"
  4. sgmlReader.WhitespaceHandling=WhitespaceHandling.All
  5. sgmlReader.CaseFolding=CaseFolding.ToLower
  6. sgmlReader.InputStream=textReader
  7. doc=XmlDocument()
  8. doc.PreserveWhitespace=True
  9. doc.XmlResolver=None
  10. doc.Load(sgmlReader)
  11. returndoc

利用webrequest写一个支持cookie和网页编码的抓网页方法

  1. defgetWebData
  2. (url,method,data=None,cookie=None,encoding="UTF-8"):
  3. req=WebRequest.Create(url)
  4. req.Method=method
  5. ifcookie!=None:
  6. req.CookieContainer=cookie
  7. ifdata!=None:
  8. stream=req.GetRequestStream()
  9. stream.Write(data,0,data.Length)
  10. rsp=req.GetResponse()
  11. reader=StreamReader
  12. (rsp.GetResponseStream(),UTF8.GetEncoding(encoding))
  13. returnreader

写一个类来定义抓取结果,这个类不需要在c#项目里定义,到时候直接用c# 4.0的dynamic关键字就可以使用

  1. classPost:
  2. def__init__(self,hit,comments,title,link,author):
  3. self.hit=hit
  4. self.comments=comments
  5. self.title=title
  6. self.link=link
  7. self.author=author

定义主要工作的类,__init__大概相当于构造函数,我们传入编码参数,并初始化cookie容器和解析结果,[]是python里的列表,大约相当于c#的List

  1. classBaiDu:
  2. def__init__(self,encoding):
  3. self.cc=self.cc=CookieContainer()
  4. self.encoding=encoding
  5. self.posts=[]

接下来定义抓取方法,调用getWebData抓网页,然后用fromHtml转换成xml,剩下的就是xml操作,和.net里一样,一看便知

  1. defgetPosts(self,url):
  2. reader=getWebData
  3. (url,"GET",None,self.cc,self.encoding)
  4. doc=fromHtml(reader)
  5. trs=doc.SelectNodes
  6. ("html//table[@id='thread_list_table']/tbody/tr")
  7. self.parsePosts(trs)
  8. defparsePosts(self,trs):
  9. fortrintrs:
  10. tds=tr.SelectNodes("td")
  11. hit=tds[0].InnerText
  12. comments=tds[1].InnerText
  13. title=tds[2].ChildNodes[1].InnerText
  14. link=tds[2].ChildNodes[1].Attributes["href"]
  15. author=tds[3].InnerText
  16. post=Post(hit,comments,title,link,author)
  17. self.posts.append(post)

c#代码要创建一个脚本运行环境,设置允许调试,然后执行baidu.py,最后创建一个Baidu的类的实例,并用dynamic关键字引用这个实例

  1. Dictionaryoptions=newDictionary();
  2. options["Debug"]=true;
  3. ScriptEngineengine=Python.CreateEngine(options);
  4. ScriptScopescope=engine.ExecuteFile("baidu.py");
  5. dynamicbaidu=engine.Operations.Invoke(scope.GetVariable("BaiDu"),"GBK");

接下来调用BaiDu这个python类的方法获取网页抓取结果,然后输出就可以了

  1. baidu.getPosts("http://tieba.baidu.com/f?kw=seo");
  2. dynamicposts=baidu.posts;
  3. foreach(dynamicpostinposts)
  4. {
  5. Console.WriteLine("{0}
  6. (回复数:{1})(点击数:{2})[作者:{3}]",
  7. post.title,
  8. post.comments,
  9. post.hit,
  10. post.author);
  11. }
标签 ,

发表回复