网络爬虫的基本工作流程如下:首先选择一部分精心挑选的种子URL,将种子URL加入到任务队列中,从待爬URL队列中取出待爬URL,进行DNS解析,得到主机的ip,并下载该URL对应的网页,存入下载的网页库中。另外,将这些网址放入已抓取的网址队列中。分析抓取到的URL队列中的URL,分析其中的其他URL,将URL放入待抓取的URL队列中,从而进入下一个循环。解析下载的网页,解析出需要的数据。如果数据是持久化的,将其保存到数据库中。爬虫抓取策略在爬虫系统中,待抓取的URL队列是一个非常重要的部分。URL队列中待抓取的URL的排列顺序也是一个很重要的问题,因为这涉及到先抓取哪个页面,后抓取哪个页面。确定这些URL顺序的方法称为抓取策略。下面重点介绍几种常见的爬虫策略:深度优先策略(DFS)深度优先策略是指爬虫从某个URL开始,逐个链接爬取,直到处理完一个链接所在的所有行。切换到另一条线。此时的爬取顺序为:A->B->C->D->E->F->G->H->I->J广度优先策略(BFS)的基本思想广度优先遍历策略是,将在新下载的网页中找到的链接直接插入到要抓取的URL队列的末尾。也就是说,网络爬虫会先爬取初始网页中链接的所有网页,然后选择其中一个链接的网页,继续爬取该网页中链接的所有网页。此时的爬取顺序为:A->B->E->G->H->I->C->F->J->D了解了爬虫的工作流程和爬取策略后,就可以开始实现了爬虫!那么如何在python中实现呢?技术栈请求人性化请求发送BloomFilter布隆过滤器,用于判断权重XPath解析HTML内容murmurhash反爬虫策略反爬虫策略MySQL用户数据存储基本实现如下是一个伪代码importQueueinitial_page="https://www.zhihu.com/people/gaoming623"url_queue=Queue.Queue()seen=set()seen.insert(initial_page)url_queue.put(initial_page)while(True):#继续执行ifurl_queue.size()>0:current_url=url_queue.get()#取出组例中第一个urlstore(current_url)#存储这个url代表的网页为next_urlinextract_urls(current_url):#提取urlifnext_urlnotinseen:seen.put(next_url)url_queue.put(next_url)else:breaklinkedtointhisurl将需要很长时间才能爬下整个用户信息,毕竟知乎有6000万月活跃用户。更不用说像谷歌这样的搜索引擎需要爬下整个网络的内容。那么问题出在哪里呢?布隆过滤器需要抓取的网页太多,上面的代码太慢了。假设全网有N个网站,那么分析判断权重的复杂度为N*log(N),因为所有的网页都需要遍历一次,需要log(N)的复杂度,reusingsetevery时间。OK,我知道python的set实现是hash——但这还是太慢了,至少内存使用效率不高。通常的重量判断方法是什么?布隆过滤器。简单来说,它仍然是一种哈希方法,但它的特点是可以使用固定的内存(不随着url的数量增长)以O(1)的效率判断url是否已经在集合中。可惜天下没有免费的午餐。唯一的问题是,如果url不在集合中,BF可以100%确定没有看到该url。但是如果这个url在集合中,它会告诉你:这个url应该出现过,但是我有2%的不确定性。请注意,当您分配的内存足够大时,这里的不确定性会变得非常小。#bloom_filter.pyBIT_SIZE=5000000classBloomFilter:def__init__(self):#Initializebloomfilter,setsizeandallbitsto0bit_array=bitarray(BIT_SIZE)bit_array.setall(0)self.bit_array=bit_arraydefadd(self,url):#Addaurl,andsetpointsinbitarrayto1(Pointscountisequaltohashpoint_list.Healtohashfuncs7.setall(0))=self.get_postions(url)forbinpoint_list:self.bit_array[b]=1defcontains(self,url):#Checkifaurlisinacollectionpoint_list=self.get_postions(url)result=Trueforbinpoint_list:result=resultandself.bit_array[b]returnresultdefget_postions(self,url):#Getpointspositionsinbitvector.point1=mmh3.hash(url,41)%BIT_SIZEpoint2=mmh3.hash(url,42)%BIT_SIZEpoint3=mmh3.hash(url,43)%BIT_SIZEpoint4=mmh3.hash(url,44)%BIT_SIZEpoint5=mmh3.hash(url,45)%BIT_SIZEpoint6=mmh3.hash(url,46)%BIT_SIZEpoint7=mmh3.hash(url,47)%BIT_SIZEreturn[point1,point2,point3,point4,point5,point6,point7]BF详细的原理参考我之前写的文章:布隆过滤器(BloomFilter)的原理和tabl的实现e楼。用户的有价值信息包括用户名、简介、行业、院校、专业、平台活动数据,如回答数、文章数、问题数、粉丝数等。用户信息存储表结构如下:CREATEDATABASE`zhihu_user`/*!40100DEFAULTCHARACTERSETutf8*/;--UserbaseinformationtableCREATETABLE`t_user`(`uid`bigint(20)unsignedNOTNULLAUTO_INCREMENT,`username`varchar(50)NOTNULLCOMMENT'用户名',`brief_info`varchar(400)COMMENT'个人简介',`industry`varchar(50)COMMENT'行业',`education`varchar(50)COMMENT'研究生院',`major`varchar(50)COMMENT'专业',`answer_count`int(10)unsignedDEFAULT0COMMENT'答案数',`article_count`int(10)unsignedDEFAULT0COMMENT'文章数',`ask_question_count`int(10)unsignedDEFAULT0COMMENT'问题数',`collection_count`int(10)unsignedDEFAULT0COMMENT'问题数',`collection_count`int(10)unsignedDEFAULT0COMMENT''收藏夹',`follower_count`int(10)unsignedDEFAULT0COMMENT'关注数',`followed_count`int(10)unsignedDEFAULT0COMMENT'关注数',`follow_live_count`int(10)unsignedDEFAULT0COMMENT'followedlivecount',`follow_topic_count`int(10)unsignedDEFAULT0COMMENT'后续主题',`follow_column_count`int(10)unsignedDEFAULT0COMMENT'后续列',`follow_question_count`int(10)unsignedDEFAULT0COMMENT'后续问题',`follow_collection_count`int(10)unsignedDEFAULT0COMMENT'后续收藏数量',`gmt_create`datetimeNOTNULLCOMMENT'创建时间',`gmt_modify`timestampNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'***编辑',PRIMARYKEY(`uid`))ENGINE=MyISAMAUTO_INCREMENT=1DEFAULTCHARSET=utf8COMMENT='基本用户信息表';反爬虫策略Response-Headers一般网站会从用户请求的header、用户行为、网站、数据加载方式等几个维度进行反爬虫分析、提取数据,最后存入数据库。从用户请求的头部反爬是最常见的策略。很多网站会检测headers的User-Agent,有的网站会检测Referer(有些资源网站的防盗就是检测Referer)。如果遇到这种反爬虫机制,可以直接给爬虫添加Headers,将浏览器的User-Agent复制到爬虫的Headers中;或者修改Referer值为目标网站的域名。对于检测Headers的反爬虫,在爬虫中修改或添加Headers可以很好的绕过它。cookies={"d_c0":"AECA7v-aPwqPTiIbemmIQ8abhJy7bdD2VgE=|1468847182","login":"NzM5ZDc2M2JkYzYwNDZlOGJlYWQ1YmI4OTg5NDhmMTY=|1480901173|9c296f424b32f241d1471203244eaf30729420f0","n_c":"1","q_c1":"395b12e529e541cbb400e9718395e346|1479808003000|1468847182000","l_cap_id":"NzI0MTQwZGY2NjQyNDQ1NThmYTY0MjJhYmU2NmExMGY=|1480901160|2e7a7faee3b3e8d0afb550e8e7b38d86c15a31bc","d_c0":"AECA7v-aPwqPTiIbemmIQ8abhJy7bdD2VgE=|1468847182","cap_id":"N2U1NmQwODQ1NjFiNGI2Yzg2YTE2NzJkOTU5N2E0NjI=|1480901160|fd59e2ed79faacc2be1010687d27dd559ec1552a"}headers={"User-Agent":"Mozilla/5.0(Macintosh;IntelMacOSX10_12_1)AppleWebKit/537.36(KHTML,likeGecko)Chrome/54.0.2840.98Safari/537.3","Referer":"https://www.zhihu.com/"}r=requests.get(url,cookies=cookies,headers=headers)反爬虫策略响应-代理IP池和部分网站检测用户行为,比如同IP在短时间内多次访问同一个页面,或者同一个账号在短时间内多次执行同一个操作。大多数网站都是前一种情况。对于这种情况,使用IP代理可以解决。这样的代理ip爬虫经常用到,最好自己准备一个。大量的代理ip,每隔几次就换一个ip,在requests或者urllib2中很容易做到,这样就可以轻松绕过第一道反爬虫。目前知乎已经对爬虫进行了限制。如果是单个IP??,一段时间后系统会提示流量异常,无法继续爬取。因此,代理IP池非常关键。网上有免费的代理IPAPI:http://api.xicidadaili.com/free2016.txtimportrequestsimportrandomclassProxy:def__init__(self):self.cache_ip_list=[]#Getrandomipfromfreeproxyapiurl.defget_random_ip(self):ifnotlen(self.cache_ip_list):api_url='http://api.xicidadaili.com/free2016.txt'try:r=requests.get(api_url)ip_list=r.text.split('\r\n')self.cache_ip_list=ip_listexceptionase:#捕获异常时返回空列表。#在这种情况下,crawlerwillnotuseproxyip.printereturn{}proxy_ip=random.choice(self.cache_ip_list)proxies={'http':'http://'+proxy_ip}returnproxies后续使用log模块记录爬取日志和错误日志分布式任务queue和分布式爬虫爬虫源码:zhihu-crawler通过pip下载安装相关的三方包后,运行$pythoncrawler.py(喜欢的话帮我点个star,也方便看更新后续功能)并运行截图:
