手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表2009年03月2日的文章

hongyin163:网页信息抓取实现

突然怀念起几年前,walkerlee、hihiyou、我还在为了NC进行打拼,也曾为了某个正则几乎通宵不睡在测试。其实在NC前已经有过多款的采集软件,但其实都是属于功能单一情况,如:开花石头的逆向小偷等。NC只是一款整合了一起功能的采集软件,也算是开创了先河吧?反正自那以后,采集程序越来越多,先是剑枫,然后是小蜜蜂,再后来是火车头,采集软件越来越多之后,随之而来的就是性能问题,采用WEB方式的采集,不可避免的就是性能。你没有办法控制一个WEB页面不会超时,而且PHP由于自身的执行方式,也就不可能是多线程执行,后来大多数人,采用了iframe的方式,让一个页面打开十个左右的iframe然后根据执程的ID来进行判断。

过去的事情,总是让人怀念,在看到这个作者写类似的采集时,感慨的同时,留一份备份,以作纪念

原文:http://www.cnblogs.com/hongyin163/archive/2009/02/11/1388615.html
内容:

最近公司需要开发一个简历导入功能,类似博客搬家或者邮箱搬家,之前抓取信息是利用火车采集器,但是简历导入功能需要用户登陆以后才能获取简历数据,无奈只好自己开发了。

首先是遇到的问题是:如何实现模拟登陆?

我们知道一般的网站都是通过Cookies来维护状态的,我抓的网站也是支持利用Cookies来验证用户的,构造一个post数据包,向服务器提 交数据,在配置火车采集器的时候,也是要先利用WSockExpert.exe工具获得Post数据包,之后修改用户名和密码,向服务器提交的。

提交了登陆数据后还没完成登陆,虽然服务器会返回登陆后的页面数据,但是如果在进入其他的链接页面,还是不允许的,因为服务器每次都需要通过你提交 过去Cookies来验证你是否登陆,在asp.net里,利用Cookies存储身份验证票证,每次都需要向服务器提交的,初学asp.net总是弄不 明它的form验证机制,它封装了太多信息,虽然用几行代码就能实现验证,后来看了些web开发基础知识才弄明白,在这个你需要保存上次登陆后返回的 Cookies,在下次有其他请求时带上这个Cookies就可以了,怎么带上呢?下面是我在.net里的实现,很简单!

利用HttpWebRequest类的CookieContainer来保存,这个CookieContainer会保存服务器回传的 Cookies,但是前提是你在初始化HttpWebRequest的时候,记得实例化这个CookieContainer,一般的请求不需要实例它的, 简单的代码如下:

 httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(URL);            
  httpWebRequest.CookieContainer = new CookieContainer();
  httpWebRequest.ContentType = "application/x-www-form-urlencoded";
 httpWebRequest.Method = "POST"

 为了能全局使用这个CookieContainer ,你可以把它作为全局变量,这样在下次request的时候将其赋给CookieContainer 属性就行了。

详细了解CookieContainer 见:http://msdn.microsoft.com/zh-cn/vstudio/system.net.cookiecontainer(VS.80).aspx

维护了这个CookieContainer 后,我们就可以访问登陆后的页面了,模拟登陆问题解决。

其次遇到的问题自然是:如果从网页上获得想要的信息?

 要在网页抓取信息,实现起来最简单,同时也是最繁琐的方法,那就是模板方法获取了,从火车采集器的配置过程看出来,它也就是用这种方法而已,不过 人家能把抓取器做成成熟的产品,并且热卖,这个比不了,所以成功与否不完全取决于技术,火车采集器虽然配置起来挺繁琐,但是用起来还不错。

用这种方式你需要做个一个模板,你需要知道目标网页的结构,知道要找的信息在什么地方,之后记录在它的前面和后面的字符串,你可以利用截取字符串的 方式获得目标信息,也可以利用正则标式获得,要保证前面和后面的字符串是唯一的,很简单,计算一下,或者匹配一下就可以获得目标信息,但是实际做起来还是 会遇到一些问题:

下面是我遇到问题;

1.首先我是想利用正则表达式匹配,但是模块里设置的前缀和后缀里有回车换行\r\n,结果总是匹配不成功,我正则的功底很差,最后知道怎么回事了,把\r\n替换成(\s*),问题解决,您可以想出为什么了吧?

2.利用字符串截取方式获取,在正则还不是很精通,用这种方式最保险了,但是在截取字符串前记得调整下目标页代码,从xml配置文件里读取的前缀和 后缀中可能有回车和换行,但是回车换行在不同系统里字符表现是不一样的,Windows里是\r\n,Linux里是\n,所以要记得统一。

3.前后缀不唯一,有时在页面里有两个不同的目标信息,但却有相同的前缀,比如:

<td width="25%" class="ResTbLfPd">数据库</td>
<td width="25%" class="ResTbLfPd">软件工程师</td>

如果用相同的前缀就比较难截到想要的信息了,我想了个办法,当然方法可能比较笨,但是问题解决了,也是火车给我的启示,利用多个字符串定位目标信息,比如我想抓去 软件工程师 ,前缀就是:

<td width="25%" class="ResTbLfPd">*</td>
<td width="25%" class="ResTbLfPd">

