32.使用selenium爬取知乎,并實現多頁保存為一個PDF文件

1.動機

對于知乎的一些高知大V,他們的回答總是那么具有說服力,通過閱讀他們的回答,了解他們對熱點事件的分析方式,通過現象看本質,一不至于被帶節奏,二增加自己的知識面。多讀多看,大有裨益。那如果在網絡信號不太好或不舍得太多流量的情況下(窮),能夠翻看他們的回答就太好了。
本篇介紹一下如何把”惡喵的奶爸“知乎回答頁全部下載下來并保存為一個PDF。

1.1.分析

實現方式一,獲取全部HTML源代碼,將多個HTML文件合成一個HTML文件,將最后合成的這個文件保存為PDF。
實現方式二,將單個HTML文件保存為PDF,再將多個PDF合成一個。
經分析,后者更容易實現。
在正式爬之前,多做一些本地的測試,在本地能夠行得通,再去騷擾目標網站。這樣做的目的,一是不讓網站運營者惡心;二是節約自己的時間和精力,因為大型網站大多有自己的反爬措施,頻繁騷擾兩三次,ip就被封了,那還要考慮換ip等一系列問題。

2.將本地HTML保存為PDF文件

先用selenium訪問以下目標網站,將源代碼保存到本地HTML,然后用本地的HTML做測試。

# -*- coding: utf-8 -*-
# @AuThor  : frank_lee
import pdfkit
htmlfile = open("zhihu_answer.html", 'r', encoding='utf-8')
confg = pdfkit.configuration(wkhtmltopdf=r'D:\htmlpdf\wkhtmltopdf\bin\wkhtmltopdf.exe')
pdfkit.from_url(htmlfile, 'zhihu.pdf', configuration=confg)
2.1.上面代碼能夠正常執行的先決條件--安裝wkhtmltopdf、pdfkit
2.1.1.先安裝wkhtmltopdf,這個工具的下載網站是:https://wkhtmltopdf.org/downloads.html

根據自己的操作系統下載對應的版本即可。安裝完成后可以將其加入到環境變量中,也可以不加入,但每次使用時需要調用wkhtmltopdf.exe的絕對路徑。

2.2.2.安裝pdfkit模塊
pip install pdfkit

3.將一個本地HTML文件保存為多個PDF文件

import pdfkit
import time
i = 0
while i < 4:
    # pdfname = "zhihu{}".format(i)+".pdf"
    htmlfile = open("zhihu_answer.html", 'r', encoding='utf-8')
    confg = pdfkit.configuration(wkhtmltopdf=r'D:\htmlpdf\wkhtmltopdf\bin\wkhtmltopdf.exe')
    pdfkit.from_url(htmlfile, "zhihu{}".format(i)+".pdf", configuration=confg)
    i += 1
    time.sleep(10)

while循環和for循環都可以實現,但是如果這樣寫,只會保存為一個完整的PDF文件,剩下的都是空白PDF:

# -*- coding: utf-8 -*-
# @AuThor  : frank_lee
import pdfkit
import time
htmlfile = open("zhihu_answer.html", 'r', encoding='utf-8')
confg = pdfkit.configuration(wkhtmltopdf=r'D:\htmlpdf\wkhtmltopdf\bin\wkhtmltopdf.exe')
i = 0
while i < 4:
    # pdfname = "zhihu{}".format(i)+".pdf"
    pdfkit.from_url(htmlfile, "zhihu{}".format(i)+".pdf", configuration=confg)
    i += 1
    time.sleep(10)

所以要注意代碼的執行順序。

4.將多個PDF文件合成一個PDF文件

# -*- coding: utf-8 -*-
# @AuThor  : frank_lee
import os, PyPDF2

# 找出所有的pdf文件,并將文件名保存至列表。
filelist = []
for filename in os.listdir('./dir-with-pdfs'):
    if filename.endswith('.pdf'):
        filelist.append(filename)

# 創建一個新的pdf
newPdfFile = PyPDF2.PdfFileWriter()

# 循環打開每一個pdf文件,將內容添加至新的pdf
for filename in filelist:
    pdfFile = open('./dir-with-pdfs/' + filename, 'rb')
    pdfObj = PyPDF2.PdfFileReader(pdfFile)
    # 獲取頁數
    pageNum = pdfObj.numPages

    for num in range(0, pageNum):
        pageContent = pdfObj.getPage(num)
        newPdfFile.addPage(pageContent)
        
newFile = open('zhihu_emiao.pdf', 'wb')
newPdfFile.write(newFile)
newFile.close()

5.動真格的

在上一篇的基礎上,添加登錄功能,因為知乎有些有些大V的回答頁面不登錄就能訪問,有些則不行。



5.1.模擬登錄

如果能夠避開驗證碼實現登錄,豈不是很輕松,哎,想一下還蠻激動,試一下使用selenium結合瀏覽器開發者模式還真可以,關鍵代碼如下:

options = webdriver.ChromeOptions()
options.add_experimental_option(
    'excludeSwitches', ['enable-automation'])
self.browser = webdriver.Chrome(options=options)

在該模式下,可以完美避開驗證碼,直接輸入用戶名和密碼就能實現登錄。如果用戶名和密碼還要手動輸入就太low了。此時,selenium可能會說,兄弟,顯示等待了解一下。這里的代碼帶有“self”,因為完整代碼是包含一個zhihu_infos類。如果直接定義一個函數,或者隨心所欲,不定義函數,想到哪兒是哪兒,可以將其刪掉。下面的代碼用到了顯示等待,顯示等待是針對于某個特定的元素設置的等待時間,如果在規定的時間范圍內,沒有找到元素,則會拋出異常,如果在規定的時間內找到了元素,則直接執行,即找到元素就執行相關操作。WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.XX, 'XX')))

既然有until,自然也有until_not

WebDriverWait()一般由until()或 until_not()方法配合使用
until(method, message=' '):調用該方法提供的驅動程序作為一個參數,直到返回值為True
until_not(method, message=' '):調用該方法提供的驅動程序作為一個參數,直到返回值為False

# 等待 登錄選項 出現,并點擊
password_login = self.wait.until(EC.presence_of_element_located(
    (By.CSS_SELECTOR, '.SignContainer-switch > span:nth-child(1)')))
password_login.click()
time.sleep(3)
# 等待 賬號 出現
zhihu_user = self.wait.until(EC.presence_of_element_located(
    (By.CSS_SELECTOR, '.SignFlow-accountInput > input:nth-child(1)')))
zhihu_user.send_keys(zhihu_username)

# 等待 密碼 出現
zhihu_pwd = self.wait.until(
    EC.presence_of_element_located(
        (By.CSS_SELECTOR,
         '.SignFlow-password > div:nth-child(1) > div:nth-child(1) > input:nth-child(1)')))
zhihu_pwd.send_keys(zhihu_password)

# 等待 登錄按鈕 出現
submit = self.wait.until(
    EC.presence_of_element_located(
        (By.CSS_SELECTOR, 'button.Button:nth-child(5)')))
submit.click()
time.sleep(10)
5.2.實現點擊

和上篇一樣,不點擊,是無法查看完整回答的,不同點是這個回答頁面共有20個回答,上篇是10個。



實現點擊并返回網頁源代碼:

def get_pagesource(self, url):
    self.browser.get(url=url)
    self.browser.maximize_window()
    time.sleep(5)

    # 執行點擊動作
    for j in range(1, 21):
        content_click = '#Profile-answers > div:nth-child(2) > div:nth-child(' + str(
            j) + ') > div > div.RichContent.is-collapsed > div.RichContent-inner'
        try:
            complete_content = self.wait.until(
                EC.presence_of_element_located(
                    (By.CSS_SELECTOR, content_click)))
            complete_content.click()
            time.sleep(1)
        except BaseException:
            pass
    pagedata = self.browser.page_source
    return pagedata
5.3.將網頁源代碼以.html的格式保存到本地
def save_to_html(self, base_file_name, pagedata):
    filename = base_file_name + ".html"
    with open(self.html_path + filename, "wb") as f:
        f.write(pagedata.encode("utf-8", "ignore"))
        f.close()
    return filename
5.4.將已保存的HTML保存為PDF格式
def html_to_pdf(self, base_file_name, htmlname):
    pdfname = base_file_name + ".pdf"
    htmlfile = open(self.html_path+htmlname, 'r', encoding='utf-8')
    confg = pdfkit.configuration(
        wkhtmltopdf=r'D:\htmlpdf\wkhtmltopdf\bin\wkhtmltopdf.exe')
    pdfkit.from_url(htmlfile, self.pdf_path + pdfname, configuration=confg)
5.5.將多個PDF文件保存為一個
def Many_to_one(self):
    # 找出所有的pdf文件,并將文件名保存至列表。
    filelist = []
    for filename in os.listdir('./pdf_file'):
        if filename.endswith('.pdf'):
            filelist.append(filename)

    # 創建一個新的pdf
    newPdfFile = PyPDF2.PdfFileWriter()

    # 循環打開每一個pdf文件,將內容添加至新的pdf
    for filename in filelist:
        pdfFile = open('./pdf_file/' + filename, 'rb')
        pdfObj = PyPDF2.PdfFileReader(pdfFile)
        # 獲取頁數
        pageNum = pdfObj.numPages

        for num in range(1, pageNum):
            pageContent = pdfObj.getPage(num)
            newPdfFile.addPage(pageContent)

    newFile = open(self.pdf_path+'惡喵的奶爸.pdf', 'wb')
    newPdfFile.write(newFile)
    newFile.close()

6.完整代碼

完整代碼實現了將多頁回答HTML格式保存為一個文件夾,PDF格式的保存到另外一個文件夾,最后將多個PDF文件合成一個PDF文件。由于代碼行數較多,貼在這里不太美觀,如有需要請查看github

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,198評論 4 61
  • 一天過去了,平平淡淡,可真是休息了,沒學習,沒工作,上午收拾家,下午打麻將,吃了飯斗地主看視頻。確實沒什么感受。
    阿燕_7526閱讀 108評論 0 2
  • 進門第一句絕對是喊媽,出門最后一句也一定是媽,跟老爸說話只會問一句“爸,我媽呢”,在我心里地位這么崇高的媽,她最愛...
    懶人菜菜閱讀 795評論 0 0