用python爬取某美劇網站的下載鏈接(一)

jopen 8年前發布 | 26K 次閱讀 網絡爬蟲

       心血來潮, 想要了解一下爬蟲的基本原理, 本著目的驅動的原則, 想要把某美劇下載網站上的聚集下載鏈接都爬下來,個人收藏;  第一次寫, 不是什么教程,只是記錄一下自己的思路和一些留著以后深入的點, 寫的太亂,還請輕噴..

         既然是目的驅動,因此每個涉及到的點在本文中都點到為止,接下來我自己也會逐步更深入的了解, 文章只是一個備忘錄, 以免稍后遺忘;

         一個最簡單的爬蟲,通常有著相似的設計思路從一個頁面開始, 分析得到所有感興趣的內容(保存)和鏈接, 再依次訪問這些鏈接進行同樣的操作, 直到不能繼續為止:

        1. 記住已經訪問過的鏈接,再次遇到時不會訪問,避免陷入循環(x)

        2. 以合適的結構和方式存儲獲取到的數據(x)

        因為本文的目的和對應的網站的鏈接及富有規律性, 且要爬取得內容也很明確(而不是像搜索引擎一樣幾乎要保存所有內容),因此以上兩點均不涉及, 最后只是簡單的使用XML保存拿到的數據;

分析對應的站點:

       版權問題, 這里掩碼了目標網站;

       本文要爬取的網站,首先有一個分頁的目錄, 每頁展示一部分美劇的簡介和詳情頁面的鏈接, 這個頁面的地址是http://www.xxxxx.net/ddc1/page/x  末尾的x是從1開始的自然數;

       詳情頁面的地址是 http://xxxxx.net/archives/xxxxx/ 末尾的xxxxx是隨機的1-5位數,無規律,并非每個數字都有頁面, 因此詳情頁面的鏈接,需要從目錄頁面提取,以免做很多無用的工作;

       目錄頁面, 每一部劇都在一個classthumbnaildiv中:

<div class="thumbnail">
   <a >
     <img class="home-thumb" src="http://xxxxx.com/soohf5.jpg" width="140px" height="100px" alt="生存指南第一季/全集Cooper Barrett.迅雷下載"/>
   </a>
 </div>

    因此可以很方便的使用下面的正則表達式取出我們需要的內容:

<div>.*?<a href="(.*?)" rel="bookmark" title="(.*?)">

   同理,分析詳情頁面, 我們得到了下面的正則表達式,取出所有的下載鏈接:

<a href="((magnet|ed2k):.*?)">(.*?)</a>

    .*? 這個問號,表示著我們需要最小匹配, 因為頁面中有著很多循環的內容;

. http請求

     使用urllib2來進行http請求;

     首先使用urllib2.Request來封裝一次請求, 其中第二個參數,設置了一個header,其中模擬了User-AgentIE, 簡單的以免部分網站攔截我們的爬蟲;

     最后,對獲取到的page,utf-8解碼, 成為unicode (python內部的字符串都是unicode)

self.header = {
     'User-Agent': 'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT)'
}
url = self.DIC_LINK + str(page_no)
req = Request(url, headers=self.header)
response = urlopen(req)
cur_page = response.read()
unicode_page = cur_page.decode("utf-8")

urlopen方法可能會拋出異常(HTTPError, URLError), HTTPError繼承自URLError, 因此應優先捕獲HTTPError; HTTPErrorcode, 是一個整形數, 也就是我們常見的404, 500http返回碼, 經過測試,目錄列表的地址,當頁數超過了已有的最大頁數的時候,網站會返回404, 所以可以把這個當作一個結束的標識;

. 正則表達式

     python的正則表達式用起來很順手, 引入 re 模塊就可以愉快的使用了;

    首先使用compile方法獲得模式對象, 注意第二個參數 re.S ,使得 . 可以匹配換行符

    然后調用findall方法,就能獲得所有的匹配了;

    正則表達式中, 括號() 圍起來的,會成為一個分組, 從左到又, 每遇到一個(, 則分組加1, 因此我們得到的items數組中, 每一個元素,又是一個數組, 這個數組的第一個元素為鏈接, 第二個元素為title的值;

    reg_str = r'<div>.*?<a href="(.*?)" rel="bookmark" title="(.*?)">'     
    reg = re.compile(reg_str, re.S)     
    items = re.findall(reg, unicode_page)

. 結構

     下面,可以開始組裝邏輯了;

      簡單的先將需要訪問的鏈接存在一個隊列中, 每次從隊列中取出鏈接訪問,獲取內容;

      這樣就將爬取目錄頁面和詳情頁面的邏輯分開了;

    下面是目錄頁面的方法:

def get_show_thread(self):
    end = False
    for page_no in range(1, 9999):
        if end:
            break
        url = self.DIC_LINK + str(page_no)
        req = Request(url, headers=self.header)
        flag = True
        while flag:
            try:
                response = urlopen(req)
                cur_page = response.read()
                unicode_page = cur_page.decode("utf-8")
                self.get_show(unicode_page)
                flag = False
            except HTTPError, e:
                print e
                if e.getcode() == 404:
                    self.total = self.total_count
                    end = True
                    break
            except URLError:
                sleep(1)
                
def get_show(self, unicode_page):
    reg_str = r'<div class="thumbnail">.*?<a href="(.*?)" rel="bookmark" title="(.*?)">'
    reg = re.compile(reg_str, re.S)
    items = re.findall(reg, unicode_page)
    self.total_count += len(items)
    for item in items:
        self.show_link.append(item)

     使用一個數組show_link,保存要訪問的詳情頁面的鏈接;

    接下來,主線程中, 不斷訪問show_link, 如果其中有鏈接, 便取出來訪問, 爬取詳情頁面的下載鏈接, 之后把這個鏈接從數組中刪除;

def get_download_link(self):
    while True:
        if len(self.show_link) > 0:
            url = self.show_link[0][0]
            notice = u'抓取' + url.encode('utf-8')
            notice += (r' ' + str(self.count) + r'/' + str(self.total_count))

            print notice
            req = Request(url, headers=self.header)
            response = urlopen(req)
            cur_page = response.read()
            unicode_page = cur_page.decode("utf-8")

            reg_str = r'<a href="((magnet|ed2k):.*?)">(.*?)</a>'
            reg = re.compile(reg_str, re.S)
            items = re.findall(reg, unicode_page)

            self.save_links(items, self.show_link[0][1])
            del self.show_link[0]
        else:
            sleep(0.5)

   --TODO-- 最后,我們按照格式,將得到的數據保存為xml文件  --TODO--

  下一篇記錄下xml事;

上文中的代碼,是截取自完整的代碼, 里面有很多變量和一些print, 主要是便于在查看運行進度(1000多個網頁,在一臺機器順序執行,且沒有做性能優化,因此是很費時間的....);


來自: http://my.oschina.net/u/587108/blog/598444

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!