一步步教你打造微信公衆號文章爬蟲(2)-下載網頁

 

一步步教你打造微信公衆號文章爬蟲(2)-下載網頁

 

書接上文,今天一塊兒來學習把網頁版文章下載到本地電腦上。

前面講過,請求網頁的流程是瀏覽器先向服務器請求html,服務器返回html,瀏覽器分析這個html,發現html中還須要一堆的js,css,圖片,而後瀏覽器再去下載這些文件,最終組裝成一個完整的html頁面。


因此,第一步,要把這個html下載下來。

是時候請出你們期待已久的python了,我在講解的過程當中只列出核心代碼,完整代碼會列在文章最後,因此強烈建議先把整篇文章看完了再動手本身敲代碼。其餘文章也是相同的邏輯,之後再也不重複。

須要用到一個鼎鼎大名的第三方庫 requests ,用它來模擬瀏覽器給微信服務器發送請求和接收請求。

那麼發送的請求中都要包含什麼內容呢?

上文介紹chrome開發者工具時提過這個問題,奧祕在Headers這個標籤中,見下圖,理論上來說chrome瀏覽器發送了什麼咱們的最好就原樣照着用python發送什麼,即把下圖所示的General 和 Request Headers 塊中的參數全都發送出去。但多數時候並不須要這樣,特別是對於get請求,通常只須要少數幾個參數便可,可是請注意User-Agent這一項必定要改得跟chrome同樣。其餘細節再也不多述,事後您操做的多了天然會明白。css

 

簡單的註釋會直接在代碼中列出,複雜的會在代碼後面用文字再解釋,另外本文是一份原稿發佈在多個平臺,可能有的平臺顯示代碼會有縮進錯亂的問題,當你發現運行代碼出錯時請秉持「盡信書不如無書」的批判態度。html

 

#下面這一行必定別忘了
import requests
#定義一個保存文件的函數
def SaveFile(fpath,fileContent):
    with open(fpath, 'w', encoding='utf-8') as f:
            f.write(fileContent)
#定義一個下載url網頁並保存的方法
def DownLoadHtml(url):
    #構造請求頭    
    headers = {
                 'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
                 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 
                 'Connection':'keep-alive', 
                 'Accept-Language':'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3'
              }     
#模擬瀏覽器發送請求     
    response = requests.get(url,headers = headers)    
    if response.status_code == 200:#返回碼爲200表示正常返回
        htmltxt = response.text #網頁正文
        print(htmltxt)        
        return htmltxt     
    else:        
        return None 
    
    
if __name__ == "__main__": 
    url = "https://mp.weixin.qq.com/s/tK6jYTTtl8jQI86fysvj4g"
    htmlstr = DownLoadHtml(url)
    savepath="c:/vWeChatFiles/test.html" #先手動在C盤下新建vWeChatFiles文件夾
    SaveFile(savepath,htmlstr)


運行上面代碼以前,請先手動在C盤下新建vWeChatFiles文件夾,本項目以後下載的全部文件都放在此目錄下,請注意這裏是保存下載的網頁文件的,而你的python源代碼能夠放在其餘目錄下。

運行上面的代碼,你將看到屏幕刷刷得顯示一堆網頁源代碼,而且在c:/vWeChatFiles文件夾下生成一個test.html文件。

下面,來看這個下載下來的test.html可否正常工做,請先打開chrome,按F12,彈出開發者工具,在最上面一行標籤中切換到 Network,而後在chrome 的網頁中輸入C:/vWeChatFiles/test.html,回車。可能須要等幾十秒,看到了剛纔咱們下載的網頁。

同時chrome開發者工具顯示以下圖所示python


有許多行是紅色的,表示這些文件下載失敗了,剛纔之因此等了很久網頁才顯示出來就是由於瀏覽器在嘗試等待這些文件的下載,等過了一段時間仍是沒有等到對的人,它說那就算了吧,我只把接收到的內容顯示出來。


