整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

「Python自学笔记」新手必备!Python爬虫requests_html模块

  • 基本使用
    • 发送请求
    • 解析响应获
  • 获取需要的内容
    • 快速获取链接
    • 获取元素
  • 高级功能
    • JS渲染
    • 自动翻页(不太好用)
  • 异步
    • 异步渲染JS
    • 异步发送请求

初识requests_html模块

感觉只要学过Python爬虫的同学应该都知道requests这个库吧,它在我们的Python爬虫任务中应该是最常用的一个库了!今天跟大家分享的这个模块requests_html,他的作者和前者是同一人!这是一个解析HTML的库,用起来和requests一样方便,下面就来介绍一下它!

  • 参考视频

使用requests_html

安装

  • 依然是那个命令 pip3 install -i https://pypi.doubanio.com/simple requests_html
  • 注意:由于requests_html模块中使用了异步asynico模块,所以官方声明,需要在python3.6以上版本才能正常使用!

基本使用

发送请求

  • requests_html发送请求获取页面需要先实例化一个HTMLSession对象,然后使用get/post...方法获取响应,如下列代码
#!/usr/bin/env python3
# coding     : utf-8
# Author     : xiao qiang
# 微信公众号   : xiaoqiangclub
# Software   : PyCharm
# File       : test.py
# Time       : 2021/5/29 7:57
from requests_html import HTMLSession

if __name__ == '__main__':
    url = 'https://wwww.baidu.com'
    session = HTMLSession()  # 获取实例化session对象
    r = session.get(url)    # 这里的请求和requests的几乎一样!同样可以根据需要添加headers等参数
  • requests_html发送请求的方式和requests中使用session方式发送请求几乎是一样的,可以对比参考
  • requests_html同样可以发送get/post等请求,且可以和requests同样携带headers/data等参数,具体用法参考requests

解析响应获

  • 接上,我们需要将获取的响应解析获取html页面,在这里我们同样可以使用requests中的r.content.decode()等原方法!
  • 但是在requests_html中还提供了更便捷的方法:r.html.html
  • r.html.html实际上是使用了requests_html中的HTML类(负责对HTML进行解析)来进行解析!如下
#!/usr/bin/env python3
# coding     : utf-8
# Author     : xiao qiang
# 微信公众号   : xiaoqiangclub
# Software   : PyCharm
# File       : test.py
# Time       : 2021/5/29 7:57
from requests_html import HTMLSession

if __name__ == '__main__':
    url = 'https://wwww.baidu.com'
    session = HTMLSession()  # 获取实例化session对象
    r = session.get(url)  # 这里的请求和requests的几乎一样!同样可以根据需要添加headers等参数

    # 获取html页面
    # html = r.content.decode()  # requests方式
    get_html = r.html.html  # requests_html中的方法
    print(get_html[:15], '...')
  • 运行结果(这里只显示了部分结果!)

获取需要的内容

快速获取链接

  • requests_html中提供了快速获取网址链接的方法
  • 使用linksabsolute_links两个属性分别可以返回HTML对象所包含的所有链接和绝对链接(均不包含锚点)
# 快速获取链接
pprint(r.html.links)  # 获取html中的链接(href属性)
pprint(r.html.absolute_links)  # 会自动拼接url生成绝对链接
  • 部分运行结果如下

获取元素

  • requests_html中的HTML对象可以直接使用xpathcss选择器

使用xpath

  • requests_html中的HTML对象支持xpath语法,它有以下几个参数:
def xpath(self, selector: str, *, clean: bool = False, first: bool = False, _encoding: str = None) -> _XPath:
- selector,要用的 xpath选择器;
- clean,布尔值,如果为True,会清除HTML中style和script标签;
- first,布尔值,如果为True,会返回第一个元素,否则会返回满足条件的元素列表;
- _encoding,编码格式。
  • 接上面的例子!使用获取到的响应得到HTML对象r.html
pprint(r.html.xpath('//li[@class="hotsearch-item odd"]/a'))
pprint(r.html.xpath('//li[@class="hotsearch-item odd"]/a', first=True).text)
  • 运行结果
  • xpath语法

使用css选择器(find方法)

  • requests_html中的HTML对象支持css选择器,它有以下几个参数:
def find(self, selector: str = "*", *, containing: _Containing = None, clean: bool = False, first: bool = False, _encoding: str = None) -> _Find:
- selector,要用的CSS选择器;
- clean,布尔值,如果为True,会清除HTML中style和script标签;
- containing,如果设置该属性,只返回包含该属性文本的标签;
- first,布尔值,如果为True,会返回第一个元素,否则会返回满足条件的元素列表;
- _encoding,编码格式。
  • 接上面的例子!使用获取到的响应得到HTML对象r.html
pprint(r.html.find('a.mnav'))
pprint(r.html.find('a.mnav', first=True).text)
  • 运行结果
  • css选择器语法
  • 可以使用text属性来获取元素的文本内容

