爬蟲(chóng)系列(八)爬蟲(chóng)中的正則表達(dá)式

現(xiàn)在擁有了正則表達(dá)式這把神兵利器,我們就可以進(jìn)行對(duì)爬取到的全部網(wǎng)頁(yè)源代碼進(jìn)行篩選了。

下面我們一起嘗試一下爬取內(nèi)涵段子網(wǎng)站: http://www.neihan8.com/article/list_5_1.html

打開(kāi)之后,不難看到里面一個(gè)一個(gè)灰常有內(nèi)涵的段子,當(dāng)你進(jìn)行翻頁(yè)的時(shí)候,注意url地址的變化:

這樣我們的url規(guī)律找到了,要想爬取所有的段子,只需要修改一個(gè)參數(shù)即可。 下面我們就開(kāi)始一步一步將所有的段子爬取下來(lái)吧。


第一步,獲取數(shù)據(jù)

1. 按照我們之前的用法,我們需要寫(xiě)一個(gè)加載頁(yè)面的方法。
這里我們統(tǒng)一定義一個(gè)類(lèi),將url請(qǐng)求作為一個(gè)成員方法處理。

我們創(chuàng)建一個(gè)文件,叫duanzi_spider.py

然后定義一個(gè)Spider類(lèi),并且添加一個(gè)加載頁(yè)面的成員方法

      import urllib2

    class Spider:
        """
            內(nèi)涵段子爬蟲(chóng)類(lèi)
        """
        def loadPage(self, page):
            """
                @brief 定義一個(gè)url請(qǐng)求網(wǎng)頁(yè)的方法
                @param page 需要請(qǐng)求的第幾頁(yè)
                @returns 返回的頁(yè)面html
            """

        url = "http://www.neihan8.com/article/list_5_" + str(page)
    + ".html"
        #User-Agent頭
        user_agent = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT
    6.1; Trident/5.0'

        headers = {'User-Agent': user_agent}
        req = urllib2.Request(url, headers = headers)
        response = urllib2.urlopen(req)
        html = response.read()
        print html

        #return html

以上的loadPage的實(shí)現(xiàn)體想必大家應(yīng)該很熟悉了,需要注意定義python類(lèi)的成員方法需要額外添加一個(gè)參數(shù)self。

那么loadPage(self, page) 中的page是我們指定去請(qǐng)求第幾頁(yè)。

最后通過(guò) print html打印到屏幕上。

然后我們寫(xiě)一個(gè)main函數(shù)見(jiàn)到測(cè)試一個(gè)loadPage方法

2. 寫(xiě)main函數(shù)測(cè)試一個(gè)loadPage方法

          #
        if __name__ == '__main__':
        """
            ======================
                內(nèi)涵段子小爬蟲(chóng)
            ======================
        """
        print '請(qǐng)按下回車(chē)開(kāi)始'
        raw_input()

        #定義一個(gè)Spider對(duì)象
        mySpider = Spider()
        mySpider.loadpage(1)

那么我們需要簡(jiǎn)單的將得到的網(wǎng)頁(yè)源代碼處理一下:

    def loadPage(self, page):
        """
            @brief 定義一個(gè)url請(qǐng)求網(wǎng)頁(yè)的方法
            @param page 需要請(qǐng)求的第幾頁(yè)
            @returns 返回的頁(yè)面html
        """

        url = "http://www.neihan8.com/article/list_5_" + str(page)
    + ".html"
        #User-Agent頭
        user_agent = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT
    6.1; Trident/5.0'
        headers = {'User-Agent': user_agent}
        req = urllib2.Request(url, headers = headers)
        response = urllib2.urlopen(req)
        html = response.read()
        gbk_html = html.decode('gbk').encode('utf-8')
        # print gbk_html
        return gbk_html

注意 :對(duì)于每個(gè)網(wǎng)站對(duì)中文的編碼各自不同,所以html.decode(‘gbk’)的寫(xiě)法并不是通用寫(xiě)法,根據(jù)網(wǎng)站的編碼而異

  • 這樣我們?cè)俅螆?zhí)行以下duanzi_spider.py ,會(huì)發(fā)現(xiàn)之前的中文亂碼可以正常顯示了。


    image.png

第二步:篩選數(shù)據(jù)

接下來(lái)我們已經(jīng)得到了整個(gè)頁(yè)面的數(shù)據(jù)。 但是,很多內(nèi)容我們并不關(guān)心,所以下一步我們需要進(jìn)行篩選。 如何篩選,就用到了上一節(jié)講述的正則表達(dá)式。

import re
  • 然后, 在我們得到的gbk_html中進(jìn)行篩選匹配。

我們需要一個(gè)匹配規(guī)則:

我們可以打開(kāi)內(nèi)涵段子的網(wǎng)頁(yè),鼠標(biāo)點(diǎn)擊右鍵 “ 查看源代碼 ” 你會(huì)驚奇的發(fā)現(xiàn),我們需要的每個(gè)段子的內(nèi)容都是在一個(gè) <div>標(biāo)簽中,而且每個(gè)div都有一個(gè)屬性class = "f18 mb20"


image.png

所以,我們只需要匹配到網(wǎng)頁(yè)中所有<div class="f18 mb20"> 到 </div> 的數(shù)據(jù)就可以了。

根據(jù)正則表達(dá)式,我們可以推算出一個(gè)公式是:

