实用科普:爬虫技术浅析 编写爬虫应注意的点

标签: 科普专线 安全科普 | 发表时间:2014-11-19 10:03 | 作者:seay
出处:http://www.cnseay.com

在乌云上看到一个关于爬虫的科普文,写的挺不错,文章里面提到的主要要关注的两个点是URL去重和相似URL过滤,如果写一个漏扫,爬虫在数据处理的效率非常重要,要考虑的点就更多了,有时间在补充 :D

原文地址:http://drops.wooyun.org/tips/3915

0×00 前言


网络爬虫(Web crawler),是一种“自动化浏览网络”的程序,或者说是一种网络机器人。它们被广泛用于互联网搜索引擎或其他类似网站,以获取或更新这些网站的内容和检索方式。它们可以自动采集所有其能够访问到的页面内容,以便程序做下一步的处理。

在WEB2.0时代,动态网页盛行起来。那么爬虫就应该能在页面内爬到这些有javascript生成的链接。当然动态解析页面只是爬虫的一个技术点。下面,我将按照如下顺序分享下面的这些内容的一些个人经验(编程语言为Python)。

1,爬虫架构。

2,页面下载与解析。

3,URL去重方法。

4,URL相似性算法。

5,并发操作。

6,数据存储

7,动态爬虫源码分享。

8,参考文章

0×01 爬虫架构


谈到爬虫架构,不得不提的是Scrapy的爬虫架构。Scrapy,是Python开发的一个快速,高层次的爬虫框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等。

enter image description here

上图是Scrapy的架构图,绿线是数据流向,首先从初始URL 开始,Scheduler 会将其交给 Downloader 进行下载,下载之后会交给 Spider 进行分析,需要保存的数据则会被送到Item Pipeline,那是对数据进行后期处理。另外,在数据流动的通道里还可以安装各种中间件,进行必要的处理。 因此在开发爬虫的时候,最好也先规划好各种模块。我的做法是单独规划下载模块,爬行模块,调度模块,数据存储模块。

0×02 页面下载与解析


页面下载

页面下载分为静态和动态两种下载方式。

传统爬虫利用的是静态下载方式,静态下载的优势是下载过程快,但是页面只是一个枯燥的html,因此页面链接分析中获取的只是< a >标签的href属性或者高手可以自己分析js,form之类的标签捕获一些链接。在python中可以利用urllib2模块或requests模块实现功能。 动态爬虫在web2.0时代则有特殊的优势,由于网页会使用javascript处理,网页内容通过Ajax异步获取。所以,动态爬虫需要分析经过javascript处理和ajax获取内容后的页面。目前简单的解决方法是通过基于webkit的模块直接处理。PYQT4、Splinter和Selenium这三个模块都可以达到目的。对于爬虫而言,浏览器界面是不需要的,因此使用一个headless browser是非常划算的,HtmlUnit和phantomjs都是可以使用的headless browser。

enter image description here

以上这段代码是访问新浪网主站。通过对比静态抓取页面和动态抓取页面的长度和对比静态抓取页面和动态抓取页面内抓取的链接个数。

enter image description here

在静态抓取中,页面的长度是563838,页面内抓取的链接数量只有166个。而在动态抓取中,页面的长度增长到了695991,而链接数达到了1422,有了近10倍的提升。

抓链接表达式