pprint(r.html.find('a.mnav')[0].text)

  • 执行结果
  • 如果要获取元素的attribute,用attrs属性

pprint(r.html.find('a.mnav')[0].attrs)

  • 执行结果
  • 获取到attrs属性后,就可以使用字典的相关方法获取内容了!
  • 最后还可以使用html属性获取一个元素的html代码,如下

pprint(r.html.find('a.mnav')[0].html)

  • 执行结果

正则搜索(search、search_all)

  • requests_html除了上面的方式还可以使用search/search_all来直接搜索内容,返回的是一个Result对象/Result对象列表实际上是作者将re正则进行了封装)!
def search(self, template: str) -> Result:
# 只有一个参数
template: 就是要检索的内容,这里使用英文状态的 {} 来获取内容,有点类似正则里面的 ()
  • 使用英文状态的 {} 来获取内容,如下
    ret = r.html.find('a.mnav')[0].search('新{}')
    pprint(ret)
    pprint(type(ret))
    pprint(ret[0])
  • 执行结果
  • search()获取到的是第一个匹配的对象,而searchh_all()则是获取所有匹配的对象,得到的是一个列表,如下

ret = r.html.find('a.mnav')[0].search_all('新{}')
pprint(ret)
pprint(type(ret))
pprint(ret[0][0])

  • 运行结果
  • 除了对某个元素进行检索外,还可以直接对html对象进行搜索,如下

ret = r.html.search_all('百度{}')
pprint(ret)
pprint(type(ret))
pprint(ret[0][0])

  • 运行结果
  • requests_html内容提取的方式这么多,大家可以根据需要和习惯选择使用!

search补充

  • 在上面提到的search()/search_all()方法中,我们设定的template参数可以有多个取值(多个{}),得到的结果是一个列表,我们可以遍历别表进行取值 取值的时候可以通过result[索引]的方式进行获取对应的数据,如下(示例部分代码) search_ret = r.html.search_all('<a h{}f="{}"',)
    print(search_ret)
    for ret in search_ret:
    print(ret)
    print(ret[1])
    print('--------------')
    运行结果(部分)
  • 除此之外,我们还可以对取值进行命名,返回的结果是可以使用类似字典(不是字典)[name]的方式取值(不能使用get),如下示例(部分代码) search_ret = r.html.search_all('<a h{test}f="{url}"',)
    print(search_ret)
    for ret in search_ret:
    print(ret)
    print(ret['name'])
    print('--------------')
  • 运行结果(部分)
  • 以上就是对requests_html模块search()/search_all()方法的补充内容!

HTML类

  • requests_html中使用HTML类负责对HTML进行解析
  • HTML类不仅可以解析网络请求获取的响应,还可以直接解析html文本,如下
>>> from requests_html import HTML
>>> doc = """<a href='https://www.baidu.com'>"""

>>> html = HTML(html=doc)
>>> html.links
{'https://www.baidu.com'}
  • 还可以直接渲染JS,如下
# 和上面一段代码接起来
>>> script = """
        () => {
            return {
                width: document.documentElement.clientWidth,
                height: document.documentElement.clientHeight,
                deviceScaleFactor: window.devicePixelRatio,
            }
        }
    """
>>> val = html.render(script=script, reload=False) # render()方法 后面会讲

>>> print(val)
{'width': 800, 'height': 600, 'deviceScaleFactor': 1}

>>> print(html.html)
<html><head></head><body><a href="https://www.baidu.com"></a></body></html>

高级功能

  • 前面介绍的是requests_htmlrequests库的基础上整合的html解析&数据筛选的功能!
  • 下面要为大家介绍的是requests_html模块中的一些高级功能:自动渲染JS&智能分页

JS渲染

  • 我们在做爬虫项目的时候会遇到网站的页面是由js生成的情况!这个时候要么就是自己去一步一步地分析请求,要么就是使用selenium等第三方库来进行渲染页面,为了解决这个难题,requests_html模块中引进了pyppeteer,使用pyppeteer可以像使用selenium一样实现网站的完整加载!而且pyppeteer是一个异步模块!效率会更高!
  • requests_html模块在HTML对象的基础上使用render()方法重新加载js页面
  • 注意:在第一次使用render()的时候,系统在用户目录(默认是~/.pyppeteer/)中下载一个chromium。下载过程只在第一次执行,以后就可以直接使用chromium来执行任务了。在没有科学上网的环境下可能下载速度有点慢,请耐心等待...
  • 下面是一个官方示例
>>> r = session.get('http://python-requests.org/')

>>> r.html.render()
[W:pyppeteer.chromium_downloader] start chromium download.
Download may take a few minutes.
[W:pyppeteer.chromium_downloader] chromium download done.
[W:pyppeteer.chromium_downloader] chromium extracted to: C:\Users\xxxx\.pyppeteer\local-chromium\571375
>>> r.html.search('Python 2 will retire in only {months} months!')['months']
'<time>25</time>'
  • requests_html模块在HTML对象的基础上使用render()方法重新加载js页面,它有以下几个参数:
