1. 程式人生 > >#python python簡單爬蟲示例——爬取自己的所有部落格,並將所有的部落格匯出到一個網頁

#python python簡單爬蟲示例——爬取自己的所有部落格,並將所有的部落格匯出到一個網頁

#python    python簡單爬蟲示例——爬取自己的所有部落格,並將所有的部落格匯出到一個網頁

學習本文需要先準備的知識點:python基本語法

1.前期準備(知識點講解)

(1)、urllib.request庫——開啟url的可擴充套件庫

urllib.request.urlopen(url)
#這個方法使用者獲取指定的連結網頁。url引數,可以是一個string,或者一個Request物件,通常是需要獲取的網頁的連結。
urllib.request.urlopen(url).read()
#讀取指定的頁面,返回值是一串字串,是指定頁面的html標籤組成的字串

 (2)、urllib.parse庫——url解析庫

urllib.parse.urljoin(pageUrl, newUrl)
#將兩個連結拼接起來

 例如:

>>> from urllib import parse
>>> url1 = "https://blog.csdn.net/weixin_41475710"
>>> url2 = "article/details/82926105"
>>> parse.urljoin(url1,url2)
'https://blog.csdn.net/weixin_41475710/article/details/82926105'
>>> url1 = "https://blog.csdn.net/weixin_41475710/article/details/82926105"
>>> url2 = "article/details/82912220"
>>> parse.urljoin(url1,url2)
'https://blog.csdn.net/weixin_41475710/article/details/82912220'

(3)、BeautifulSoup庫——從HTML或XML檔案中提取資料

BeautifulSoup物件的建立:

物件名  = BeautifulSoup(含有網頁原始碼的字串,"解析方式",from_encoding='解析編碼')

解析方式常用lxmlhtml.parser,lxml未安裝的話需要下載;解析編碼就是網頁的編碼方式,通常為utf-8,可以在網頁<head>裡的<mate>標籤裡找到。

獲取網頁指定標籤的內容:

物件名.標籤名

獲取指定標籤的指定屬性的內容

物件名.標籤名.屬性名

例如:

#建立物件
soup = BeautifulSoup(htmlCont, 'html.parser', from_encoding='utf-8')
#訪問title標籤
soup.title
#獲取title標籤的name屬性
soup.title.name

從BeautifulSoup物件裡面查詢所有的符合要求的內容:

物件名.findAll('標籤名',class_="class屬性名",name="name屬性名",…………)

標籤的所有屬性都可以用作上面這個方法的晒選,注意標籤的class屬性用class_代替,因為python裡有class的關鍵字,不能用關鍵字作為屬性名。

從BeautifulSoup屬性裡獲取第一個符合要求的內容:

物件名.find('標籤名',class_="class屬性名",name="name屬性名",…………)

 此外,還可以在已經查詢到的內容裡面繼續查詢,例如:

links = soup.find('div', class_='article-list').findAll('a', class_='title'))
for link in links:
    newUrl = link['href']
    newFullUrl = urllib.parse.urljoin(pageUrl, newUrl)
    newUrls.add(newFullUrl)

如果findAll()和find()沒有查詢到內容,會報這樣的錯誤:'NoneType' object has no attribute 'getText',程式就會在這裡中斷,如果需要重複爬取多次可以藉助try來丟擲錯誤,例如:

 try:
    titleNode = soup.find('h1', class_='title-article')
    resData = titleNode.getText()
except:
    resData = "沒有內容"

2.思路解析

(1)、爬取思路:

我們的爬蟲需要首先能從我們部落格的首頁獲取我們每一篇部落格的連結,將其儲存起來,然後爬取每一個連結,獲取文章標題和內容。

a、從主頁獲取部落格連結:

開啟我們的部落格主頁,在文章列表的最上面右擊,選擇審查元素,在這附近的程式碼上下移動,即可找到到文章列表所在的標籤:

