最近因维护微信公众号需要,想用Python自动获取知乎上获赞过千的答案。
于是想到了爬虫,当然一开始做得很简单,仅仅是单线程的爬取。后来发现速度实在太慢,就开发了多线程功能。
关键的地方都加了注释,思想也不复杂,所以直接上代码:
#coding=utf-8
import urllib2,re,os,threading#使用正则匹配出所需部分
def spider(url):
try:
#user_agent = {'User-agent': 'spider'}
r = urllib2.Request(url)
data=urllib2.urlopen(r).read()
title_reg = re.compile(r'<title>\s*(.*?)\s*</title>')
title=re.findall(title_reg,data)[0]
title=re.findall(r'(.*?) -.* -.*',title)[0].decode('utf-8')#加了decode之后解决中文文件夹名问题
if not os.path.exists(title):
vote=map(int,re.findall('data-votecount="(.*?)"',data))#点赞量
link=re.findall('target="_blank" href="(.*?)"',data)#该回答的链接
if max(vote)<1000:
return data
f=open(title+'.txt','w+')
for i in range(len(vote)):
if vote[i]>1000:#若赞数超过一千,则将该答案的链接保存下来
f.write('www.zhihu.com'+link[i]+'\n'+str(vote[i])+'赞'+'\n\n')
f.close()
except:
return
def CrawlTopic(topicURL):
basicURL='http://www.zhihu.com'
topicURL+='/questions?page='
filename=topicURL[27:34]+'.txt'#file用来存取每次爬取结束后的页数,因为爬取时间较长,很难一次爬完
if not os.path.exists(filename):
f_page=open(filename,'w+')
f_page.write('1')
f_page.close()
page=1
else:
f_page=open(filename,'r+')
page=int(f_page.read())
f_page.close()
while 1:
r1 = urllib2.Request(topicURL+str(page))
try:
res=urllib2.urlopen(r1).read()
except:
page+=1
f_page=open(filename,'w+')
f_page.write(str(page))
f_page.close()
continue
if not res:
f_page=open(filename,'w+')
f_page.write(str(page))
f_page.close()
continue
questions=re.findall('<a target="_blank" class="question_link" href="(.*?)">',res)
print page
for q in questions:
spider(basicURL+q)
page+=1
f_page=open(filename,'w+')
f_page.write(str(page))
f_page.close()
threads = []
topics=[#需要爬取的网页
'http://www.zhihu.com/topic/19551147',
'http://www.zhihu.com/topic/19569848',
'http://www.zhihu.com/topic/19691659',
'http://www.zhihu.com/topic/19556423',
'http://www.zhihu.com/topic/19550564',
'http://www.zhihu.com/topic/19566266',
'http://www.zhihu.com/topic/19556758',
'http://www.zhihu.com/topic/19694211'
]
for item in topics:
t = threading.Thread(target=CrawlTopic,args=(item,))
threads.append(t)#设置多线程对象
for t in threads:
t.setDaemon(True)
#将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。
t.start()
#开始线程活动
t.join()#在子线程完成运行之前,这个子线程的父线程将一直被阻塞。否则父线程一结束就将关闭子线程
爬完之后的结果是这样的
每个txt文件内容如下
这样就避开了很多无效信息,以后阅读知乎的效率也就高了不少。