def render(self, retries: int = 8, script: str = None, wait: float = 0.2, scrolldown=False, sleep: int = 0, reload: bool = True, timeout: Union[float, int] = 8.0, keep_page: bool = False):
- retries: 加载页面失败的次数
- script: 页面上需要执行的JS脚本(可选)
- wait: 加载页面前等待的时间(秒),防止超时(可选)
- scrolldown: 页面向下滚动的次数(整数)
- sleep: 在页面初次渲染之后的等待时间
- reload: 如果为False,那么页面不会从浏览器中加载,而是从内存中加载,只有设置为True才会在浏览器中渲染JS
- keep_page: 如果为True,允许您使用 r.html.page 访问浏览器页面
  • requests_html还支持异步渲染JS[^1]

自动翻页(不太好用)

  • 很多网站会出现翻页的情况,requests_html模块的HTML对象中提供了一个next()方法来实现自动翻页!
  • requests_html模块在HTML对象的基础上使用next()方法来实现自动翻页!它有以下几个参数:
def next(self, fetch: bool = False, next_symbol: _NextSymbol = DEFAULT_NEXT_SYMBOL) -> _Next:
fetch: 一个布尔型参数,默认为False:直接返回下一页的 url地址;
       如果设置为True:则直接返回下一页的 HTML对象
  • 这个方法我自己测试了一下,只有一些特定的网站才能实现这个功能,requests_html在的源码中可以看到,作者通过搜索包含'next', 'more', 'older'字段的a标签(因为一般情况下我们的下一页url就是在a标签下的href属性中),所以只有满足了他的条件才会实现这个功能(也就是说HTML页面不按照这个套路它就无法实现这个功能!),下面是部分源码
DEFAULT_NEXT_SYMBOL = ['next', 'more', 'older']
# next()方法
    def next(self, fetch: bool = False, next_symbol: _NextSymbol = DEFAULT_NEXT_SYMBOL) -> _Next:
        """Attempts to find the next page, if there is one. If ``fetch``
        is ``True`` (default), returns :class:`HTML <HTML>` object of
        next page. If ``fetch`` is ``False``, simply returns the next URL.

        """

        def get_next():
            candidates = self.find('a', containing=next_symbol) # 寻找 包含字段'next', 'more', 'older' 的a标签
  • 这里我就不做举例了,大家可以自行去尝试!

异步

  • requests_html中还支持了异步功能
  • requests_html是使用了asynico来封装了一些异步操作,所以这里的一些操作会用到asynico库相关的一些知识,如果您还不太了解,请自行学习!

异步渲染JS

  • 前面我们介绍了使用render()方法渲染JS,其实它还支持异步渲染
  • 一般异步渲染使用在我们有多个页面需要进行渲染的情况下,因为只要在多任务的时候才能体现出异步的高效率,它有以下几个参数:
def __init__(self, loop=None, workers=None, mock_browser: bool = True, *args, **kwargs):
loop: 使用的Asyncio循环。
workers: 用于执行异步调用的线程数量。如果不传递,它将默认为电脑处理器数量乘以5
  • 更多的异步使用方法请参考asyncio库的使用方法,下面是一个官方示例
>>> async def get_pyclock():
...     r = await asession.get('https://pythonclock.org/')
...     await r.html.arender()
...     return r
...
>>> results = asession.run(get_pyclock, get_pyclock, get_pyclock) # 这里作者将同一个页面使用异步方式进行了3次渲染,但是实际上使用的时间并不是平时的3倍!可能只是比平时渲染一个页面多花了一点时间而已!这就是异步的好处!
  • 注意:results是一个列表

异步传参

  • 这里是后面加的内容,因为我突然想到有时候我们的函数有可能是带参数的,那么这个时候我们可以使用lambda来进行传参,看下面示例
#!/usr/bin/env python
# -*- encoding: utf-8 -*-                            
# @Author     : xiao qiang
# @WeChat     : xiaoqiangclub                              
# @Software   : PyCharm      
# @File       : test002.py
# @Time       : 2021/5/30 19:48
from requests_html import AsyncHTMLSession

aSession = AsyncHTMLSession()


async def test(tt, yy):
    r = await aSession.get('https://www.baidu.com/')
    await r.html.arender()
    print('-{}-{}-'.format(tt, yy))
    return r


ret1 = aSession.run(lambda: test('1', 'a'))
ret2 = aSession.run(lambda: test('2', 'b'))
ret3 = aSession.run(lambda: test('3', 'c'))
print(ret1)
print(ret2)
print(ret3)
  • 注意:这里ret1/ret2/ret3都是列表
  • 运行结果
  • 上面的示例还可以这样写

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# @Author : xiao qiang
# @WeChat : xiaoqiangclub
# @Software : PyCharm
# @File : test002.py
# @Time : 2021/5/30 19:48
from requests_html import AsyncHTMLSession
aSession = AsyncHTMLSession()
async def test(tt, yy):
r = await aSession.get('https://www.baidu.com/')
await r.html.arender()
print('-{}-{}-'.format(tt, yy))
return r
# ret1 = aSession.run(lambda: test('1', 'a'))
# ret2 = aSession.run(lambda: test('2', 'b'))
# ret3 = aSession.run(lambda: test('3', 'c'))
# print(ret1)
# print(ret2)
# print(ret3)
#
test_dict = {
'1': 'a',
'2': 'b',
'3': 'c'
}
tasks = [lambda i=i, y=y: test(i, y) for i, y in
test_dict.items()]
# lambda传参误区参考文章:https://www.jianshu.com/p/58ebd1618556
ret = aSession.run(*tasks)
# 注意前面有个 *,不可少!# 参考文章:https://www.jianshu.com/p/58ebd1618556
print(ret)

  • 这里在使用lambda传参的时候可能会出现一个错误,可以参考文章解决!
  • 运行结果

异步发送请求

  • 我们在做爬虫的时候,特别是大型爬虫的时候,需要对很多页面进行操作,或者说是需要发送很多请求,也就是需要进行很多IO操作。所以,使用异步发送请求能显著地提升我们的爬虫效率!
  • requests_html模块中,设置了一个AsyncHTMLSession类来实现发送异步请求
  • ,下面是一个官方示例
>>> from requests_html import AsyncHTMLSession
>>> asession = AsyncHTMLSession()
>>> async def get_pythonorg():
...     r = await asession.get('https://python.org/')
...     return r
...
>>> async def get_reddit():
...    r = await asession.get('https://reddit.com/')
...    return r
...
>>> async def get_google():
...    r = await asession.get('https://google.com/')
...    return r
...
>>> results = asession.run(get_pythonorg, get_reddit, get_google)
>>> results # check the requests all returned a 200 (success) code
[<Response [200]>, <Response [200]>, <Response [200]>]
>>> # Each item in the results list is a response object and can be interacted with as such
>>> for result in results:
...     print(result.html.url)
...
https://www.python.org/
https://www.google.com/
https://www.reddit.com/
  • 上面的示例用到了asynico库中一些相关的知识,如果您还不太了解,请自行学习!

总结

  • requests_html模块requests库的基础上封装了页面解析数据清理的功能,并且添加了对当前比较流行的异步操作,让我们在做爬虫项目(一般项目)的时候无需再去使用多个第三方模块来实现功能,几乎是提供了一站式的服务!
  • 所以Python写爬虫使用requests_html就对了!(当然大项目还是首选scrapy,个人愚见!)
  • 更多内容

视频讲解源码

from requests_html import HTMLSession, HTML, AsyncHTMLSession
from pprint import pprint


