爬蟲:Python下載html保存成pdf——以下載知乎下某個專欄下所有文章爲例

原文地址

分類目錄——萬能的Python系列

分類目錄——爬蟲系列

  • 首先,需要下載安裝支持工具 wkhtmltopdf

    wkhtmltopdf官網

    下載地址

    安裝完成後將其下bin目錄的絕對路徑追加到環境變量中

  • 之前

    import requests
    import re
    import os
    import json
    import pdfkit
    
    HEADERS={	# 設置requests要用到的header
            'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36',
    }
    # 如果配置了環境變量無法立即生效(大概是需要重啓),可以通過這一行語句添加環境變量
    os.environ["PATH"] += os.pathsep + r'C:\Program Files\wkhtmltopdf\bin'
    
  • 獲取所有文章的url

    簡單說一下原理,訪問要下載的專欄(我的測試專欄 強化學習前沿)主頁可以發現,主頁顯示的文章列表是動態加載的,每次即將滑動到最底部的時候會動態再加載一批列表出來,通過F12打開檢查模式,抓包到動態請求的鏈接https://zhuanlan.zhihu.com/api/columns/{}/articles?include=data&limit=10&offset=40這是開發中分頁顯示的操作,limit是一頁顯示的數量,offset是此頁的起始索引,那麼只要循環幾次就可以獲得所有頁的文章列表了

    測試發現這裏limit的上限爲100,即請求一次最多獲得100條文章表項

    強化學習論文彙總的一種方式

    通過檢查當前頁的表項數目,如果少於limit,則視爲最後一頁,不再繼續請求

    def getUrls(zhuanlan):
        '''
        :param zhuanlan: such as https://zhuanlan.zhihu.com/reinforcementlearning   傳入的是最後這個reinforcementlearning
        :return: 返回專欄下所有文章的url
        '''
        urls = []
        # p_titles = []
        offset = 0
        while True:
            url = 'https://zhuanlan.zhihu.com/api/columns/{}/articles?include=data&limit=100&offset={}'.format(zhuanlan, offset)
            html_string = requests.get(url,headers=HEADERS).text
            content = json.loads(html_string)   # 獲取的content可以加載爲json格式
            urls.extend([item['url'] for item in content['data']])  # 就可以用json的方式索引到所有的url
            # p_titles.extend([item['title'] for item in content['data']])  # 獲取標題
            if len(content['data']) < 100:  # 如果獲得的表項不足100,也就是最後一頁
                break
            else:
                offset += 100
        return urls
    

    我自己用正則寫的獲取方式

    在主頁中獲取文章總數,求得頁數上限

    def getUrls2(zhuanlan):
        '''
        :param zhuanlan: such as https://zhuanlan.zhihu.com/reinforcementlearning   傳入的是最後這個reinforcementlearning
        :return: 返回專欄下所有文章的url
        '''
        urlindex = 'https://zhuanlan.zhihu.com/{}'.format(zhuanlan)
        resindex = requests.get(urlindex, headers=HEADERS)
        # print(resindex.text)
        matchac = re.search(r'"articlesCount":(\d+),', resindex.text)   # 通過正則表達式獲取文章總數
        articlesCount = int(matchac.group(1))
        upper = articlesCount//100+1  # 下面設置了每頁顯示100條,這裏求總頁數
        urls = []
        for i in range(upper):
            urlpage = 'https://zhuanlan.zhihu.com/api/columns/{}/articles?include=data&limit={}&offset={}'.format(zhuanlan, 100, 100*i)
            # limit最大是100
            respage = requests.get(urlpage, headers=HEADERS)
            respage.encoding = 'unicode_escape'
            matchurl = re.findall(r'"title":\s"[^"]+?",\s"url":\s"([^"]+?)",', respage.text)    # 通過正則匹配url
            urls+=matchurl
        return urls
    

    emmm,感覺還是人家寫的簡潔一些

  • mainJob——html to pdf

    這裏可以通過兩種方式實現

    • 直接通過os.system(wkhtmltopdf命令)執行wkhtmltopdf完成
    • 通過集成功能包pdfkit完成,需要安裝pdfkit包

    其中碰到的問題就是直接從url->pdf生成的pdf中沒有圖片,這是因爲網頁中的圖片地址採用的是相對地址,這樣直接訪問就無法獲取,講道理得拼接成完整地址,也就是說需要先獲得網頁內容(str),進行一步預處理,再轉成pdf,這裏採用了強化學習論文彙總中的方式進行了處理

    def saveArticlesPdf(urls, target_path):
        os.makedirs(target_path, exist_ok=True)
        for i, url in enumerate(urls):
            print('[ {} / {} ] processing'.format(str(i+1).zfill(3), len(urls)))
            content = requests.get(url, headers=HEADERS).text
            title = re.search(r'<h1\sclass="Post-Title">(.+)</h1>', content).group(1)
            content = content.replace('<noscript>', '')     # 解決無法下載圖片問題,其中圖片路徑爲相對路徑
            content = content.replace('</noscript>', '')
            try:
                # 方式一,直接調用wkhtmltopdf的命令
                # os.system('wkhtmltopdf {} {}'.format(content, target_path+'/{}.pdf'.format(title)))
    
                # 方式二,調用pdfkit包的方式
                pdfkit.from_string(content, target_path+'/{}.pdf'.format(title))
            except ValueError as e:	# 如果出錯打印錯誤
                print(title, e)
    
  • do it

    if __name__ == '__main__':
        zhuanlan = 'reinforcementlearning'
        # urls = getUrls(zhuanlan)
        urls = getUrls2(zhuanlan)
        saveArticlesPdf(urls, 'data/{}'.format(zhuanlan))
    

    運行效果

    • 成功的一項

      success
    • 失敗的一項

      error

    可以從urls中按索引取出失敗的url重新操作,我這裏就不再實現了,注意我在print時爲了顯示效果將索引進行了+1操作

  • 說明

    全部代碼,按順序複製本文代碼亦可執行

  • 參考文獻

    強化學習論文彙總

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