Django+python+BeautifulSoup垂直搜索爬蟲

 使用python+BeautifulSoup完成爬蟲抓取特定數據的工作,並使用Django搭建一個管理平臺,用來協調抓取工作。

因爲自己很喜歡Django admin後臺,所以這次用這個後臺對抓取到的鏈接進行管理,使我的爬蟲可以應對各種後期的需求。比如分時段抓取,定期的對已經抓取的地址重新抓取。數據庫是用python自帶的sqlite3,所以很方便。

 

這幾天正好在做一個電影推薦系統,需要些電影數據。本文的例子是對豆瓣電影抓取特定的數據。

 

第一步:建立Django模型


模仿nutch的爬蟲思路,這裏簡化了。每次抓取任務開始先從數據庫裏找到未保存的(is_save = False)的鏈接,放到抓取鏈表裏。你也可以根據自己的需求去過濾鏈接。

 

python代碼:

 

  1. view plain 
  2. class Crawl_URL(models.Model):   
  3.     url = models.URLField('抓取地址',max_length=100unique=True)   
  4.     weight = models.SmallIntegerField('抓取深度',default = 0)#抓取深度起始1   
  5.     is_save = models.BooleanField('是否已保存',defaultFalse)#   
  6.     date = models.DateTimeField('保存時間',auto_now_add=True,blank=True,null=True)   
  7.     def __unicode__(self):   
  8.         return self.url   

然後生成相應的表。

 

還需要一個admin管理後臺


 
  1. view plain 
  2. class Crawl_URLAdmin(admin.ModelAdmin):   
  3.     list_display = ('url','weight','is_save','date',)   
  4.     ordering = ('-id',)   
  5.     list_filter = ('is_save','weight','date',)   
  6.     fields = ('url','weight','is_save',)   
  7. admin.site.register(Crawl_URL, Crawl_URLAdmin)   

 

 

第二步,編寫爬蟲代碼

 

爬蟲是單線程,並且每次抓取後都有相應的暫定,豆瓣網會禁止一定強度抓取的爬蟲

爬蟲根據深度來控制,每次都是先生成鏈接,然後抓取,並解析出更多的鏈接,最後將抓取過的鏈接is_save=true,並把新鏈接存入數據庫中。每次一個深度抓取完後都需要花比較長的時候把鏈接導入數據庫。因爲需要判斷鏈接是否已存入數據庫。

 

這個只對滿足正則表達式 http://movie.douban.com/subject/(/d+)/ 的地址進行數據解析。並且直接忽略掉不是電影模塊的鏈接。

第一次抓取需要在後臺加個鏈接,比如http://movie.douban.com/chart,這是個排行榜的頁面,電影比較受歡迎。

 

python代碼:

#這段代碼不能格式化發

# coding=UTF-8

import urllib2

from BeautifulSoup import *

from urlparse import urljoin

from pysqlite2 import dbapi2 as sqlite

from movie.models import *

from django.contrib.auth.models import User

from time import sleep

 

p_w_picpath_path = 'C:/Users/soul/djcodetest/picture/'

 

user = User.objects.get(id=1)

def crawl(depth=10):

    for i in range(1,depth):

        print '開始抓取 for %d....'%i

        pages = Crawl_URL.objects.filter(is_save=False)

        newurls={}      

        for crawl_page in pages:

            page = crawl_page.url

            try:

                c=urllib2.urlopen(page)

            except:

                continue     

            try:

                #解析元數據和url

                soup=BeautifulSoup(c.read())

                #解析電影頁面

                if re.search(r'^http://movie.douban.com/subject/(/d+)/$',page):

                    read_html(soup)

                #解析出有效的鏈接,放入newurls

                links=soup('a')

                for link in links:      

                    if 'href' in dict(link.attrs):      

                        url=urljoin(page,link['href'])             

                    if url.find("'")!=-1: continue

                    if len(url) > 60: continue

                    url=url.split('#')[0]  # removie location portion

                    if re.search(r'^http://movie.douban.com', url):

                        newurls[url]= crawl_page.weight + 1 #連接有效。存入字典中

                        try:

                            print 'add url :'

                        except:

                            pass        

            except Exception.args:

                try:

                    print "Could not parse : %s" % args

                except:

                    pass

            #newurls存入數據庫 is_save=False weight=i

            crawl_page.is_save = True

            crawl_page.save()

            #休眠2.5秒

            sleep(2.5)

        save_url(newurls)          