正则:re.compile(“href=\”([^\"]*)\””)

Xpath:xpath(‘//*[@href]‘)

页面解析

页面解析是实现抓取页面内链接和抓取特定数据的模块,页面解析主要是对字符串的处理,而html是一种特殊的字符串,在Python中re、beautifulsoup、HTMLParser、lxml等模块都可以解决问题。对于链接,主要抓取a标签下的href属性,还有其他一些标签的src属性。

0×03 URL去重


URL去重是爬虫运行中一项关键的步骤,由于运行中的爬虫主要阻塞在网络交互中,因此避免重复的网络交互至关重要。爬虫一般会将待抓取的URL放在一个队列中,从抓取后的网页中提取到新的URL,在他们被放入队列之前,首先要确定这些新的URL没有被抓取过,如果之前已经抓取过了,就不再放入队列了。

Hash表

利用hash表做去重操作一般是最容易想到的方法,因为hash表查询的时间复杂度是O(1),而且在hash表足够大的情况下,hash冲突的概率就变得很小,因此URL是否重复的判断准确性就非常高。利用hash表去重的这个做法是一个比较简单的解决方法。但是普通hash表也有明显的缺陷,在考虑内存的情况下,使用一张大的hash表是不妥的。Python中可以使用字典这一数据结构。

URL压缩

如果hash表中,当每个节点储存的是一个str形式的具体URL,是非常占用内存的,如果把这个URL进行压缩成一个int型变量,内存占用程度上便有了3倍以上的缩小。因此可以利用Python的hashlib模块来进行URL压缩。 思路:把hash表的节点的数据结构设置为集合,集合内储存压缩后的URL。

Bloom Filter

Bloom Filter是通过极少的错误换取了存储空间的极大节省。Bloom Filter 是通过一组k 个定义在n 个输入key 上的Hash Function,将上述n 个key 映射到m 位上的数据容器。

enter image description here

上图很清楚的说明了Bloom Filter的优势,在可控的容器长度内,所有hash函数对同一个元素计算的hash值都为1时,就判断这个元素存在。 Python中hashlib,自带多种hash函数,有MD5,sha1,sha224,sha256,sha384,sha512。代码中还可以进行加盐处理,还是很方便的。 Bloom Filter也会产生冲突的情况,具体内容查看文章结尾的参考文章。

在Python编程过程中,可以使用jaybaird提供的BloomFilter接口,或者自己造轮子。

小细节

有个小细节,在建立hash表的时候选择容器很重要。hash表占用空间太大是个很不爽的问题,因此针对爬虫去重,下列方法可以解决一些问题。

enter image description here

上面这段代码简单验证了生成容器的运行时间。

enter image description here

由上图可以看出,建立一个长度为1亿的容器时,选择list容器程序的运行时间花费了7.2s,而选择字符串作为容器时,才花费了0.2s的运行时间。

接下来看看内存的占用情况。

enter image description here

如果建立1亿的列表占用了794660k内存。

enter image description here

而建立1亿长度的字符串却占用了109720k内存,空间占用大约减少了700000k。

0×04 URL相似性


初级算法

对于URL相似性,我只是实践一个非常简单的方法。

在保证不进行重复爬去的情况下,还需要对类似的URL进行判断。我采用的是sponge和ly5066113提供的思路。具体资料在参考文章里。

下列是一组可以判断为相似的URL组

http://auto.sohu.com/7/0903/70/column213117075.shtml

http://auto.sohu.com/7/0903/95/column212969565.shtml

http://auto.sohu.com/7/0903/96/column212969687.shtml

http://auto.sohu.com/7/1103/61/column216206148.shtml

http://auto.sohu.com/s2007/0155/s254359851/index1.shtml

http://auto.sohu.com/s2007/5730/s249066842/index2.shtml

http://auto.sohu.com/s2007/5730/s249067138/index3.shtml

http://auto.sohu.com/s2007/5730/s249067983/index4.shtml

按照预期,以上URL归并后应该为

http://auto.sohu.com/7/0903/70/column213117075.shtml

http://auto.sohu.com/s2007/0155/s254359851/index1.shtml

思路如下,需要提取如下特征

1,host字符串

2,目录深度(以’/’分割)

3,尾页特征

具体算法

enter image description here

算法本身很菜,各位一看就能懂。

实际效果:

enter image description here

上图显示了把8个不一样的url,算出了2个值。通过实践,在一张千万级的hash表中,冲突的情况是可以接受的。

0×05 并发操作


Python中的并发操作主要涉及的模型有:多线程模型、多进程模型、协程模型。Elias专门写了一篇文章,来比较常用的几种模型并发方案的性能。对于爬虫本身来说,限制爬虫速度主要来自目标服务器的响应速度,因此选择一个控制起来顺手的模块才是对的。

多线程模型

多线程模型,是最容易上手的,Python中自带的threading模块能很好的实现并发需求,配合Queue模块来实现共享数据。

多进程模型

多进程模型和多线程模型类似,multiprocessing模块中也有类似的Queue模块来实现数据共享。在linux中,用户态的进程可以利用多核心的优势,因此在多核背景下,能解决爬虫的并发问题。

协程模型

协程模型,在Elias的文章中,基于greenlet实现的协程程序的性能仅次于Stackless Python,大致比Stackless Python慢一倍,比其他方案快接近一个数量级。因此基于gevent(封装了greenlet)的并发程序会有很好的性能优势。

具体说明下gevent(非阻塞异步IO)。,“Gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。”

从实际的编程效果来看,协程模型确实表现非常好,运行结果的可控性明显强了不少, gevent库的封装易用性极强。

0×06 数据存储


数据存储本身设计的技术就非常多,作为小菜不敢乱说,但是工作还是有一些小经验是可以分享的。

前提:使用关系数据库,测试中选择的是mysql,其他类似sqlite,SqlServer思路上没有区别。

当我们进行数据存储时,目的就是减少与数据库的交互操作,这样可以提高性能。通常情况下,每当一个URL节点被读取,就进行一次数据存储,对于这样的逻辑进行无限循环。其实这样的性能体验是非常差的,存储速度非常慢。

进阶做法,为了减少与数据库的交互次数,每次与数据库交互从之前传送1个节点变成传送10个节点,到传送100个节点内容,这样效率变有了10倍至100倍的提升,在实际应用中,效果是非常好的。:D

0×07 动态爬虫源码分享


爬虫模型

enter image description here

目前这个爬虫模型如上图,调度模块是核心模块。调度模块分别与下载模块,析取模块,存储模块共享三个队列,下载模块与析取模块共享一个队列。数据传递方向如图示。

爬虫源码

实现了以下功能:

动态下载

gevent处理

BloomFilter过滤

URL相似度过滤

关键字过滤

爬取深度

Github地址:https://github.com/manning23/MSpider

代码总体来说难度不大,各位轻喷。

0×08 参考文章


感谢以下分享的文章与讨论

http://security.tencent.com/index.php/blog/msg/34 http://www.pnigos.com/?p=217

http://security.tencent.com/index.php/blog/msg/12 http://wenku.baidu.com/view/7fa3ad6e58fafab069dc02b8.html

http://wenku.baidu.com/view/67fa6feaaeaad1f346933f28.html

http://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/

http://www.elias.cn/Python/PyConcurrency?from=Develop.PyConcurrency

http://blog.csdn.net/HanTangSongMing/article/details/24454453

http://blog.csdn.net/historyasamirror/article/details/6746217 http://www.spongeliu.com/399.html

http://xlambda.com/gevent-tutorial/ http://simple-is-better.com/news/334

http://blog.csdn.net/jiaomeng/article/details/1495500 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=1337181

http://www.tuicool.com/articles/nieEVv http://www.zhihu.com/question/21652316 http://code.rootk.com/entry/crawler

博主猜你喜欢:

跨站科普:说一说新手在寻找XSS时所存在的一些误区

安全科普:php中用PDO查询Mysql避免SQL注入示例和应注意的地方

安全科普:深入浅出DDoS攻击防御 补遗篇

安全科普:URL Hacking – 前端URL猥琐流思路

各种php包含漏洞替代技术总结
无觅

相关 [科普 爬虫 技术] 推荐:

实用科普:爬虫技术浅析 编写爬虫应注意的点

- - Seay's blog 网络安全博客
在乌云上看到一个关于爬虫的科普文,写的挺不错,文章里面提到的主要要关注的两个点是URL去重和相似URL过滤,如果写一个漏扫,爬虫在数据处理的效率非常重要,要考虑的点就更多了,有时间在补充 :D. 原文地址:http://drops.wooyun.org/tips/3915. 网络爬虫(Web crawler),是一种“自动化浏览网络”的程序,或者说是一种网络机器人.

网络爬虫

- - 四火的唠叨
文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》. 最近在写一个程序,去爬热门事件和热门关键词网站上的数据. 网络爬虫也叫做网络蜘蛛,是一种互联网机器人,把需要的网页撷取下来,组织成适当格式存储. 它是搜索引擎的重要组成部分,虽然从技术实现上来说,它的难度往往要小于对于得到的网页信息的处理.

Google 图片爬虫

- - 吴良超的学习笔记
这里的 Google 图片爬虫指的是爬取在 Google 上通过关键词搜索得到的图片,由于最近需要一些特定领域的图片,而且现有的数据库满足不了要求,因此就想通过 Google 搜索筛选出这些特定领域的图片,然后下载下来后再进行人工筛选. 这里采用了两种方法,区别在于是否需要解析网页端的 JS 代码.

JSOUP实现简单爬虫

- - ITeye博客
这个说是简单爬虫 其实连个爬虫也算不上吧 功能太精简了.... 流程很简单: 输入几个初始的网页 然后通过JSOUP获取网页中的a标签的href的值. 接着把新得到的地址放入任务队列中. 实现中的worker是一个单线程的派发器 用于产生Parser. Parser用于完成网页的保存 网页的解析 以及入队列操作.

最全Python爬虫总结

- - CSDN博客综合推荐文章
最近总是要爬取一些东西,索性就把Python爬虫的相关内容都总结起来了,自己多动手还是好. (2)保存爬取的图片/视频和文件和网页. (7)某个网站的站内所有目录爬虫. (9)爬虫框架Scrapy   . 二,保存爬取的图片/视频和文件和网页. #图片/视频和文件和网页的地址抓取下来后,利用模块urllib里的urlretrieve()方法下载下来:.

htmlunit爬虫优化方案

- - 研发管理 - ITeye博客
发现很多人搞爬虫会把python作为首选技术,理由是简单;但是本人最熟悉的还是java,所以对java内存浏览器技术htmlunit做了一次研究,发现原生的htmlunit的性能及对多线程的支持不是那么友好,特别是使用代理ip后,oom是很正常的,监控程序并查看源码总结问题原因:. 1、js执行器执行js是使用多线程执行,在关闭js执行线程的时候,使用com.gargoylesoftware.htmlunit.javascript.background.DefaultJavaScriptExecutor这个类的时候,有段代码.

爬虫需谨慎!那些你不知道的爬虫反爬虫套路 学起来

- - IT瘾-bigdata
爬虫与反爬虫,是一个很不阳光的行业. 第一是,这个行业是隐藏在地下的,一般很少被曝光出来. 很多公司对外都不会宣称自己有爬虫团队,甚至隐瞒自己有反爬虫团队的事实. 这可能是出于公司战略角度来看的,与技术无关. 第二是,这个行业并不是一个很积极向上的行业. 很多人在这个行业摸爬滚打了多年,积攒了大量的经验,但是悲哀的发现,这些经验很难兑换成闪光的简历.

Scrapy爬虫笔记【1-基本框架】

- - CSDN博客研发管理推荐文章
Scrapy 是一款抓取网页并从中提取结构化数据的应用程序框架,它具有非常广泛的应用场景,如:数据挖掘、信息处理和历史信息归档等. 尽管 Scrapy 最初被设计用于 屏幕抓取(准确地说是 网页抓取),但您也可以仅使用它的 API 来提取数据(就像. Amazon Associates Web Services)或作为通用的网页爬虫来使用.

JAVA爬虫Nutch、WebCollector的正则约束

- - CSDN博客互联网推荐文章
爬虫爬取时,需要约束爬取的范围. 基本所有的爬虫都是通过正则表达式来完成这个约束. 代表"http://www.xinhuanet.com/"后加任意个任意字符(可以是0个). 通过这个正则可以约束爬虫的爬取范围,但是这个正则并不是表示爬取新华网所有的网页. 新华网并不是只有www.xinhuanet.com这一个域名,还有很多子域名,类似:news.xinhuanet.com.

Python写爬虫与网页解析

- - 互联网实践
Python写个简单爬虫,并作网页解析,还是非常高效的. urllib2是urllib得增强版,httplib更为底层,可以理解为urllib是对httplib的抽象. httplib是一个相对底层的http请求模块,其上有专门的包装模块,如urllib内建模块,goto等第三方模块,但是封装的越高就越不灵活,比如urllib模块里请求错误时就不会返回结果页的内容,只有头信息,对于某些需要检测错误请求返回值的场景就不适用,所以就得用这个模块了.