因此,仔細查看加載出來的test.html,文字顯示正常,格式也正常,可是沒圖片啊。其實這個圖片沒顯示的緣由比較複雜,先不展開講。

在test.html網頁的空白處右鍵,點"查看網頁源代碼",chrome中會打開一個新窗口,顯示這個網頁的原始源代碼,此處的源代碼,跟用記事本打開test.html文件所看到的源代碼是同樣的。

可是,這些原始代碼跟開發者工具中 Elements 中顯示的,即下圖右側顯示的格式化的網頁代碼可能不同。好比下圖箭頭所指位置的「1周前」,雖然你在右側的源代碼中也看到了「1周前」,可是在最原始的代碼中此處並無「1周前」。
其實右側顯示的代碼是原始源代碼被瀏覽器打開以後運行了js程序計算出來的網頁結構代碼。搞明白兩種源代碼的不一樣之處,能夠幫初學者減小許多迷惑。
chrome


然而奇怪的是,看源代碼對應的<img />標籤中居然沒有 src 屬性,而開發者工具中的<img /> 中的src是一堆奇怪的文字,並不像一個網址。(不過至少此處再次印證了上文我說的兩處的源代碼有可能並不一致)
編程


稍延伸一下,上圖所示的src中的一串字符實際上是表示一張圖片,一張用base64文本表示的簡單圖片,有興趣的朋友能夠自行百度,順便告訴你一個好消息:百度居然不要錢。

這些都無論了,直接把src改爲正確的圖片網址吧。但是,圖片網址在哪裏呢?請再仔細看上面的一張圖, data-src 好像是一個網頁,把這一串網頁複製出來,粘貼到瀏覽器地址欄中打開,神奇的事情發生了:居然是文章中的一張高清無碼大圖。那咱們就有理由相信,這就是本來此處要顯示的圖片的網址,可它爲啥藏着掖着,而不是直接給src設置好呢?這是個好問題,知道答案的朋友請在文章下方留言,我送你上牆。

下面,就把此網頁中全部圖片標籤<img /> 中的 data-src 的網址賦給src,須要用到另外一個優秀的第三方庫 BeautifulSoup,它能夠解析網頁中的元素,並對它們進行修改、增長、刪除等操做。下面只列出了改變過、須要重點查看的部分,並非完整代碼,因此你直接保存複製是運行不起來的,但若是加在上面給出的源代碼的後面就能跑起來(固然要小改一些地方,再次提醒讀者須要有一點編程基礎)。
瀏覽器

#新添加一個引用
from bs4 import BeautifulSoup
#讀取文件
def ReadFile(filepath):
    with open(filepath, 'r', encoding='utf-8') as f:
        all_the_text = f.read()
    return all_the_text

#修改網頁中圖片的src,使圖片能正常顯示
def ChangeImgSrc(htmlsource):
    bs = BeautifulSoup(htmlsource,"lxml") #由網頁源代碼生成BeautifulSoup對象,第二個參數固定爲lxml
    imgList = bs.findAll("img") #找出網頁中全部的<img 標籤
    for img in imgList:
        originalURL = ""  # 定義一個變量保存圖片真實url
        if "data-src" in img.attrs: #防止有的<img 標籤中可能沒有data-src而出錯
            originalURL = img.attrs['data-src']
        elif "src" in img.attrs: #若是有src則提取出來
            originalURL = img.attrs['src']
        else:
            originalURL = ""
        if originalURL.startswith("//"):#若是url以//開始,則須要添加http:
            originalURL = "http:" + originalURL
        img.attrs["src"] = originalURL
    return str(bs) #將BeautifulSoup對象再置換爲字符串,用於保存

if __name__ == "__main__":
    url = "https://mp.weixin.qq.com/s/tK6jYTTtl8jQI86fysvj4g"
    htmlstr = DownLoadHtml(url)
    htmlstr = ChangeImgSrc(htmlstr)
    savepath="c:/vWeChatFiles/test2.html" #先手動在C盤下新建vWeChatCrawl文件夾
    SaveFile(savepath,htmlstr)