#保存url,放到數據庫裏

def save_url(newurls):

    for (url,weight) in newurls.items():

        url = Crawl_URL(url=url,weight=weight)

        try:

            url.save()

        except:

            try:

                print 'url重複:'

            except:

                pass

    return True

 


 

第三步,用BeautifulSoup解析頁面

抽取出電影標題,圖片,劇情介紹,主演,標籤,地區。關於BeautifulSoup的使用可以看這裏BeautifulSoup技術文檔

 

  1. view plain 
  2. #抓取數據   
  3. def read_html(soup):   
  4.     #解析出標題   
  5.     html_title = soup.html.head.title.string   
  6.     title = html_title[:len(html_title)-5]   
  7.     #解析出電影介紹   
  8.     try:   
  9.         intro = soup.find('span',attrs={'class':'all hidden'}).text   
  10.     except:   
  11.         try:   
  12.             node = soup.find('div',attrs={'class':'blank20'}).previousSibling   
  13.             intro = node.contents[0]+node.contents[2]   
  14.         except:   
  15.             try:   
  16.                 contents = soup.find('div',attrs={'class':'blank20'}).previousSibling.previousSibling.text   
  17.                 intro = contents[:len(contents)-22]   
  18.             except:   
  19.                 intro = u'暫無'   
  20.        
  21.     #取得圖片   
  22.     html_p_w_picpath = soup('a',href=re.compile('douban.com/lpic'))[0]['href']   
  23.     data = urllib2.urlopen(html_p_w_picpath).read()   
  24.     p_w_picpath = '201003/'+html_p_w_picpath[html_p_w_picpath.rfind('/')+1:]   
  25.     f = file(p_w_picpath_path+p_w_picpath,'wb')   
  26.     f.write(data)   
  27.     f.close()   
  28.        
  29.            
  30.     #解析出地區   
  31.     try:   
  32.         soupsoup_obmo = soup.find('div',attrs={'class':'obmo'}).findAll('span')   
  33.         html_area = soup_obmo[0].nextSibling.split('/')   
  34.         area = html_area[0].lstrip()   
  35.     except:   
  36.         area = ''   
  37.        
  38.     #time = soup_obmo[1].nextSibling.split(' ')[1]   
  39.     #timetime = time.strptime(html_time,'%Y-%m-%d')   
  40.        
  41.     #生成電影對象   
  42.     new_movie = Movie(titletitle=title,introintro=intro,areaarea=area,version='暫無',upload_user=user,p_w_picpathp_w_picpath=p_w_picpath)   
  43.     new_movie.save()   
  44.     try:   
  45.         actors = soup.find('div',attrs={'id':'info'}).findAll('span')[5].nextSibling.nextSibling.string.split(' ')[0]   
  46.         actors_list = Actor.objects.filter(name = actors)   
  47.         if len(actors_list) == 1:   
  48.             actor = actors_list[0]   
  49.             new_movie.actors.add(actor)   
  50.         else:   
  51.             actor = Actor(name=actors)   
  52.             actor.save()       
  53.             new_movie.actors.add(actor)   
  54.     except:   
  55.         pass   
  56.        
  57.     #tag   
  58.     tags = soup.find('div',attrs={'class':'blank20'}).findAll('a')   
  59.     for tag_html in tags:   
  60.         tag_str = tag_html.string   
  61.         if len(tag_str) > 4:   
  62.             continue   
  63.         tag_list = Tag.objects.filter(name = tag_str)   
  64.         if len(tag_list) == 1:   
  65.             tag = tag_list[0]   
  66.                
  67.             new_movie.tags.add(tag)   
  68.         else:   
  69.             tag = Tag(name=tag_str)   
  70.             tag.save()     
  71.             new_movie.tags.add(tag)   
  72.     #try:   
  73.            
  74.     #except Exception.args:   
  75.     #   print "Could not download : %s" % args   
  76.     print r'download success'   

豆瓣的電影頁面並不是很對稱,所以有時候抓取的結果可能會有點出入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章