發現文章列表儲存在一個class屬性為article-list的div標籤裡面。再往下查詢可以發現,文章的連結儲存在a標籤裡面,而且連結為http://blog.csdn.net/weixin_41475710/article/details/  + 一串數字,因此只需要查詢所有的href包含weixin_41475710/article/details/ 的a標籤,獲取他們的href屬性的內容就可以獲取所有部落格文章的連結,將其儲存起來。

b、爬取部落格文章標題和內容:

和上面同樣的方式,可以發現標題儲存在class為title-article的h1標籤裡,

文章內容有的放在class為htmledit_views的div標籤裡,有的放在class為article_content clearfix csdn-tracking-statistics的div標籤裡:

 只需要獲取網頁程式碼後查詢class為title-article的h1標籤,獲取其中的文字即為標題,查詢class為htmledit_views或者article_content clearfix csdn-tracking-statistics的div標籤,獲取其中的文字即為文章內容。

(2)、程式碼演示

a、先建立一個網頁下載器,將指定頁面的內容下載下來。

import urllib.request


class HtmlDownloader(object):
    @classmethod
    #cls表示函式本身,url是傳入的網頁連結
    def download(cls, url):
        #如果連結是空的,返回空值
        if url is None:
            return None
        #如果url不為空,獲取該網頁的頁面原始碼
        response = urllib.request.urlopen(url).read()
        return response

b、建立一個網頁解析器,解析網頁並從中查詢指定資訊。

import re
import urllib.parse

from bs4 import BeautifulSoup

class HtmlParser(object):
    #傳入頁面的url和頁面的原始碼,進行解析
    def parse(self, pageUrl, htmlCont):
        #如果url為空則函式結束
        if pageUrl is None or htmlCont is None:
            return
        #將頁面的原始碼解析,使用html.parser進行解析,解析編碼設定為utf-8
        #在讀取頁面原始碼的read()函式裡也可以用.decode('utf-8')來指定編碼方式,兩者只需其一
        soup = BeautifulSoup(htmlCont, 'html.parser', from_encoding='utf-8')
        #呼叫下面的方法從網頁原始碼中查詢內容
        #查詢頁面裡包含的指定連結並儲存
        newUrls = self.getNewUrls(pageUrl, soup)
        #查詢頁面裡指定的資料並儲存
        newData = self.getNewData(pageUrl, soup)
        return newUrls, newData
    
    #此函式用於查詢頁面中的指定連結,獲取各個部落格文章的連結
    def getNewUrls(self, pageUrl, soup):
        #建立一個set物件
        newUrls = set()
        try:
            #從頁面的class為'article-list'的div標籤裡查詢屬性href中包含有‘weixin_41475710/article/details/’的a標籤,儲存進links
            links = soup.find('div', class_='article-list').findAll('a', href=re.compile(r"weixin_41475710/article/details/"))
            for link in links:
                #獲取標籤的href屬性
                newUrl = link['href']
                #將獲取到的連結與原連結拼接
                newFullUrl = urllib.parse.urljoin(pageUrl, newUrl)
                #將上面獲拼接後的連結儲存
                newUrls.add(newFullUrl)
        except:
            return None
        return newUrls

    #此方法用於從獲取部落格文章標題和內容
    def getNewData(self, pageUrl, soup):
        resData = {}
        resData['url'] = pageUrl
        try:
            #文章標題儲存在class屬性為'title-article'的h1標籤裡面
            titleNode = soup.find('h1', class_='title-article')
            #獲取上面指定標籤的文字
            resData['title'] = titleNode.getText()
        except:
            resData['title'] = "沒有內容"

        #下面的程式碼使用者獲取網頁的文章內容,並儲存
        #有些部落格文章的正文儲存在class為markdown_views prism-atelier-sulphurpool-light的div標籤裡,有的儲存在class為htmledit_views的div標籤裡
        #所以當在class為htmledit_views標籤裡找不到就要到class為markdown_views prism-atelier-sulphurpool-light的div標籤裡找
        try:
            summaryNode = soup.find('div', class_='htmledit_views')
            resData['summary'] = summaryNode.getText()
        except:
            try:
                titleNode = soup.find('div', class_='markdown_views prism-atelier-sulphurpool-light')
                resData['summary'] = titleNode.getText()
            except:
                resData['summary'] = "沒有內容"
        return resData