<div.*?class="f18 mb20">(.*?)</div>
  • 這個(gè)表達(dá)式實(shí)際上就是匹配到所有div中class="f18 mb20 里面的內(nèi)容(具體可以看前面正則介紹)

  • 然后將這個(gè)正則應(yīng)用到代碼中,我們會(huì)得到以下代碼:

    def loadPage(self, page):
        """
            @brief 定義一個(gè)url請(qǐng)求網(wǎng)頁(yè)的方法
            @param page 需要請(qǐng)求的第幾頁(yè)
            @returns 返回的頁(yè)面html
        """
    
        url = "http://www.neihan8.com/article/list_5_" + str(page)
    + ".html"
        #User-Agent頭
        user_agent = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT
    6.1; Trident/5.0'
        headers = {'User-Agent': user_agent}
        req = urllib2.Request(url, headers = headers)
        response = urllib2.urlopen(req)
        html = response.read()
        gbk_html = html.decode('gbk').encode('utf-8')
    
        #找到所有的段子內(nèi)容<div class = "f18 mb20"></div>
        #re.S 如果沒(méi)有re.S 則是只匹配一行有沒(méi)有符合規(guī)則的字符串,如果沒(méi)有則下一行重新匹配
        # 如果加上re.S 則是將所有的字符串將一個(gè)整體進(jìn)行匹配
        pattern = re.compile(r'<div.*?class="f18 mb20">(.*?)</di
    v>', re.S)
        item_list = pattern.findall(gbk_html)
    
        return item_list
    
    
    def printOnePage(self, item_list, page):
        """
            @brief 處理得到的段子列表
            @param item_list 得到的段子列表
            @param page 處理第幾頁(yè)
        """
    
        print "******* 第 %d 頁(yè) 爬取完畢...*******" %page
        for item in item_list:
            print "================"
            print item
    
  • 這里需要注意一個(gè)是re.S是正則表達(dá)式中匹配的一個(gè)參數(shù)。

  • 如果 沒(méi)有re.S 則是 只匹配一行 有沒(méi)有符合規(guī)則的字符串,如果沒(méi)有則下一行重新匹配。

  • 如果 加上re.S 則是將 所有的字符串 將一個(gè)整體進(jìn)行匹配,findall 將所有匹配到的結(jié)果封裝到一個(gè)list中。

  • 然后我們寫(xiě)了一個(gè)遍歷item_list的一個(gè)方法 printOnePage() 。 ok程序?qū)懙竭@,我們?cè)僖淮螆?zhí)行一下。

        python duanzi_spider.py
    

我們第一頁(yè)的全部段子,不包含其他信息全部的打印了出來(lái)。

  • 你會(huì)發(fā)現(xiàn)段子中有很多 <p> , </p> 很是不舒服,實(shí)際上這個(gè)是html的一種段落的標(biāo)簽。

  • 在瀏覽器上看不出來(lái),但是如果按照文本打印會(huì)有<p>出現(xiàn),那么我們只需要把我們不希望的內(nèi)容去掉即可了。

  • 我們可以如下簡(jiǎn)單修改一下 printOnePage().

      def printOnePage(self, item_list, page):
          """
              @brief 處理得到的段子列表
              @param item_list 得到的段子列表
              @param page 處理第幾頁(yè)
          """
    
          print "******* 第 %d 頁(yè) 爬取完畢...*******" %page
          for item in item_list:
              print "================"
              item = item.replace("<p>", "").replace("</p>", "").repl
      ace("<br />", "")
              print item
    

第三步:保存數(shù)據(jù)

  • 我們可以將所有的段子存放在文件中。比如,我們可以將得到的每個(gè)item不是打印出來(lái),而是存放在一個(gè)叫 duanzi.txt 的文件中也可以。

      def writeToFile(self, text):
      '''
          @brief 將數(shù)據(jù)追加寫(xiě)進(jìn)文件中
          @param text 文件內(nèi)容
      '''
          myFile = open("./duanzi.txt", 'a') #追加形式打開(kāi)文件
          myFile.write(text)
          myFile.write("---------------------------------------------
      --------")
          myFile.close()
    
  • 然后我們將print的語(yǔ)句 改成writeToFile() ,當(dāng)前頁(yè)面的所有段子就存在了本地的MyStory.txt文件中。

      def printOnePage(self, item_list, page):
      '''
          @brief 處理得到的段子列表
          @param item_list 得到的段子列表
          @param page 處理第幾頁(yè)
      '''
          print "******* 第 %d 頁(yè) 爬取完畢...*******" %page
          for item in item_list:
              # print "================"
              item = item.replace("<p>", "").replace("</p>", "").repl
      ace("<br />", "")
              # print item
              self.writeToFile(item)
    

第四步:顯示數(shù)據(jù)

  • 接下來(lái)我們就通過(guò)參數(shù)的傳遞對(duì)page進(jìn)行疊加來(lái)遍歷 內(nèi)涵段子吧的全部段子內(nèi)容。

  • 只需要在外層加一些邏輯處理即可。

      def doWork(self):
      '''
          讓爬蟲(chóng)開(kāi)始工作
      '''
          while self.enable:
              try:
                  item_list = self.loadPage(self.page)
              except urllib2.URLError, e:
                  print e.reason
                  continue
    
              #對(duì)得到的段子item_list處理
              self.printOnePage(item_list, self.page)
              self.page += 1 #此頁(yè)處理完畢,處理下一頁(yè)
              print "按回車(chē)?yán)^續(xù)..."
              print "輸入 quit 退出"
              command = raw_input()
              if (command == "quit"):
                  self.enable = False
                  break
    
  • 最后,我們執(zhí)行我們的代碼,完成后查看當(dāng)前路徑下的duanzi.txt文件,里面已經(jīng)有了我們要的內(nèi)涵段子。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容