class DouBanTest:
    def __init__(self):
        self.start_url = 'https://movie.douban.com/chart'  # 豆瓣电影排行榜url
        self.js_url = 'https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0'
        self.session = HTMLSession()  # 实例化session
        self.aSession = AsyncHTMLSession()  # 实例化异步session
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'
        }

    def get_response(self, url):
        """获取响应,并返回requests_html中的HTML对象"""
        r = self.session.get(url, headers=self.headers)
        # print(r)

        return r.html

    # 快速获取页面中的url
    def fast_get_urls(self):
        """快速获取页面中的url"""
        html = self.get_response(self.start_url)

        # HTML的 links属性 可以快速获取到页面中 a标签中的href属性
        urls = html.links
        # pprint(urls)

        # HTML的 absolute_links属性 可以快速获取到页面中 a标签中的href属性,并返回绝对url地址

        absolute_urls = html.absolute_links
        pprint(absolute_urls)

    # 清洗数据(提取数据)
    def get_data_by_xpath(self):
        """使用xpath获取数据"""
        html = self.get_response(self.start_url)
        a_list = html.xpath('//table//div/a')
        # pprint(a_list)

        # 提取它的标题和url
        movies_info = dict()
        for a in a_list:
            title = a.text  # 获取标题(文本)
            # print(title)
            movie_url = a.attrs.get('href')  # 使用 attrs 来解析element元素,并获得一个字典
            # print(movie_url)
            # print('-----')
            movies_info[title] = movie_url

        pprint(movies_info)

    # 清洗数据(提取数据)
    def get_data_by_css(self):
        """使用css获取数据"""
        html = self.get_response(self.start_url)
        a_list = html.find('tr[class="item"] div a')  # 参考 css选择器 语法
        # pprint(a_list)

        # 提取它的标题和url
        movies_info = dict()
        for a in a_list:
            title = a.text  # 获取标题(文本)
            # print(title)
            movie_url = a.attrs.get('href')  # 使用 attrs 来解析element元素,并获得一个字典
            # print(movie_url)
            # print('-----')
            movies_info[title] = movie_url

        pprint(movies_info)

    # 清洗数据(提取数据)
    def get_data_by_re(self):
        """使用css获取数据"""
        html = self.get_response(self.start_url)

        # search() 获取第一条匹配的数据
        # first_url = html.search('a href="{}"')  # 参数可以参考正则,获取第一条匹配的数据
        # pprint(first_url)

        # search_all() 获取所有满足条件的数据列表
        # url_list = html.search_all('a h{}f="{}"')
        url_list = html.search_all('a h{title}f="{url}"')  # 对取值方式进行命名,返回一个列表

        # pprint(url_list)
        #
        # 提取数据
        for url in url_list:
            print(url)
            print(url['title'])  # 使用 result[name] 进行取值
            print(url['url'])
            # print(url[0])
            # print(url[1])
            print('----------')

    # HTML类
    def use_HTML(self):
        """使用HTML模块处理文档"""
        html_str = '<a class="nbg" href="https://movie.douban.com/subject/3099221/" title="活死人军团">'
        html = HTML(html=html_str)

        # links
        print(html.links)

        # search()
        print(html.search('href="{}"'))

    # 加载JS页面
    def load_js(self):
        html = self.get_response(self.js_url)

        # 使用一个 render()方法 来加载js(实际上使用这个pyppeteer)
        # html.render(wait=3)  # js加载
        print(html.html)

    async def send_requests_ues_async(self, url):
        """发送异步请求"""

        """获取响应,并返回requests_html中的HTML对象"""
        r = await self.aSession.get(url, headers=self.headers)
        # print(r)

        return r.html

    def get_response_by_async(self):
        url_list = [
            'https://www.baidu.com',
            'https://www.qq.com',
            'https://www.163.com',
        ]
        tasks = [lambda url=url: self.send_requests_ues_async(url) for url in url_list]
        ret = self.aSession.run(*tasks)  # 返回的是一个HTML对象列表
        # print(ret)
        # print(ret[0].html)
        for html in ret:
            print(html)

    async def load_js_use_async(self, url):
        """异步加载js"""
        html = await self.send_requests_ues_async(url)

        # 异步加载js
        await html.arender()

        return html

    def get_js_by_async(self):
        # ret = self.aSession.run(self.load_js_use_async)
        #
        # print(ret[0].html)

        url_list = [
            'https://www.baidu.com',
            'https://www.qq.com',
            'https://www.163.com',
        ]
        tasks = [lambda url=url: self.load_js_use_async(url) for url in url_list]
        ret = self.aSession.run(*tasks)  # 返回的是一个HTML对象列表
        # print(ret)
        # print(ret[0].html)
        for html in ret:
            print(html)


if __name__ == '__main__':
    test = DouBanTest()
    # test.get_data_by_xpath()
    # test.get_data_by_css()
    # test.fast_get_urls()
    # test.get_data_by_re()
    # test.use_HTML()
    # test.load_js()
    # test.get_response_by_async()
    test.get_js_by_async()

【本文由 "XiaoqiangClub" 发布,2021年6月17日】

要使用的是wkhtmltopdf的Python封装——pdfkit

安装

1. Install python-pdfkit:

$ pip install pdfkit

2. Install wkhtmltopdf:

  • Debian/Ubuntu:

$ sudo apt-get install wkhtmltopdf

  • Redhat/CentOS

sudo yum intsall wkhtmltopdf

  • MacOS

brew install Caskroom/cask/wkhtmltopdf

使用

一个简单的例子:

import pdfkit

pdfkit.from_url('http://google.com', 'out.pdf')

pdfkit.from_file('test.html', 'out.pdf')

pdfkit.from_string('Hello!', 'out.pdf')

你也可以传递一个url或者文件名列表:

pdfkit.from_url(['google.com', 'yandex.ru', 'engadget.com'], 'out.pdf')

pdfkit.from_file(['file1.html', 'file2.html'], 'out.pdf')

也可以传递一个打开的文件:

with open('file.html') as f:

pdfkit.from_file(f, 'out.pdf')

如果你想对生成的PDF作进一步处理, 你可以将其读取到一个变量中:

# 设置输出文件为False,将结果赋给一个变量

pdf = pdfkit.from_url('http://google.com', False)

你可以制定所有的 wkhtmltopdf 选项 http://wkhtmltopdf.org/usage/wkhtmltopdf.txt. 你可以移除选项名字前面的 '--' .如果选项没有值, 使用None, False or * 作为字典值:

options = {

'page-size': 'Letter',

'margin-top': '0.75in',

'margin-right': '0.75in',

'margin-bottom': '0.75in',

'margin-left': '0.75in',

'encoding': "UTF-8",

'no-outline': None

}

pdfkit.from_url('http://google.com', 'out.pdf', options=options)

默认情况下, PDFKit 将会显示所有的 wkhtmltopdf 输出. 如果你不想看到这些信息,你需要传递一个 quiet 选项:

options = {

'quiet': ''

}

pdfkit.from_url('google.com', 'out.pdf', options=options)

由于wkhtmltopdf的命令语法 , TOC 和 Cover 选项必须分开指定:

