爬虫: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操作

  • 说明

    全部代码,按顺序复制本文代码亦可执行

  • 参考文献

    强化学习论文汇总

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