美文网首页
32.使用selenium爬取知乎,并实现多页保存为一个PDF文

32.使用selenium爬取知乎,并实现多页保存为一个PDF文

作者: starrymusic | 来源:发表于2019-04-08 21:09 被阅读0次

    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

    相关文章

      网友评论

          本文标题:32.使用selenium爬取知乎,并实现多页保存为一个PDF文

          本文链接:https://www.haomeiwen.com/subject/khhmiqtx.html