toc = {

'xsl-style-sheet': 'toc.xsl'

}

cover = 'cover.html'

pdfkit.from_file('file.html', options=options, toc=toc, cover=cover)

当你转换文件、或字符串的时候,你可以通过css选项指定扩展的 CSS 文件。

# 单个 CSS 文件

css = 'example.css'

pdfkit.from_file('file.html', options=options, css=css)

# Multiple CSS files

css = ['example.css', 'example2.css']

pdfkit.from_file('file.html', options=options, css=css)

你也可以通过你的HTML中的meta tags传递任意选项:

body = """

<html>

<head>

<meta name="pdfkit-page-size" content="Legal"/>

<meta name="pdfkit-orientation" content="Landscape"/>

</head>

Hello World!

</html>

"""

pdfkit.from_string(body, 'out.pdf') #with --page-size=Legal and --orientation=Landscape

配置

每个API调用都有一个可选的参数。这应该是pdfkit.configuration()API 调用的一个实例. 采用configuration 选项作为初始化参数。可用的选项有:

  • wkhtmltopdf ——wkhtmltopdf二进制文件所在的位置。默认情况下pdfkit 会尝试使用which (在类UNIX系统中) 或 where (在Windows系统中)来判断
  • meta_tag_prefix -- pdfkit的前缀指定 meta tags(元标签) - 默认情况是pdfkit-

示例 :针对wkhtmltopdf不在系统路径中(不在$PATH里面)

PATH里面):

config = pdfkit.configuration(wkhtmltopdf='/opt/bin/wkhtmltopdf'))

pdfkit.from_string(html_string, output_file, configuration=config)

问题

IOError:'No wkhtmltopdf executable found':

确保 wkhtmltopdf 在你的系统路径中(PATH), 会通过 configuration进行了配置 (详情看上文描述)。 在Windows系统中使用where wkhtmltopdf命令 或 在 linux系统中使用 which wkhtmltopdf 会返回 wkhtmltopdf二进制可执行文件所在的确切位置.

  • IOError: 'Command Failed'

如果出现这个错误意味着 PDFKit不能处理一个输入。你可以尝试直接在错误信息后面直接运行一个命令来查看是什么导致了这个错误 (某些版本的 wkhtmltopdf会因为段错误导致处理失败)

  • 正常生成,但是出现中文乱码

确保两项:

1)、你的系统中有中文字体

2)、在html中加入

下面是我随便写的一个HTML表格:

<html>

<head><meta charset="UTF-8"></head>

<body>

<table width="400" border="1">

<tr>

<th align="left">Item....</th>

<th align="right">1</th>

</tr>

<tr>

<td align="left">衣服</td>

<td align="right">1.10</td>

</tr>

<tr>

<td align="left">化妆品</td>

<td align="right">.00</td>

</tr>

<tr>

<td align="left">食物</td>

<td align="right">0.40</td>

</tr>

<tr>

<th align="left">tOTAL</th>

<th align="right">01.50</th>

</tr>

</table>

</body>

</html>

下面是生成的PDF截图

用正则表达式

正则表达式                                                              描述                                    匹配结果
\d+(\.\d*)?                                                       任意整数和浮点数                            0.004 2 75.

\b[^\Wa-z0-9_][^\WA-Z0-9_]*\b                                      首字母只能大写                               Boo Foo

^http:\/\/([\w-]+(\.[\w-]+)+(\/[\w-.\/\?%&=\u4e00-\u9fa5]*)?)?$      验证网址                           http://www.baidu.com/?id=1

^[\u4e00-\u9fa5]{
 0,}$                                                验证汉字                                 汉字汉字

\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*                       验证电子邮件                             example@163.com

^[1-9]([0-9]{
 16}|[0-9]{
 13})[xX0-9]$                                 验证身份证                            14525419951215445X

^13[0-9]{
 1}[0-9]{
 8}|^15[9]{
 1}[0-9]{
 8}                               验证手机号                             138459572***

^(25[0-5]|2[0-4][0-9]|[0-1]{
 1}[0-9]{
 2}|[1-9]{
 1}[0-9]{
 1}|[1-9])          验证IP                             192.168.1.1
\.(25[0-5]|2[0-4][0-9]|[0-1]{
 1}[0-9]{
 2}|[1-9]{
 1}[0-9]{
 1}|[1-9]|0)
\.(25[0-5]|2[0-4][0-9]|[0-1]{
 1}[0-9]{
 2}|[1-9]{
 1}[0-9]{
 1}|[1-9]|0)
\.(25[0-5]|2[0-4][0-9]|[0-1]{
 1}[0-9]{
 2}|[1-9]{
 1}[0-9]{
 1}|[0-9])$	

^[a-zA-Z0-9]+([a-zA-Z0-9\-\.]+)?\.s|)$                                验证域名                                baidu.com

