巧用IronPython做更灵活的网页爬虫
创始人
2024-07-21 10:31:26
0

由于各种原因,我们经常需要去别的网站采集一些信息,.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. import clr, sys  
  2. clr.AddReference("SgmlReaderDll")  
  3. clr.AddReference("System.Xml") 

完了导入我们需要的类

  1. from Sgml import *  
  2. from System.Net import *  
  3. from System.IO import TextReader,StreamReader  
  4. from System.Xml import *  
  5. from System.Text.UnicodeEncoding import UTF8 

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

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

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

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

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

  1. class Post:  
  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. class BaiDu:  
  2.     def __init__(self,encoding):  
  3.         self.cc = self.cc = CookieContainer()          
  4.         self.encoding = encoding  
  5.         self.posts = [] 

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

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

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

  1. Dictionary options = new Dictionary();  
  2. options["Debug"] = true;  
  3. ScriptEngine engine = Python.CreateEngine(options);  
  4. ScriptScope scope = engine.ExecuteFile("baidu.py");  
  5. dynamic baidu = engine.Operations.Invoke(scope.GetVariable("BaiDu"), "GBK"); 

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

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

原文链接:http://www.cnblogs.com/onlytiancai/archive/2011/02/22/1960859.html

【编辑推荐】

  1. Python 3.2发布 改进调试器PDB
  2. 浅谈Python Web的五大框架
  3. 一个Python程序员的进化
  4. 年度黑马Python 自省指南
  5. 学习python处理python编码

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...
2012年第四季度互联网状况报... [[71653]]  北京时间4月25日消息,据国外媒体报道,全球知名的云平台公司Akamai Te...