在信息可能不同的地方用*代替,类似通配符,这样利用*将一个字符串切割为两个,先找到第一个,之后以这个索引位置为起点,再找第二个字符串,这样就可以定位到最终的信息了,同样可以用多个字符串三个或更多,这样实现是解决了问题,希望有更好的方式,希望以后会改进。

4.在抓取信息的时候还可以利用MITHtmlPparser,这是一个开源的类库,在codeproject找搜到的,将网页内的所以标签都分析出来,如果获取信息不是很多、很碎的话,用这个也比较好用,只需知道那个最终要得到信息在那个标签里,然后直接取出就行了。

好了,希望在新的一年里能学到更多,能经得住考验!

Tags: 采集

ZendStudio.Net:某网站AJAX的加密压缩传输算法的一点研究

题前话:
其实以前也看到过类似的东西,但是我没有想到进用gzip之类的加密,看来,即使是见过的东西,也有不熟悉的。看到作者这样仔细的分析相类似的资料,当然要备份一点。
原文:http://www.zendstudio.net/js-zip-inflate/
AJAX还是比较强大的!(显然,这是一句废话),最近在研究一个网站的AJAX应用中发现其中的“拓展视野”部分频频被挖掘出来(也由此可见,平时本人 的视野有多么的狭窄了),首先是全站的JS全部使用packed进行了压缩,呃!也不知道这种称法是否正确,就是用 eval(function(p,a,c,k,e,d){})的那种世界各地都很流行的压缩方法吧,在实际的观察中,一个压缩后仅为6K,在我将其转化为 “肉眼能看清楚的代码”之后,足足有20K,可见其效果还是相当明显的;此外,用HttpWatch弄到了传输数据后,居然是加密的。。。形如下面这段:

XML/HTML代码
  1. q1YqT81MzyhRsqpWys3MU7Iy0FHKTaxQsjLWUUrLL8pNBMooqeoZpSnV6igVFGUmp2KoVDIzMrIwNdAzMFBC1pOiVFsLAA==  

任何一个有些许密码学经验的同志都容易很看出来,这是base64编码(我实在不喜欢称这个为“加密”),没错,和各位看官一样,我很快就用php自带的base64_decode函数对其进行了解密,如果您觉得问题到此为止,那就错了!这时我才稍稍感到了有些震撼,解密出来的数据:
大小: 6.92 K
尺寸: 500 x 50
浏览: 1452 次
点击打开新窗口浏览全图
呃!一堆乱码,其实应该是二进制数据,加密了(后来知道是压缩了),可是用户是看不懂这些的,客户端是肯定要进行解密的!用什么?AJAX的当然用JS解密了,挖解密函数啊,挖解密函数,看到了如下的精彩代码:

JavaScript代码
  1. var filterList=eval('('+utf8to16(zip_depress(base64decode(g_pgFilterList)))+')');  

utf8to16()和base64decode()都好理解,也再一次证明加密的最后是用base64编码输出的,关键就是这个zip_depress(),zip解压?
是的,千真万确,用JS实现了zip的解压算法!!!到这里我深深的感到了震撼,原来,我知道的真的太少了啊!虽然之前知晓有md5.js,知道JS在运算方面是没有问题的。不会是这家伙自己写的压缩算法吧?经过搜索,我找到了这个算法(Zip inflate)的原版,原来该网站的制作人员修改了函数名,难怪我直接google不到呢?

什么是inflate算法?—
  1. inflate是GZip, PNG等广泛使用的解压算法,linux也使用inflate对内核进行解压.inflate的解压算法使用的第3种快速解压法的一个子集,它不考虑 LONG_CODE,同时把SAME_LENGTH合并到MEDIUM_CODE。而对于规则的SAME_LENGTH编码,比如length和 distance编码,inflate则使用额外的base和extra表示。这是因为在构造一般的查找表时,虽然对于SAME_LENGTH前缀可以不构造副表,但我们需要另外一个表格来保存符号的顺序,而这个表格的空间可能更大。但对于length和distance编码,他们的顺序是递增的,所以无需额外的表格来保存符号的顺序。  
  2.   
  3. inflate使用root表示上述的b,查找表的数据结构为code.主表和副同时保存在inflate_state结构中的大数组codes[ENOUGH]中.表的构造函数位于inftrees.c文件的inflate_table中.  
令人感到欣喜若狂的是,PHP竟然已经提供的现成函数来解压和压缩inflate,它们是gzinflate()gzdeflate(),哈哈哈!我不禁仰天狂笑的一番,用gzinflate()成功的将上文数据解密,内容是这样的:

JavaScript代码
  1. {"weight":{"min":0,"max":3,"format":"%.2f"},"price":{"min":0,"max":"622850.00","format":"%d"}}  

标准的JSON数据啦,不错!这就为以后的AJAX的传输上多了一个选择,虽然还不确定这种方法能否节省流量(因为base64算法会将原始数据“稍稍” 增大),但客户端有了解压算法,服务端的php压缩函数又是现成的,大不了在base64这个环节上大概需要改进下,我想对于大流量的数据应该还是有确切 效果的。嗯,我很满意。
————————
看完以上的内容,嗯,我也很满意,呵呵