写在前面的话~
有段时间没有写爬虫相关的文章了,今天抽时间把之前做的一个程序分享给大家。
经常逛A站和B站的人,肯定对一个节目不陌生《网络上常见的GIF动态图》
今天就来分享一下,怎么通过爬虫自动的将这些个动作收藏到自己的电脑中(其实这个程序5月份就写好了,一直拖到现在才想起来将它分享出来)。
一.思路分析
'http://gifcc.com/forum-38-1.html', #美女GIF动态图出处
'http://gifcc.com/forum-47-1.html',#科幻奇幻电影GIF动态图出处
'http://gifcc.com/forum-48-1.html',#喜剧搞笑电影GIF动态图出处
'http://gifcc.com/forum-49-1.html',#动作冒险电影GIF动态图出处
'http://gifcc.com/forum-50-1.html'#恐怖惊悚电影GIF动态图出处
二 开工动手
#仅仅获取页面内容 def get_html_Pages(self,url): try: #browser = webdriver.PhantomJS(executable_path=r'C:\Python27\Scripts\phantomjs.exe') browser = webdriver.PhantomJS() browser.get(url) html = browser.execute_script("return document.documentElement.outerHTML") browser.close() html=HTMLParser.HTMLParser().unescape(html).decode('utf-8') return html #捕捉异常,防止程序直接死掉 except Exception,e: print u"连接失败,错误原因",e return None
#获取页码 def get_page_num(self,html): doc = pq(html) print u'开始获取总页码' #print doc('head')('title').text()#获取当前title try: #如果当前页面太多,超过8页以上,就使用另一种方式获取页码 if doc('div[class="pg"]')('[class="last"]'): num_content= doc('div[class="pg"]')('[class="last"]').attr('href') print num_content.split('-')[1].split('.')[0] return num_content.split('-')[1].split('.')[0] else: num_content= doc('div[class="pg"]')('span') return filter(str.isdigit,str(num_content.text()))[0] #如果获取页码失败,那么就返回1, 即值获取1页内容 except Exception,e: print u'获取页码失败'.e return '1'
这里的页码处理用到了一个模块pq, 即 PyQuery
from pyquery import PyQuery as pq
采用PyQuery的方式查找我们需要的元素,感觉更好处理一点,挺方便的
同时这里的处理稍微有点意思,如果观察这个页面的话,会发现,每个模块的页码数,在上面和下面都有一个,然后我这里裁取的一下,因为我们只需要一个页码数字即可
3-6 第三步到第六步一起来说
其实就是根据页码数,来进行遍历,获取到每一页的内容
然后得到每一页中的所有图片地址
print u'总共有 %d页内容' % int(page_num) #3.遍历每一页的内容 for num in range(1,int(page_num)): #4.组装新的url new_url = self.url.replace( self.url.split('-')[2],(str(num)+'.html') ) print u'即将获取的页面是:',new_url #5.加载每一页内容,获取gif list 的内容 items=self.parse_items_by_html(self.get_all_page(new_url)) print u'在第%d页,找到了%d 个图片内容' % (num,len(items)) #6.处理每一个元素的内容 self.get_items_url(items,num)在进行获取每一页的内容的时候,需要重新组装页面地址。
#4.组装新的url new_url = self.url.replace( self.url.split('-')[2],(str(num)+'.html') ) print u'即将获取的页面是:',new_url有了新的地址,就可以获取当前页面的内容,并进行数据处理,得到每一张图片的地址列表
#5.加载每一页内容,获取gif list 的内容 items=self.parse_items_by_html(self.get_all_page(new_url)) print u'在第%d页,找到了%d 个图片内容' % (num,len(items))
#解析页面内容,获取gif的图片list def parse_items_by_html(self, html): doc = pq(html) print u'开始查找内容msg' return doc('div[class="c cl"]')在获取到图片列表后,再次解析,获取每一张图片的URL
#解析gif 的list ,处理每一个gif内容 def get_items_url(self,items,num): i=1 for article in items.items(): print u'开始处理数据(%d/%d)' % (i, len(items)) #print article self.get_single_item(article,i,num) i +=1 #处理单个gif内容,获取其地址,gif 最终地址 def get_single_item(self,article,num,page_num): gif_dict={} #每个页面的地址 gif_url= 'http://gifcc.com/'+article('a').attr('href') #每个页面的标题 gif_title= article('a').attr('title') #每张图的具体地址 #html=self.get_html_Pages(gif_url) #gif_final_url=self.get_final_gif_url(html) gif_dict['num']=num gif_dict['page_num']=page_num gif_dict['gif_url']=gif_url gif_dict['gif_title']=gif_title self.gif_list.append(gif_dict) data=u'第'+str(page_num)+'页|\t'+str(num)+'|\t'+gif_title+'|\t'+gif_url+'\n' self.file_flag.write(data)在这里,把数据整合一下,为将数据写入数据库做准备
7.将图片存到本地,以及将数据写入数据库
#使用urllib2来获取图片最终地址 def get_final_gif_url_use_urllib2(self,url): try: html= urllib2.urlopen(url).read() gif_pattern=re.compile('<div align="center.*?<img id=.*?src="(.*?)" border.*?>',re.S) return re.search(gif_pattern,html).group(1) except Exception,e: print u'获取页面内容出错:',e #最终处理 存贮数据 def get_gif_url_and_save_gif(self): def save_gif(url,name): try: urllib.urlretrieve(url, name) except Exception,e: print '存贮失败,原因:',e for i in range(0,len(self.gif_list)): gif_dict=self.gif_list[i] gif_url=gif_dict['gif_url'] gif_title=gif_dict['gif_title'] #依然使用webdriver获取最终的gif地址 final_html=self.get_html_Pages(gif_url) gif_final_url=self.get_final_gif_url(final_html) #使用另外一种方式(urllib2)获取最终地址 #gif_final_url=self.get_final_gif_url_use_urllib2(gif_url) gif_dict['gif_final_url']=gif_final_url print u'开始向数据库写入第%d页第%d项数据,并开始存贮图片到本地 ' % (gif_dict['page_num'],gif_dict['num']) self.BookTable.insert_one(gif_dict) gif_name=self.dir_name+'/'+gif_title+'.gif' save_gif(gif_final_url,gif_name)到这里其实大体的内容已经完成了.
我们能够将这个论坛各个模块的动图都存到本地,同时呢,也将数据放入到了数据库中
三 数据库的筛选
在完成了将数据放入到数据库的之后, 我想着可以直接通过调用数据库,将图片保存
(为什么有这个想法呢,因为我发现如果直接在主程序中存贮图片,它跑的太慢了,不如将数据都放到数据库中,之后专门调用数据库来贮存图片)
但是这里发现一个问题,数据中的内容挺多的,然后发现了好多内容是重复的,因此我们需要对数据库进行去重
关于数据去重的内容,其实我之前的文章已经写过了(写那篇文章的时候,这个爬虫已经完成了呢~)
主要思路是针对某一个元素的数量进行操作,pymongo里面有一个方法是可以统计指定元素的数量的,如果当前元素只有一个,就不管,不是一个元素,就删除
核心代码如下:
for url in collection.distinct('name'):#使用distinct方法,获取每一个独特的元素列表 num= collection.count({"name":url})#统计每一个元素的数量 print num for i in range(1,num):#根据每一个元素的数量进行删除操作,当前元素只有一个就不再删除 print 'delete %s %d times '% (url,i) #注意后面的参数, 很奇怪,在mongo命令行下,它为1时,是删除一个元素,这里却是为0时删除一个 collection.remove({"name":url},0) for i in collection.find({"name":url}):#打印当前所有元素 print i print collection.distinct('name')#再次打印一遍所要去重的元素
四 读取数据库中的内容,存贮图片
数据去重之后,再次进行图片的存贮,就方便多了
之后如果图片删除了,也不用重新跑一边,或者说有时候本地图片占地方,那么只用保存有数据库的数据就好了
核心代码如下:
def save_gif(url,name): try: urllib.urlretrieve(url, name) except Exception,e: print u'存贮失败,原因:',e client = pymongo.MongoClient('localhost', 27017) print client.database_names() db = client.GifDB for table in db.collection_names(): print 'table name is ',table collection=db[table] for item in collection.find(): try: if item['gif_final_url']: url,url_title= item['gif_final_url'],item['gif_title'] gif_filename=table+'/'+url_title+'.gif' print 'start save %s, %s' % (url,gif_filename) save_gif(url,gif_filename) except Exception,e: print u'错误原因:',e
完整代码
#coding: utf-8 from pyquery import PyQuery as pq from selenium import webdriver import HTMLParser,urllib2,urllib,re,os import pymongo import time import sys reload(sys) sys.setdefaultencoding('utf-8') class download_gif: def __init__(self): self.url='http://gifcc.com/forum-38-1.html' self.url_list=['http://gifcc.com/forum-37-1.html',#其他各种GIF动态图出处 'http://gifcc.com/forum-38-1.html', #美女GIF动态图出处 'http://gifcc.com/forum-47-1.html',#科幻奇幻电影GIF动态图出处 'http://gifcc.com/forum-48-1.html',#喜剧搞笑电影GIF动态图出处 'http://gifcc.com/forum-49-1.html',#动作冒险电影GIF动态图出处 'http://gifcc.com/forum-50-1.html'#恐怖惊悚电影GIF动态图出处 ] self.choices={'1':u'其他各种GIF动态图出处', '2':u'美女GIF动态图出处', '3':u'科幻奇幻电影GIF动态图出处', '4':u'喜剧搞笑电影GIF动态图出处', '5':u'动作冒险电影GIF动态图出处', '6':u'恐怖惊悚电影GIF动态图出处' } self.dir_name=u'gif出处' self.gif_list=[] self.connection = pymongo.MongoClient() #BookTable.insert_one(dict_data)#插入单条数据 #BookTable.insert(dict_data)#插入 字典list 数据 #获取页面内容,并且加载JS, 通过滚动获取页面更多的元素 def get_all_page(self,url): try: #browser = webdriver.PhantomJS(executable_path=r'C:\Python27\Scripts\phantomjs.exe') browser = webdriver.PhantomJS() browser.get(url) #time.sleep(3) #页面滚动 js = "var q=document.body.scrollTop=100000" #for i in range(5): #调试语句,先暂时不加载太多次数 for i in range(30): #循环执行下滑页面50次 browser.execute_script(js) #加载一次,休息一下 time.sleep(1) print u'这是第 %d 次划动页面' % i # 执行js得到整个页面内容 html = browser.execute_script("return document.documentElement.outerHTML") browser.close() html=HTMLParser.HTMLParser().unescape(html) return html except Exception,e: print u'发生错误:',e #解析页面内容,获取gif的图片list def parse_items_by_html(self, html): doc = pq(html) print u'开始查找内容msg' return doc('div[class="c cl"]') #解析gif 的list ,处理每一个gif内容 def get_items_url(self,items,num): i=1 for article in items.items(): print u'开始处理数据(%d/%d)' % (i, len(items)) #print article self.get_single_item(article,i,num) i +=1 #处理单个gif内容,获取其地址,gif 最终地址 def get_single_item(self,article,num,page_num): gif_dict={} #每个页面的地址 gif_url= 'http://gifcc.com/'+article('a').attr('href') #每个页面的标题 gif_title= article('a').attr('title') #每张图的具体地址 #html=self.get_html_Pages(gif_url) #gif_final_url=self.get_final_gif_url(html) gif_dict['num']=num gif_dict['page_num']=page_num gif_dict['gif_url']=gif_url gif_dict['gif_title']=gif_title self.gif_list.append(gif_dict) data=u'第'+str(page_num)+'页|\t'+str(num)+'|\t'+gif_title+'|\t'+gif_url+'\n' self.file_flag.write(data) #通过webdriver获得页面内容后,获得最终地址 def get_final_gif_url(self,html): doc = pq(html) image_content= doc('td[class="t_f"]') gif_url= image_content('img').attr('src') return gif_url #使用urllib2来获取图片最终地址 def get_final_gif_url_use_urllib2(self,url): try: html= urllib2.urlopen(url).read() gif_pattern=re.compile('<div align="center.*?<img id=.*?src="(.*?)" border.*?>',re.S) return re.search(gif_pattern,html).group(1) except Exception,e: print u'获取页面内容出错:',e #最终处理 存贮数据 def get_gif_url_and_save_gif(self): def save_gif(url,name): try: urllib.urlretrieve(url, name) except Exception,e: print '存贮失败,原因:',e for i in range(0,len(self.gif_list)): gif_dict=self.gif_list[i] gif_url=gif_dict['gif_url'] gif_title=gif_dict['gif_title'] #依然使用webdriver获取最终的gif地址 final_html=self.get_html_Pages(gif_url) gif_final_url=self.get_final_gif_url(final_html) #使用另外一种方式(urllib2)获取最终地址 #gif_final_url=self.get_final_gif_url_use_urllib2(gif_url) gif_dict['gif_final_url']=gif_final_url print u'开始向数据库写入第%d页第%d项数据,并开始存贮图片到本地 ' % (gif_dict['page_num'],gif_dict['num']) self.BookTable.insert_one(gif_dict) gif_name=self.dir_name+'/'+gif_title+'.gif' save_gif(gif_final_url,gif_name) #仅仅获取页面内容 def get_html_Pages(self,url): try: #browser = webdriver.PhantomJS(executable_path=r'C:\Python27\Scripts\phantomjs.exe') browser = webdriver.PhantomJS() browser.get(url) html = browser.execute_script("return document.documentElement.outerHTML") browser.close() html=HTMLParser.HTMLParser().unescape(html).decode('utf-8') return html #捕捉异常,防止程序直接死掉 except Exception,e: print u"连接失败,错误原因",e return None #获取页码 def get_page_num(self,html): doc = pq(html) print u'开始获取总页码' #print doc('head')('title').text()#获取当前title try: #如果当前页面太多,超过8页以上,就使用另一种方式获取页码 if doc('div[class="pg"]')('[class="last"]'): num_content= doc('div[class="pg"]')('[class="last"]').attr('href') print num_content.split('-')[1].split('.')[0] return num_content.split('-')[1].split('.')[0] else: num_content= doc('div[class="pg"]')('span') return filter(str.isdigit,str(num_content.text()))[0] #如果获取页码失败,那么就返回1, 即值获取1页内容 except Exception,e: print u'获取页码失败'.e return '1' # filter(str.isdigit,num_content)#从字符串中提取数字 #创建文件夹 def mk_dir(self,path): if not os.path.exists(path): os.makedirs(path) def set_db(self,tablename): self.BookDB = self.connection.GifDB #数据库db的名字 self.BookTable =self.BookDB[tablename] #数据库table表的名字 #主函数 def run(self): choice_type=5 if choice_type: #for choice_type in range(len(self.choices)): if choice_type+1: self.dir_name=self.choices[str(choice_type+1)].strip() self.url=self.url_list[int(choice_type)] print self.dir_name,self.url #0.创建文件夹存贮图片,建立文件存贮内容 self.mk_dir(self.dir_name) self.filename=self.dir_name+'/'+self.dir_name+'.txt' print self.filename self.file_flag=open(self.filename,'w') self.set_db(self.dir_name) self.BookTable .insert({'filename':self.dir_name}) print self.url #1.获取入口页面内容 html=self.get_html_Pages(self.url) #2.获取页码数目 page_num=self.get_page_num(html) print u'总共有 %d页内容' % int(page_num) #3.遍历每一页的内容 #page_num=3#调试语句,先暂时将页面内容设置的小一点 for num in range(1,int(page_num)): #4.组装新的url new_url = self.url.replace( self.url.split('-')[2],(str(num)+'.html') ) print u'即将获取的页面是:',new_url #5.加载每一页内容,获取gif list 的内容 items=self.parse_items_by_html(self.get_all_page(new_url)) print u'在第%d页,找到了%d 个图片内容' % (num,len(items)) #6.处理每一个元素的内容 self.get_items_url(items,num) #5.数据全部抓取完毕,开始处理数据 self.get_gif_url_and_save_gif() print 'success' self.file_flag.close() if __name__ == '__main__': print u''' ************************************************** ** Welcome to Spider of GIF 出处图片 ** ** Created on 2017-05-21 ** ** @author: Jimy _Fengqi ** ************************************************** ''' print u''' 选择你要下载的gif图片类型 1:'其他各种GIF动态图出处' 2:'美女GIF动态图出处' 3:'科幻奇幻电影GIF动态图出处' 4:'喜剧搞笑电影GIF动态图出处' 5:'动作冒险电影GIF动态图出处' 6:'恐怖惊悚电影GIF动态图出处' ''' #选择要下载的类型 mydownload=download_gif() html=mydownload.run()02_delete_repeat_url_in_mongodb.py
#coding: utf-8 from pyquery import PyQuery as pq from selenium import webdriver import HTMLParser,urllib2,urllib,re,os import pymongo import time import sys reload(sys) sys.setdefaultencoding('utf-8') import pymongo def save_gif(url,name): try: urllib.urlretrieve(url, name) except Exception,e: print '存贮失败,原因:',e def print_database_and_table_name(): import pymongo client = pymongo.MongoClient('localhost', 27017) print client.database_names() for database in client.database_names(): for table in client[database].collection_names(): print 'table [%s] is in database [%s]' % (table,database) def delete_single_database_repeat_data(): import pymongo client = pymongo.MongoClient('localhost', 27017) db=client.GifDBtemptemp2#这里是将要清洗数据的数据库名字 for table in db.collection_names(): print 'table name is ',table collection=db[table] for url in collection.distinct('gif_title'):#使用distinct方法,获取每一个独特的元素列表 num= collection.count({"gif_title":url})#统计每一个元素的数量 print num for i in range(1,num):#根据每一个元素的数量进行删除操作,当前元素只有一个就不再删除 print 'delete %s %d times '% (url,i) #注意后面的参数, 很奇怪,在mongo命令行下,它为1时,是删除一个元素,这里却是为0时删除一个 collection.remove({"gif_title":url},0) for i in collection.find({"gif_title":url}):#打印当前所有元素 print i def delete_repeat_data(): import pymongo client = pymongo.MongoClient('localhost', 27017) db = client.local collection = db.person for url in collection.distinct('name'):#使用distinct方法,获取每一个独特的元素列表 num= collection.count({"name":url})#统计每一个元素的数量 print num for i in range(1,num):#根据每一个元素的数量进行删除操作,当前元素只有一个就不再删除 print 'delete %s %d times '% (url,i) #注意后面的参数, 很奇怪,在mongo命令行下,它为1时,是删除一个元素,这里却是为0时删除一个 collection.remove({"name":url},0) for i in collection.find({"name":url}):#打印当前所有元素 print i print collection.distinct('name')#再次打印一遍所要去重的元素 delete_single_database_repeat_data()
03_from_mongodb_save_pic.py
#coding: utf-8 from pyquery import PyQuery as pq from selenium import webdriver import HTMLParser,urllib2,urllib,re,os import pymongo import time import sys reload(sys) sys.setdefaultencoding('utf-8') import pymongo def save_gif(url,name): try: urllib.urlretrieve(url, name) except Exception,e: print u'存贮失败,原因:',e client = pymongo.MongoClient('localhost', 27017) print client.database_names() db = client.GifDB for table in db.collection_names(): print 'table name is ',table collection=db[table] for item in collection.find(): try: if item['gif_final_url']: url,url_title= item['gif_final_url'],item['gif_title'] gif_filename=table+'/'+url_title+'.gif' print 'start save %s, %s' % (url,gif_filename) save_gif(url,gif_filename) except Exception,e: print u'错误原因:',eGithub地址:https://github.com/JimyFengqi/Gif_Spider
如果感觉博主的内容有帮助,可以支持一下