^([a-zA-Z]\:|\\)\\([^\\]+\\)*[^\/:*?"<>|]+\.txt(l)?$               验证文件路径	                             C:\user\wo

<(.*)>(.*)<\/(.*)>|<(.*)\/>                                        HTML标签匹配(需修改)                          xxxx

一,必备知识

1.修饰符(flag)

re.I  使匹配对大小写不敏感
re.L  做本地化识别匹配
re.M  多行匹配,影响^和$
re.S  使.匹配包括换行在内的所有字符
re.U  根据Unicode字符集解析字符.这个标志影响\w \W \b \B
re.X  该标志通过给予你更灵活的格式以便你将正则表达式写的更易于理解.

# 例子 result = re.match('^The.*?(\d+).*?phone.', content, re.S)

2.匹配模式

^          匹配字符串开头
$          匹配字符串结尾
.          匹配人以字符,除了换行符号.当re.DOTAALL标记被指定时,则可以匹配包括换行符的任意字符.
[...]      用来表示一组字符,单独列出:[amk]匹配a,m或k
[^...]     不在[]中的字符:[^amk]匹配除amk之外的字符
re*        匹配0个或多个的表达式
re+        匹配1个或多个的表达式
re?        匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式.
re{
 n}      精准匹配n个前面表达式
re{
 n,}     匹配大于等于n个前面表达式
re{
 n,m}    匹配n到m个前面的表达式定义的片段,贪婪方式
a|b        匹配a或b
(re)       对正则表达式分组,并记住匹配的文本
(?imx)     正则表达式包含三种可选标志,imx,只影响括号中的区域.
(?-imx)    正则表达式关闭imx可选标志,只影响括号中的区域.
(?:re)     类似(...)但不表示一个组
(?imx:re)  在括号中使用imx可选标志
(?-imx:re) 在括号中不是用imx可选标志
(?#...)    注释
(?=re)     前向肯定界定符.如果所含正则表达式,以...表示,在当前位置成功匹配时成功,否则失败.但一旦所含表达式已经尝试,匹配引擎根本没有提高,模式的剩余部分还要尝试界定符右边.
(?!re)     前向否定界定符.与肯定界定符相反;当所含的表达式不能在字符串当前位置匹配成功时成功.
(?>re)     匹配的独立模式,省去回朔.
\w         匹配字符数字以及下划线
\W         匹配非字母数字下划线
\s         匹配任意空白字符,等价于[\t\n\r\f]
\S         匹配任意非空白字符
\d         匹配任意数字
\D         匹配任意非数字
\A         匹配字符串开始
\Z         匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串.
\z         匹配字符串结束
\G         匹配最后匹配完成的位置
\b         匹配一个单词边界,也就是指单词和空格之间的位置
\B         匹配非单词边界
\n \t      匹配一个换行符,一个制表符
\1...\9    匹配第n个分组的内容

3. r的作用

  • r’(\w+) (\w+)(?P.*)’, 字符串之前的r 可以避免 转行符 混淆

正则表达式使用 反斜杠 来表示特殊形式,或者把特殊字符 转义成普通字符

反斜杠 在普通的 Python 字符串 里也有 相同的作用 ,所以就产生了 冲突

解决办法是对于正则表达式样式使用 Python 的原始字符串表示法;在带有 ‘r’ 前缀的字符串字面值中, 反斜杠不必做任何特殊处理

import re

if re.search(r'\\d', 'I have one nee\dle') is not None:
    print('match it',re.search(r'\\d', 'I have one nee\dle').group(0))
else:
    print('not match')

# match it \d

4. 贪婪与非贪婪—通配符后面加?

  • 在贪婪匹配下,.* 会 匹配尽可能多的字符
  • 正则表达式中.*后面是\d+,也就是至少一个数字,并没有指定具体多少个数字,因此,.*就尽可能匹配多的字符,这里就把12345匹配了,给\d+留下个可满 足条件的数字 6.最后得到的内容就只有数字6了
import re

content = 'The 123456 is my one phone number.'
print('贪婪匹配:')
result = re.match(r'^The.*(\d+).*', content) #使用match匹配, 第一个参数为正则表达式, 第二个为要匹配的字符串
print(result.group()) #输出匹配内容
print('result = %s'%result.group(1)) #输出第一个被()包裹的内容

print('-'*20)

print('非贪婪匹配:')
result = re.match(r'^The.*?(\d+).*', content) 
print(result.group())
print('result = %s'%result.group(1))

# 贪婪匹配:
# The 123456 is my one phone number.
# result = 6
# --------------------
# 非贪婪匹配:
# The 123456 is my one phone number.
# result = 123456

5.转义匹配的使用

  • 由于()属于正则表达式的特殊字符, 因此在需要匹配()时, 需要加上转义字符\.
import re

content = '(百度)www.baidu.com'
result = re.match('(百度)www.baidu.com', content)
result2 = re.match('\(百度\)www\.baidu\.com', content)
if result:
    print(result.group())
else:
    print('result = None')
if result2:
    print(result2.group())
else:
    print('result2 = None')

# result = None
# (百度)www.baidu.com

6. group(num = 0)和groups( )

  • group 函数有一个int类型的参数,参数为0表示返回正则匹配的字符串,参数为1返回正则中第一个组匹配的内容,2返回第二组的内容一次类推
  • groups 函数是所有group函数结果组成一个元组。

二,函数应用

1. re.compile(pattern,flags=0)—自定义匹配模板

  • 他的第一个参数是正则字符串,第二个参数是修饰符
  • 编译成一个正则表达式对象,可以用于匹配match/search/findall 方法 序列
  • 在需要匹配相同正则表达式情况下, 事先定义一个compile可以简化代码量, 同时compile中也可以使用修饰符r.S等.
import re

content1 = '2016-1-1 12:01'
content2 = '2017-1-1 12:02'
content3 = '2018-1-1 12:03'

pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern, '', content1)
result2 = re.sub(pattern, '', content2)
result3 = re.sub(pattern, '', content3)
print(result1, result2, result3)

#2016-1-1  2017-1-1  2018-1-1 

2. re.match(pattern,string,flags=0)—从第一个字符开始匹配

  • 该函数从第 一个字符 开始匹配, 如果第一个对不上,就不可能匹配到数据
  • 从第一个字符开始进行匹配,不成功则返回None。
  • 一个正则表达式中若有多个()可用group方法输出,比如:ex_re=re.match(‘(.?)actor(.?)$’),可用ex_re.group()访问生成的列表,可用ex_re.group(0)访问第一个括号里的元素。
  • pettern 就是正则字符串,如果是通过re.compile方法生成的正则对象.match来调用的话,就不需要这个参数了,因为正则对象本身就代表了一个正则匹配模式。
  • string 就是要进行匹配的目标字符串
  • flags 就是正则的修饰符,比如 re.I
import re
regex = '(foo\w)(\w)'
m = re.match(r'(foo\w)(\w)','fooasdfooasd')
if m is not None:
    print(m.group(1))
    print(m.groups())

#输出
#fooa
#('fooa', 's')

3. re.search(pattern,string,flags=0)—不固定开始,但是只匹配第一个

  • re.search()并不要求必须从字符串的开头进行匹配如match(),也就是说,正则表达式可以是字符串的一部分。
  • 用法基本与match相同,只不过是扫描整个字符串,从 一开始匹配到的地方 开始。
import re
regex = '<(.*)>(.*)<\/(.*)>|<(.*)\/>'
m = re.search(regex,"aa<a>aaaa</a>")
#一样只有search能匹配到标签
if m is not None:
    print(m)
    print(m.group())

# 结果
# <re.Match object; span=(2, 13), match='<a>aaaa</a>'>
# <a>aaaa</a>

4. re.findall(pattern, string,[, flags])—匹配所有数据

  • match()和search()都是返回匹配到的 第一个内容就结束匹配 , findall()是返回 所有 符合匹配规则的内容
  • 搜索字符串内,以列表形式返回全部能匹配的子串。
import re
text = "I am so happy! "
array1 = re.findall("[a-zA-Z]", text)
array2 = re.findall("[a-zA-Z]+", text)
print(array1)
print(array2)

#结果
# ['I', 'a', 'm', 's', 'o', 'h', 'a', 'p', 'p', 'y']
# ['I', 'am', 'so', 'happy']

5. finditer(pattern, string,[,flags] )—返回的是匹配到的迭代器对象

  • 与findall()相同,但返回的是一个迭代器。对于每一次匹配,迭代器都能返回一个匹配对象
import re
regex = 'apple'
m = [ g.group() for g in re.finditer(regex,'apple1 apple2 apple3')]
print(m)
#输出
#['apple', 'apple', 'apple']

6. split(pattern, string, max = 0)

  • 根据正则表达式的模式分隔符,split函数将字符串分割为列表,返回匹配列表,分割最多操作max次
import re
list = [
'aaa,bbb,ccc',
'ddd,eee,fff',
]
for i in list:
    print(re.split(',',i))


# ['aaa', 'bbb', 'ccc']
# ['ddd', 'eee', 'fff']

7. re.sub(pattern, repl, string, count=0, flags=0)—可正则的字符串替换

  • 替换字符串中的某些子串,可以用正则表达式来匹配被选子串。pattern:表示正则表达式中的模式字符串;
  • repl:被替换的字符串(既可以是字符串,也可以是函数);
  • string:要被处理的,要被替换的字符串;
  • count:匹配的次数, 默认是全部替换
import re
st = "hello 2019"
st = re.sub("([0-9]+)","yudengwu",st)
print(st)

#结果
# hello yudengwu

8. str.replace(old, new,max)—不是re函数,仅用作字符串替换

  • 替代字符串中的某一些子串为另一些字符。
  • old – 将被替换的子字符串。
  • new – 新字符串,用于替换old子字符串。
  • max – 可选字符串, 替换不超过 max 次