注意第21行,有的圖片會以 //http://res.wx.qq.com開頭,須要換成常見的http://res.wx.qq.com 形式開頭,不然本地打開網頁時這些圖片會沒法正常加載。至於爲何會有 // 開頭的網址您能夠自行百度。

C:\vWeChatFiles文件夾下會有一個test2.html文件,在chrome中打開,網頁能夠正常顯示了,可是開發者工具中依然顯示有許多請求是紅字狀態,先不用管。

接下來的問題是,雖然圖片能夠在網頁中顯示了,但src是一個網址,圖片是在每次打開網頁時從遠程服務器下載的,並無保存到本地,若是遠程文章被刪了圖片就無法看了。

接下來咱們要作的是:把圖片下載到本地,並在html中把圖片的src指向本地圖片的位置。

主要代碼以下,須要先在vWeChatFiles目錄下新建一個images文件夾服務器

#將圖片從遠程下載保存到本地
#url:圖片網址
#savepath:本地保存路徑
def DownImg(url,savepath):
    r = requests.get(url)
    with open(savepath, 'wb') as f:
        f.write(r.content)

#修改網頁中圖片的src,使圖片能正常顯示
def ChangeImgSrc(htmltxt):
    bs =BeautifulSoup(htmltxt,"lxml") #由網頁源代碼生成BeautifulSoup對象,第二個參數固定爲lxml
    imgList = bs.findAll("img") #找出網頁中全部的圖片
    imgindex = 0 #圖片編號,不一樣圖片要保存爲不一樣的名稱
    for img in imgList:
        imgindex += 1
        originalURL = ""  # 定義一個變量用以保存圖片真實url
        if "data-src" in img.attrs:#有的<img 標籤中可能沒有data-src
            originalURL = img.attrs['data-src']
        elif "src" in img.attrs:#若是沒有data-src但有src則提取src
            originalURL = img.attrs['src']
        else: #啥都沒有則src爲空吧
            originalURL = ""
        if originalURL.startswith("//"):#若是url以//開始,則須要添加http:
            originalURL = "http:" + originalURL
        if len(originalURL) > 0: #有圖片網址則下載該圖片
            print(originalURL)
            if "data-type" in img.attrs:
                imgtype = img.attrs["data-type"] #文件的擴展名
            else:
                imgtype = "png" #不知道圖片擴展名的狀況下默認爲png
            imgname = str(imgindex)+"."+imgtype #形如 1.png的圖片名
            imgsavepath = "c:/vWeChatFiles/images/"+imgname #圖片保存目錄,後續要改爲相對地址
            DownImg(originalURL,imgsavepath) #下載圖片
            img.attrs["src"] = "images/" + imgname #網頁中圖片的相對路徑
        else :
            img.attrs["src"] = ""
    return str(bs) #將BeautifulSoup對象再置換爲字符串,用於保存


至此,網頁保存到本地基本完成,其實還有幾個小問題,好比:微信


引用樣式表時也是用到了 // 開頭的網址,聰明的你仿照圖片的修改方法去修改這裏應該不是問題,就當是小小的練習了。

又好比:開發者工具中看到許多 js 文件加載失敗,但並不影響網頁顯示,且事後會專門處理js,因此先不用管。

請注意,本示例代碼爲了方便你們理解,許多地方用了硬編碼,且都堆在一個文件中,這不是最佳方式,甚至能夠說是糟糕的方式,但在現階段代碼很少的狀況下這不是啥問題,你們先把注意力集中在功能的實現上便可。

本文會同步在多個平臺,因爲公衆號等平臺發文後不可修改,因此勘誤和難點解釋請注意查看文後留言,也能夠點擊"閱讀原文"查看個人我的博客版。關注本號,查看後續更新。


本文僅用於技術學習交流,請勿用於非法用途,由此引發的後果本做者概不負責。
app

相關文章
相關標籤/搜索