今天早上起来,第一件事情就是理一理今天该作的事情,瞬间get到任务,写一个只用python字符串内建函数的爬虫,定义为v1.0,开发中的版本号定义为v0.x。数据存放?这个是一个练手的玩具,就写在txt文本里吧。其实主要的不是学习爬虫,而是依照这个需求锻炼下本身的编程能力,最重要的是要有一个清晰的思路(我在以这个目标努力着)。ok,主旨已经订好了,开始‘撸串’了。html
目标网站:http://bohaishibei.com/post/category/main/(一个颇有趣的网站,一段话配一个图,老有意思了~)网站形式以下:python
目标:把大的目标分为几个小的目标。由于第一次干这个,因此对本身能力很清楚,因此完成顺序由简单到复杂。git
1.爬取一期的内容,包括标题,和图片的urlgithub
2.把数据存在本地的txt文件中web
3.想爬多少就爬就爬少正则表达式
4.写一个网站,展现一下。(纯用于学习)sql
Let‘s 搞定它!数据库
时间——9:14编程
把昨天晚上作的事情交代一下。昨天晚上写的代码实现了爬取一期里的全部标题。api
第一步:
我用的是google浏览器,进入开发者模式,使用’页面内的元素选择器‘,先看一下内页中的结构,找到咱们要的数据所在’标签‘。
这里咱们须要的博海拾贝一期的内容所有在<article class="article-content">这个标签里面,以下图:
第一条红线是:页面内的元素选择器
第二条是:内容所在标签
第三条是:title
通过分析得出,我只要<article class="article-content">,这个标签的内容:因此写了下面的方法:
def content(html): # 内容分割的标签 str = '<article class="article-content">' content = html.partition(str)[2] str1 = '<div class="article-social">' content = content.partition(str1)[0] return content # 获得网页的内容
这里须要说一下:在写这个爬虫以前我就打算只用字符串的内置函数来处理匹配问题,因此我就上http://www.w3cschool.cc/python/进入到字符串页面,大体看了一遍字符串的内建函数有哪些。
partition() 方法用来根据指定的分隔符将字符串进行分割。
若是字符串包含指定的分隔符,则返回一个3元的元组,第一个为分隔符左边的子串,第二个为分隔符自己,第三个为分隔符右边的子串。
partition() 方法是在2.5版中新增的。参考:http://www.w3cschool.cc/python/att-string-partition.html
这样我就获得只有内容的字符串了,干净~
第二步:
获得title的内容。title的格式以下,我只要’【2】‘后面的文字,后面的img暂时不考虑一步步的来。
<p>【2】这是我最近的状态,请告诉我不是我一我的!</p><p><img src=http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8ifthnj30an0aot8w.jpg /></p><p>
我写了下面的方法:
def title(content,beg = 0): # 思路是利用str.index()和序列的切片 try: title_list = [] while True: num1 = content.index('】',beg) num2 = content.index('</p>',num1) title_list.append(content[num1:num2]) beg = num2 except ValueError: return title_list
这里用try....except是由于我不知道怎么跳出循环。。。。求大神有更好的方法告诉我。
我这里跳出循环用的是当抛出VlaueError异常就说明找不到了,那就返回列表。就跳出循环了。
num1是】的位置,num2是</p>的位置,而后用序列的切片,咔嚓咔嚓一下就是我想要的数据了。这里须要注意的是:切片’要头不要尾‘因此咱们的获得的数据就是这个样子的:
哎呀,这个是什么鬼!要头不要尾就是这个意思!
而后我就想:那就把num1加1不就完了吗?我真是太天真了。。。。
请+3,我以为原理是这样的,这个是个中文字符!(求大神指点)
第三步:
交代清楚我昨天晚上作的事情了,记录下时间——10:01,下面我要爬图片的url了。这里要说一下,若是要把图片下下来,最重要的一步就是获得url,而后下载下来保存到本地(用文本的IO)。
我先得到url,实现原理同获取title,我在想,既然同样卸载获取title的方法里好,仍是在写一个方法好。我单独写了一个方法,可是其实就是复制了一下title的方法,改了下匹配的字符串,代码以下:
def img(content,beg = 0): # 思路是利用str.index()和序列的切片 try: img_list = [] while True: src1 = content.index('http',beg) src2 = content.index('/></p>',src1) img_list.append(content[src1:src2]) beg = src2 except ValueError: return img_list
结果图以下:
这里发现,有的时候一个title会有不少个图片。我思考以后有以下思路:
1.须要写一个方法,当一个title出现多个图片的时候,捕获url。这个须要有一个判断语句,当url长度大于一个url长度的时候,才须要调用这个函数。
2.多个图片的url怎么放?使用符号隔开存放仍是嵌套放入一个数组里面?我这里打算用’|‘隔开,这样的话加一个判语句,或者先判断一下url长度,均可以进行。
这个问题先放在这里,由于当我要下载的时候这个url才须要过滤,因此先进行下一步,把数据存到本地txt文中,这里在解决这个问题也不晚。
第四步:
把数据存到本地的txt中。Python文件IO参考资料:http://www.w3cschool.cc/python/python-files-io.html
这里须要注意的是,文本写入的时候记得close,还有就是注意打开文本的模式。
时间——11:05 吃个饭先
时间——11:44 回来了
这里我考虑了一个问题,根据《编写高质量代码——改善python程序的91个建议》这本书中写道的,字符串链接时,用jion()效率高于’+‘
因此我写了以下代码:
def data_out(data): #这里写成一个方法好处是,在写入文本的时候就在这里写 fo = open("/home/qq/data.txt", "a+") #这里注意从新写一个地址 #for i,e in enumerate(data): fo.write("\n".join(data)); #print '第%d个,title:%s' % (i,e) # 关闭打开的文件 fo.close()
这样形成了一个问题,看图
形成最后一个和新的一个列表写入时在同一行。同时用with....as更好。修改后代码以下:
def data_out(data): #写入文本 with open("/home/qq/foo.txt", "a+") as fo: fo.write('\n') fo.write("\n".join(data));
下面研究title和img以什么样的格式存入txt文本:
title$img
这里我有一个概念混淆了,+和join()方法的效率问题主要在链接多个字符串的时候,我这个只用链接一次,不须要考虑这个问题。
def data_out(title, img): #写入文本 with open("/home/qq/foo.txt", "a+") as fo: fo.write('\n') size = 0 for size in range(0, len(title)): fo.write(title[size]+'$'+img[size]+'\n');
文本中的内容以下:
愿你贪吃不胖,愿你懒惰不丑,愿你深情不被辜负。$http://ww1.sinaimg.cn/mw690/005CfBldtw1etay8dl1bsj30c50cbq4m.jpg" 这是我最近的状态,请告诉我不是我一我的!$http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8ifthnj30an0aot8w.jpg 引诱别人和你击拳庆祝,而后偷偷把手势变成二,就能够合体成为蜗牛cosplay……$http://ww2.sinaimg.cn/mw690/005CfBldtw1etay8fzm1sg30b40644qq.gif 原来蜗牛是酱紫吃东西的。。。。涨姿式!$http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8egg8vg30bo08ax6p.gif
写入文本的最后,解决多个图片的问题:
def many_img(data,beg = 0): #用于匹配多图中的url try: many_img_str = '' while True: src1 = data.index('http',beg) src2 = data.index(' /><br /> <img src=',src1) many_img_str += data[src1:src2]+'|' # 多个图片的url用"|"隔开 beg = src2 except ValueError: return many_img_str def data_out(title, img): #写入文本 with open("/home/qq/data.txt", "a+") as fo: fo.write('\n') for size in range(0, len(title)): # 判断img[size]中存在的是否是一个url if len(img[size]) > 70: img[size] = many_img(img[size])# 调用many_img()方法 fo.write(title[size]+'$'+img[size]+'\n')
输出以下:
元气少女陈意涵 by @TopFashionStyle$http://ww2.sinaimg.cn/mw690/005CfBldtw1etay848iktj30bz0bcq4x.jpg|http://ww1.sinaimg.cn/mw690/005CfBldtw1etay83kv5pj30c10bkjsr.jpg|http://ww3.sinaimg.cn/mw690/005CfBldtw1etay82qdvsj30c10bkq3z.jpg|http://ww1.sinaimg.cn/mw690/005CfBldtw1etay836z8lj30c00biq40.jpg|http://ww4.sinaimg.cn/mw690/005CfBldtw1etay8279qmj30ac0a0q3p.jpg|http://ww1.sinaimg.cn/mw690/005CfBldtw1etay81ug5kj30c50bnta6.jpg|http://ww2.sinaimg.cn/mw690/005CfBldtw1etay8161ncj30c20bgmyt.jpg|http://ww2.sinaimg.cn/mw690/005CfBldtw1etay804oy7j30bs0bgt9r.jpg|
暂时功能是实现了,后面遇到问题须要修改在改吧。。。。新手走一步看一步!!!
到此为止,已经完成了前两个简单的计划:
1.爬取一期的内容,包括标题,和图片的url
2.把数据存在本地的txt文件中
所有代码以下:
#coding:utf-8 import urllib ###### #爬虫v0.1 利用urlib 和 字符串内建函数 ###### def getHtml(url): # 获取网页内容 page = urllib.urlopen(url) html = page.read() return html def content(html): # 内容分割的标签 str = '<article class="article-content">' content = html.partition(str)[2] str1 = '<div class="article-social">' content = content.partition(str1)[0] return content # 获得网页的内容 def title(content,beg = 0): # 匹配title # 思路是利用str.index()和序列的切片 try: title_list = [] while True: num1 = content.index('】',beg)+3 num2 = content.index('</p>',num1) title_list.append(content[num1:num2]) beg = num2 except ValueError: return title_list def get_img(content,beg = 0): # 匹配图片的url # 思路是利用str.index()和序列的切片 try: img_list = [] while True: src1 = content.index('http',beg) src2 = content.index('/></p>',src1) img_list.append(content[src1:src2]) beg = src2 except ValueError: return img_list def many_img(data,beg = 0): #用于匹配多图中的url try: many_img_str = '' while True: src1 = data.index('http',beg) src2 = data.index(' /><br /> <img src=',src1) many_img_str += data[src1:src2]+'|' # 多个图片的url用"|"隔开 beg = src2 except ValueError: return many_img_str def data_out(title, img): #写入文本 with open("/home/qq/data.txt", "a+") as fo: fo.write('\n') for size in range(0, len(title)): # 判断img[size]中存在的是否是一个url if len(img[size]) > 70: img[size] = many_img(img[size])# 调用many_img()方法 fo.write(title[size]+'$'+img[size]+'\n') content = content(getHtml("http://bohaishibei.com/post/10475/")) title = title(content) img = get_img(content) data_out(title, img) # 实现了爬的单个页面的title和img的url并存入文本
时间——15:14
下面要从新分析网站,我已经能够得到一期的内容了,我如今要获得,其它期的url,这样就想爬多少就爬多少了。
目标网址:http://bohaishibei.com/post/category/main/
按照上面的方法进入开发者模式分析网站结构,找出目标数据所在的标签,撸它!
在首页中须要的数据所有都在<div class="content">标签里,分隔方法以下:
def main_content(html): # 首页内容分割的标签 str = '<div class="content">' content = html.partition(str)[2] str1 = '</div>' content = content.partition(str1)[0] return content # 获得网页的内容
我暂时须要的数据:每一期的名字和每一期的url。
通过个人分析:该网站的每期的url格式是这样的:"http://bohaishibei.com/post/10189/"只有数字是变化的。
后来我又发现,我想要的这两个数据都在<h2>这个标签下面,获取每期url的方法以下:
def page_url(content, beg = 0): try: url = [] while True: url1 = content.index('<h2><a href="',beg)+13 url2 = content.index('" ',url1) url.append(content[url1:url2]) beg = url2 except ValueError: return url
title的格式,
我思考了一下,我要title其实没什么太大的意思,用户有不可能说我要看那期,只须要输入看多少期就能够了,标题没有什么实际意义(不像内容中的title是帮助理解改张图笑点的)。因此我打算在这个版本中只实现,你输入想查看要多少期,就返回多少期!
那么下面就须要一个策略了:
http://bohaishibei.com/post/category/main/ 共20期
http://bohaishibei.com/post/category/main/page/2/ 共20期
......
经查看,每页都是20期
当你要查看的期数,超过20期的时候须要,增长page的数值,进入下一页进行获取
最后一页为这个:http://bohaishibei.com/post/category/main/page/48/
实现代码,这个我要想想怎么写,我是第一次写爬虫,不要嘲讽我啊!
时间——17:09
感受快实现了,还在写:
def get_order(num): page = num / 20 order = num % 20 # 超出一整页的条目 for i in range(1, page+1): # 需这里须要尾巴 url = 'http://bohaishibei.com/post/category/main/page/%d' % i print url if (i == page)&(order > 0): url = 'http://bohaishibei.com/post/category/main/page/%d' % (i+1) print url+",%d条" % order get_order(55)
运行结果:
http://bohaishibei.com/post/category/main/page/1 http://bohaishibei.com/post/category/main/page/2 http://bohaishibei.com/post/category/main/page/3,15条 2 ~~~~~~~~~~~~ 15
这里我考虑是这样的我须要重写 page_url,须要多加一个参数,以下:
# 新增一个参数order,默认为20 def page_url(content, order = 20, beg = 0): try: url = [] i = 0 while i < order: url1 = content.index('<h2><a href="',beg)+13 url2 = content.index('" ',url1) url.append(content[url1:url2]) beg = url2 i = i + 1 return url except ValueError: return url
下面这个方法是传入参数num(须要多少期),一页20期,返回每一期的url,代码以下:
def get_order(num): # num表明获取的条目数量 url_list = [] page = num / 20 order = num % 20 # 超出一整页的条目 if num < 20: # 若是获取的条目数量少于20(一页20个),直接爬取第一页的num条 url = 'http://bohaishibei.com/post/category/main' main_html = getHtml(url) clean_content = main_content(main_html) url_list = url_list + page_url(clean_content, num) for i in range(1, page+1): # 需这里须要尾巴 url = 'http://bohaishibei.com/post/category/main/page/%d' % i # 爬取整页的条目 main_html = getHtml(url) clean_content = main_content(main_html) url_list = url_list + page_url(clean_content) #获取整夜 if (i == page)&(order > 0): # 爬到最后一页,若是有超出一页的条目则继续怕order条 url = 'http://bohaishibei.com/post/category/main/page/%d' % (i+1) main_html = getHtml(url) clean_content = main_content(main_html) url_list = url_list + page_url(clean_content, order) #print len(page_url(clean_content, order)) return url_list
下面开始gogogo
order = get_order(21) for i in range(0, len(order)): #这个遍历列表太丑了,改了: for i in order html = getHtml(order[i]) content_data = content(html) title_data = title(content_data) img_data = get_img(content_data) data_out(title_data, img_data)
ok了全部的代码都写完了
完整的代码我已经上传到个人github上了,地址为:https://github.com/521xueweihan/PySpider/blob/master/Spider.py
这里我在测试的时候有bug,由于该网站上有时候有的地方没有img的地址。以下图
个人代码也就跟着出问题了,由于个人title和img列表数量不一了,而列表长度我是以title的len()为准,结果就出现超出范围了。
这里记录一下,而后我要去除bug了。
ok啦,bug消除了。我改了img的匹配方法以下:
def get_img(content,beg = 0): # 匹配图片的url # 思路是利用str.index()和序列的切片 try: img_list = [] while True: src1 = content.index('src=',beg)+4 # 这样写就能够匹配src="/" src2 = content.index('/></p>',src1) img_list.append(content[src1:src2]) beg = src2 except ValueError: return img_list
主函数:
order = get_order(30) # get_order方法接受参数,抓取多少期的数据 for i in order: # 遍历列表的方法 html = getHtml(i) content_data = content(html) title_data = title(content_data) img_data = get_img(content_data) data_out(title_data, img_data)
爬下来的数据:
data.txt属性(共30期的数据):
终于写完了!
开始时间——9:14
写爬虫,吃饭,洗澡,休息了一会。
结束时间——21:02
呼,没有半途而废就满足了,感受这样把写爬虫的流程走了一遍下次再写的话会快一些吧。
爬虫是写完了,可是用网站显示尚未写,明天看若是没事就把网站写出来。
图片下载的功能,我尚未写,等写网站的时候再把它完善出来。
总结:
整个过程,纯手写,没有参考别人的代码。这一点能够赞一下。
此次写爬虫就是强制本身不用正则表达式,和XPATH,发现有不少地方,用这两个会很方便。这让我下定决心去学正则表达式和Xpath,哈哈。体会过才有深有感触。
下一个目标是学习正则表达式和Xpath。一点点来,当我学完就来写爬虫v2.0,逐步完善吧,若是上来就要写可贵,个人智商着急啊!
而后多看看别人的爬虫,学习别人厉害的地方,提升本身。
欢迎你们指导交流。
完整的代码我已经上传到个人github上了,地址为:https://github.com/521xueweihan/PySpider/blob/master/Spider.py
http://www.cnblogs.com/xueweihan/p/4592212.html
python编写知乎爬虫实践
网络爬虫的基本工做流程以下:
在爬虫系统中,待抓取URL队列是很重要的一部分。待抓取URL队列中的URL以什么样的顺序排列也是一个很重要的问题,由于这涉及到先抓取那个页面,后抓取哪一个页面。而决定这些URL排列顺序的方法,叫作抓取策略。下面重点介绍几种常见的抓取策略:
了解了爬虫的工做流程和爬取策略后,就能够动手实现一个爬虫了!那么在python里怎么实现呢?
下面是一个伪代码
import Queue initial_page = "https://www.zhihu.com/people/gaoming623" url_queue = Queue.Queue() seen = set() seen.insert(initial_page) url_queue.put(initial_page) while(True): #一直进行 if url_queue.size()>0: current_url = url_queue.get() #拿出队例中第一个的url store(current_url) #把这个url表明的网页存储好 for next_url in extract_urls(current_url): #提取把这个url里链向的url if next_url not in seen: seen.put(next_url) url_queue.put(next_url) else: break
若是你直接加工一下上面的代码直接运行的话,你须要很长的时间才能爬下整个知乎用户的信息,毕竟知乎有6000万月活跃用户。更别说Google这样的搜索引擎须要爬下全网的内容了。那么问题出如今哪里?
须要爬的网页实在太多太多了,而上面的代码太慢太慢了。设想全网有N个网站,那么分析一下判重的复杂度就是N*log(N),由于全部网页要遍历一次,而每次判重用set的话须要log(N)的复杂度。OK,我知道python的set实现是hash——不过这样仍是太慢了,至少内存使用效率不高。
一般的判重作法是怎样呢?Bloom Filter. 简单讲它仍然是一种hash的方法,可是它的特色是,它可使用固定的内存(不随url的数量而增加)以O(1)的效率断定url是否已经在set中。惋惜天下没有白吃的午饭,它的惟一问题在于,若是这个url不在set中,BF能够100%肯定这个url没有看过。可是若是这个url在set中,它会告诉你:这个url应该已经出现过,不过我有2%的不肯定性。注意这里的不肯定性在你分配的内存足够大的时候,能够变得很小不多。
# bloom_filter.py BIT_SIZE = 5000000 class BloomFilter: def __init__(self): # Initialize bloom filter, set size and all bits to 0 bit_array = bitarray(BIT_SIZE) bit_array.setall(0) self.bit_array = bit_array def add(self, url): # Add a url, and set points in bitarray to 1 (Points count is equal to hash funcs count.) # Here use 7 hash functions. point_list = self.get_postions(url) for b in point_list: self.bit_array[b] = 1 def contains(self, url): # Check if a url is in a collection point_list = self.get_postions(url) result = True for b in point_list: result = result and self.bit_array[b] return result def get_postions(self, url): # Get points positions in bit vector. point1 = mmh3.hash(url, 41) % BIT_SIZE point2 = mmh3.hash(url, 42) % BIT_SIZE point3 = mmh3.hash(url, 43) % BIT_SIZE point4 = mmh3.hash(url, 44) % BIT_SIZE point5 = mmh3.hash(url, 45) % BIT_SIZE point6 = mmh3.hash(url, 46) % BIT_SIZE point7 = mmh3.hash(url, 47) % BIT_SIZE return [point1, point2, point3, point4, point5, point6, point7]
BF详细的原理参考我以前写的文章:布隆过滤器(Bloom Filter)的原理和实现
用户有价值的信息包括用户名、简介、行业、院校、专业及在平台上活动的数据好比回答数、文章数、提问数、粉丝数等等。
用户信息存储的表结构以下:
CREATE DATABASE `zhihu_user` /*!40100 DEFAULT CHARACTER SET utf8 */; -- User base information table CREATE TABLE `t_user` ( `uid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '用户名', `brief_info` varchar(400) COMMENT '我的简介', `industry` varchar(50) COMMENT '所处行业', `education` varchar(50) COMMENT '毕业院校', `major` varchar(50) COMMENT '主修专业', `answer_count` int(10) unsigned DEFAULT 0 COMMENT '回答数', `article_count` int(10) unsigned DEFAULT 0 COMMENT '文章数', `ask_question_count` int(10) unsigned DEFAULT 0 COMMENT '提问数', `collection_count` int(10) unsigned DEFAULT 0 COMMENT '收藏数', `follower_count` int(10) unsigned DEFAULT 0 COMMENT '被关注数', `followed_count` int(10) unsigned DEFAULT 0 COMMENT '关注数', `follow_live_count` int(10) unsigned DEFAULT 0 COMMENT '关注直播数', `follow_topic_count` int(10) unsigned DEFAULT 0 COMMENT '关注话题数', `follow_column_count` int(10) unsigned DEFAULT 0 COMMENT '关注专栏数', `follow_question_count` int(10) unsigned DEFAULT 0 COMMENT '关注问题数', `follow_collection_count` int(10) unsigned DEFAULT 0 COMMENT '关注收藏夹数', `gmt_create` datetime NOT NULL COMMENT '建立时间', `gmt_modify` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后一次编辑', PRIMARY KEY (`uid`) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户基本信息表';
网页下载后经过XPath进行解析,提取用户各个维度的数据,最后保存到数据库中。
通常网站会从几个维度来反爬虫:用户请求的Headers,用户行为,网站和数据加载的方式。从用户请求的Headers反爬虫是最多见的策略,不少网站都会对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; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.3", "Referer": "https://www.zhihu.com/" } r = requests.get(url, cookies = cookies, headers = headers)
还有一部分网站是经过检测用户行为,例如同一IP短期内屡次访问同一页面,或者同一帐户短期内屡次进行相同操做。
大多数网站都是前一种状况,对于这种状况,使用IP代理就能够解决。这样的代理ip爬虫常常会用到,最好本身准备一个。有了大量代理ip后能够每请求几回更换一个ip,这在requests或者urllib2中很容易作到,这样就能很容易的绕过第一种反爬虫。目前知乎已经对爬虫作了限制,若是是单个IP的话,一段时间系统便会提示异常流量,没法继续爬取了。所以代理IP池很是关键。网上有个免费的代理IP API: http://api.xicidaili.com/free2016.txt
import requests import random class Proxy: def __init__(self): self.cache_ip_list = [] # Get random ip from free proxy api url. def get_random_ip(self): if not len(self.cache_ip_list): api_url = 'http://api.xicidaili.com/free2016.txt' try: r = requests.get(api_url) ip_list = r.text.split('\r\n') self.cache_ip_list = ip_list except Exception as e: # Return null list when caught exception. # In this case, crawler will not use proxy ip. print e return {} proxy_ip = random.choice(self.cache_ip_list) proxies = {'http': 'http://' + proxy_ip} return proxies
爬虫源代码:zhihu-crawler 下载以后经过pip安装相关三方包后,运行$ python crawler.py便可(喜欢的帮忙点个star哈,同时也方便看到后续功能的更新)
运行截图: