1.動(dòng)機(jī)
對(duì)于知乎的一些高知大V既穆,他們的回答總是那么具有說服力,通過閱讀他們的回答雀鹃,了解他們對(duì)熱點(diǎn)事件的分析方式幻工,通過現(xiàn)象看本質(zhì),一不至于被帶節(jié)奏黎茎,二增加自己的知識(shí)面囊颅。多讀多看,大有裨益傅瞻。那如果在網(wǎng)絡(luò)信號(hào)不太好或不舍得太多流量的情況下(窮)踢代,能夠翻看他們的回答就太好了。
本篇介紹一下如何把”惡喵的奶爸“知乎回答頁(yè)全部下載下來(lái)并保存為一個(gè)PDF俭正。
1.1.分析
實(shí)現(xiàn)方式一奸鬓,獲取全部HTML源代碼,將多個(gè)HTML文件合成一個(gè)HTML文件掸读,將最后合成的這個(gè)文件保存為PDF串远。
實(shí)現(xiàn)方式二宏多,將單個(gè)HTML文件保存為PDF,再將多個(gè)PDF合成一個(gè)澡罚。
經(jīng)分析伸但,后者更容易實(shí)現(xiàn)。
在正式爬之前留搔,多做一些本地的測(cè)試更胖,在本地能夠行得通,再去騷擾目標(biāo)網(wǎng)站隔显。這樣做的目的却妨,一是不讓網(wǎng)站運(yùn)營(yíng)者惡心;二是節(jié)約自己的時(shí)間和精力括眠,因?yàn)榇笮途W(wǎng)站大多有自己的反爬措施彪标,頻繁騷擾兩三次,ip就被封了掷豺,那還要考慮換ip等一系列問題捞烟。
2.將本地HTML保存為PDF文件
先用selenium訪問以下目標(biāo)網(wǎng)站,將源代碼保存到本地HTML当船,然后用本地的HTML做測(cè)試题画。
# -*- 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.上面代碼能夠正常執(zhí)行的先決條件--安裝wkhtmltopdf、pdfkit
2.1.1.先安裝wkhtmltopdf德频,這個(gè)工具的下載網(wǎng)站是:https://wkhtmltopdf.org/downloads.html
根據(jù)自己的操作系統(tǒng)下載對(duì)應(yīng)的版本即可苍息。安裝完成后可以將其加入到環(huán)境變量中,也可以不加入抱婉,但每次使用時(shí)需要調(diào)用wkhtmltopdf.exe的絕對(duì)路徑档叔。
2.2.2.安裝pdfkit模塊
pip install pdfkit
3.將一個(gè)本地HTML文件保存為多個(gè)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循環(huán)和for循環(huán)都可以實(shí)現(xiàn),但是如果這樣寫蒸绩,只會(huì)保存為一個(gè)完整的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)
所以要注意代碼的執(zhí)行順序。
4.將多個(gè)PDF文件合成一個(gè)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)
# 創(chuàng)建一個(gè)新的pdf
newPdfFile = PyPDF2.PdfFileWriter()
# 循環(huán)打開每一個(gè)pdf文件传蹈,將內(nèi)容添加至新的pdf
for filename in filelist:
pdfFile = open('./dir-with-pdfs/' + filename, 'rb')
pdfObj = PyPDF2.PdfFileReader(pdfFile)
# 獲取頁(yè)數(shù)
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.動(dòng)真格的
在上一篇的基礎(chǔ)上,添加登錄功能步藕,因?yàn)橹跤行┯行┐骎的回答頁(yè)面不登錄就能訪問惦界,有些則不行。
5.1.模擬登錄
如果能夠避開驗(yàn)證碼實(shí)現(xiàn)登錄咙冗,豈不是很輕松沾歪,哎,想一下還蠻激動(dòng)雾消,試一下使用selenium結(jié)合瀏覽器開發(fā)者模式還真可以灾搏,關(guān)鍵代碼如下:
options = webdriver.ChromeOptions()
options.add_experimental_option(
'excludeSwitches', ['enable-automation'])
self.browser = webdriver.Chrome(options=options)
在該模式下挫望,可以完美避開驗(yàn)證碼,直接輸入用戶名和密碼就能實(shí)現(xiàn)登錄狂窑。如果用戶名和密碼還要手動(dòng)輸入就太low了媳板。此時(shí),selenium可能會(huì)說泉哈,兄弟蛉幸,顯示等待了解一下。這里的代碼帶有“self”丛晦,因?yàn)橥暾a是包含一個(gè)zhihu_infos類奕纫。如果直接定義一個(gè)函數(shù),或者隨心所欲采呐,不定義函數(shù)若锁,想到哪兒是哪兒搁骑,可以將其刪掉斧吐。下面的代碼用到了顯示等待,顯示等待是針對(duì)于某個(gè)特定的元素設(shè)置的等待時(shí)間仲器,如果在規(guī)定的時(shí)間范圍內(nèi)煤率,沒有找到元素,則會(huì)拋出異常乏冀,如果在規(guī)定的時(shí)間內(nèi)找到了元素蝶糯,則直接執(zhí)行,即找到元素就執(zhí)行相關(guān)操作辆沦。WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.XX, 'XX')))
既然有until昼捍,自然也有until_not
WebDriverWait()一般由until()或 until_not()方法配合使用
until(method, message=' '):調(diào)用該方法提供的驅(qū)動(dòng)程序作為一個(gè)參數(shù),直到返回值為True
until_not(method, message=' '):調(diào)用該方法提供的驅(qū)動(dòng)程序作為一個(gè)參數(shù)肢扯,直到返回值為False
# 等待 登錄選項(xiàng) 出現(xiàn)妒茬,并點(diǎn)擊
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)
# 等待 賬號(hào) 出現(xiàn)
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)
# 等待 密碼 出現(xiàn)
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)
# 等待 登錄按鈕 出現(xiàn)
submit = self.wait.until(
EC.presence_of_element_located(
(By.CSS_SELECTOR, 'button.Button:nth-child(5)')))
submit.click()
time.sleep(10)
5.2.實(shí)現(xiàn)點(diǎn)擊
和上篇一樣,不點(diǎn)擊蔚晨,是無(wú)法查看完整回答的乍钻,不同點(diǎn)是這個(gè)回答頁(yè)面共有20個(gè)回答,上篇是10個(gè)铭腕。
實(shí)現(xiàn)點(diǎn)擊并返回網(wǎng)頁(yè)源代碼:
def get_pagesource(self, url):
self.browser.get(url=url)
self.browser.maximize_window()
time.sleep(5)
# 執(zhí)行點(diǎn)擊動(dòng)作
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.將網(wǎng)頁(yè)源代碼以.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.將多個(gè)PDF文件保存為一個(gè)
def Many_to_one(self):
# 找出所有的pdf文件,并將文件名保存至列表银择。
filelist = []
for filename in os.listdir('./pdf_file'):
if filename.endswith('.pdf'):
filelist.append(filename)
# 創(chuàng)建一個(gè)新的pdf
newPdfFile = PyPDF2.PdfFileWriter()
# 循環(huán)打開每一個(gè)pdf文件,將內(nèi)容添加至新的pdf
for filename in filelist:
pdfFile = open('./pdf_file/' + filename, 'rb')
pdfObj = PyPDF2.PdfFileReader(pdfFile)
# 獲取頁(yè)數(shù)
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.完整代碼
完整代碼實(shí)現(xiàn)了將多頁(yè)回答HTML格式保存為一個(gè)文件夾累舷,PDF格式的保存到另外一個(gè)文件夾浩考,最后將多個(gè)PDF文件合成一個(gè)PDF文件。由于代碼行數(shù)較多被盈,貼在這里不太美觀析孽,如有需要請(qǐng)查看github