c、編寫一個url管理器,儲存並管理url。

class UrlManager(object):
    #建構函式裡面建立兩個set物件,用於儲存新的url和已經使用的url
    def __init__(self):
        self.newUrls = set()
        self.outUrls = set()

    #向set物件裡面新增一個新的連結
    def addNewUrl(self, url):
        if url is None:
            return
        if url not in self.newUrls and url not in self.outUrls:
            self.newUrls.add(url)

    #向set物件裡新增許多新連結
    def addNewUrls(self, urls):
        if urls is None or len(urls) == 0:
            return
        for url in urls:
            self.addNewUrl(url)
    #判斷是否還有沒有爬取的新連結
    def hasNewUrl(self):
        return len(self.newUrls) != 0

    #返回儲存的第一個連結,將其從儲存新連結的set裡刪除,存入儲存已經匯出的set裡面
    def getNewUrl(self):
        newUrl = self.newUrls.pop()
        self.outUrls.add(newUrl)
        return newUrl

d、再寫一個頁面輸出的類,將爬取到的資訊輸出到一個網頁裡:

class HtmlOutputer(object):
    #建構函式裡建立一個list
    def __init__(self):
        self.datas = []

    #將爬取到的資訊儲存到上面定義的list裡
    def collectData(self, data):
        if data is None:
            return
        self.datas.append(data)

    #將list裡的資料輸出到網頁裡
    def outputHtml(self):
        #建立並開啟一個網頁,編碼設定為utf-8,防止亂碼
        fout = open('output.html', 'w',encoding="utf-8")
        #write函式會把函式裡的字串引數寫入網頁
        fout.write('<!DOCTYPE HTML>')
        fout.write('<html>')
        fout.write('<head><meta charset="utf-8"></head>')
        fout.write('<body>')
        fout.write('<table>')
        for data in self.datas:
            fout.write("<tr>")
            fout.write("<td>%s</td>" % data['url'])
            fout.write("<td>%s</td>" % data['title'])
            fout.write("<td>%s</td>" % data['summary'])
            fout.write("</tr>")

        fout.write('</table>')
        fout.write('</body>')
        fout.write('</html>')

e、最後寫一個協調的類來將上述所有類實現並應用:

from reptile.blog_Spider import urlManager, htmlDownloader, htmlParser, htmlOutputer

class SpiderMain(object):
    #建構函式裡面講所有的類初始化
    def __init__(self):
        self.urls = urlManager.UrlManager()
        self.downloader = htmlDownloader.HtmlDownloader()
        self.parser = htmlParser.HtmlParser()
        self.outPuter = htmlOutputer.HtmlOutputer()

    #爬取資訊的主方法
    def craw(self, rootUrl):
        #count記錄爬取次數
        count = 1
        #講初始連結寫入url管理器
        self.urls.addNewUrl(rootUrl)
        #如果url管理器裡面有新的連結,就繼續爬取
        while self.urls.hasNewUrl():
            try:
                #獲取url管理器裡的第一個連結
                newUrl = self.urls.getNewUrl()
                #輸出爬取的連結
                print('craw %d : %s' % (count, newUrl))
                #獲取該連結的原始碼
                htmlCont = self.downloader.download(newUrl)
                #從原始碼裡查詢符合要求的url和資料
                newUrls, newData = self.parser.parse(newUrl, htmlCont)
                #將爬取到的url存入url管理器
                self.urls.addNewUrls(newUrls)
                #將爬取到的資料存入輸出網頁物件的list裡
                self.outPuter.collectData(newData)
                #當次爬取完畢,count加一
                count += 1
            except:
                #如果出現異常,輸出爬取失敗
                print('craw failed')
        #爬取完畢後,將爬取到的資料輸出
        self.outPuter.outputHtml()


if __name__ == "__main__":
    啟動程式,建立上述的類的物件,執行爬取的函式
    rootUrl = "https://blog.csdn.net/weixin_41475710"
    obj_spider = SpiderMain()
    obj_spider.craw(rootUrl)