当前位置:首页 > 教育

requests库 吐血总结,Python Requests库使用指南

源代码/代码和艺术& amp编程禅师

请求库用于在Python中发出标准的HTTP请求。它将请求背后的复杂性抽象成一个美丽而简单的API,这样您就可以专注于与服务交互和在应用程序中使用数据。

在本文中,您将看到请求提供的一些有用的功能,以及如何针对您可能遇到的不同情况定制和优化这些功能。您还将学习如何有效地使用请求,以及如何防止对外部服务的请求降低应用程序的速度。

在本教程中,您将学习如何:

使用常见的HTTP方法发送请求定制你的请求头和数据,使用查询字符串和消息体检查你的请求和响应的数据发送带身份验证的请求配置你的请求来避免阻塞或减慢你的应用程序

虽然我尽量包含尽可能多的信息来理解本文包含的函数和示例,但是阅读本文需要对HTTP有基本的了解。

现在让我们了解更多关于如何在您的应用程序中使用请求!

开始处理请求

让我们先安装请求库。为此,请运行以下命令:

pip install requests

如果您喜欢使用Pipenv来管理Python包,您可以运行以下命令:

pipenv install requests

安装请求后,您可以在应用程序中使用它。像这样导入请求:

import requests

现在您已经准备好结束了,是时候开始使用请求之旅了。你的第一个目标是学习如何提出GET请求。

获取请求

HTTP方法,如GET和POST,决定了在发出HTTP请求时尝试什么操作。除了GET和POST之外,还有其他常用的方法,您将在本教程的后面使用。

GET是最常见的HTTP方法之一。GET方法指示您正在尝试从指定的资源获取或检索数据。要发送GET请求,请调用requests.get()。

您可以通过以下方式向GitHub的根REST API发送GET请求:

>>> requests.get( https://api.github.com )<Response [200]>

恭喜!你提出了第一个要求。接下来,让我们仔细看看对这个请求的响应。

反应

响应是检查请求结果的强大对象。让我们再次发出相同的请求,但是这次将返回值存储在一个变量中,以便您可以仔细查看它的属性和方法:

>>> response = requests.get( https://api.github.com )

在本例中,您捕获get()的返回值(它是响应的一个实例),并将其存储在一个名为Response的变量中。现在,您可以使用响应来查看关于GET请求结果的所有信息。

状态代码

您可以从响应中获得的第一条信息是状态代码。状态代码显示您的请求的状态。

例如,200OK表示您的请求成功,而404NOT FOUND表示找不到您要寻找的资源。还有许多其他状态代码可以为您提供关于您的请求发生了什么的特定信息。

您可以通过访问查看服务器返回的状态代码。状态代码:

>>> response.status_code200

。status_code返回200,这意味着您的请求成功,服务器返回了您请求的数据。

有时,您可能希望在代码中使用这些信息来做出判断:

if response.status_code == 200:print( Success! )elif response.status_code == 404:print( Not Found. )

按照这个逻辑,如果服务器返回状态码200,你的程序就会打印Success!如果结果是404,您的程序将打印“未找到”。

请求为您进一步简化了这一过程。如果在条件表达式中使用响应实例,当状态代码在200和400之间时,它将被评估为真,否则它将为假。

因此,您可以通过重写if语句来简化前面的示例:

if response:print( Success! )else:print( An error has occurred. )

技术细节:因为__bool__()是Response上的重载方法,所以建立了真值检验。

这意味着重新定义响应的默认行为,它用于在确定对象的真实值时考虑状态代码。

请记住,此方法不验证状态代码是否等于200。原因是200到400范围内的其他状态代码,如204无内容和304未修改,在某种意义上也被认为是成功的响应。

例如,204告诉您响应成功,但是在下面的消息体中没有返回任何内容。

因此,如果您通常想知道请求是否成功,请确保使用这个方便的缩写,然后在必要时根据状态代码适当地处理响应。

假设您不想在if语句中检查响应的状态代码。相反,如果请求不成功,你要抛出异常。您可以使用。raise_for_status():

import requestsfrom requests.exceptions import HTTPErrorfor url in [ https://api.github.com , https://api.github.com/invalid ]:try:response = requests.get(url)# If the response was successful, no Exception will be raisedresponse.raise_for_status()except HTTPError as http_err:print(f HTTP error occurred: {http_err} ) # Python 3.6except Exception as err:print(f Other error occurred: {err} ) # Python 3.6else:print( Success! )

如果你打电话。对于某些状态代码,将引发HTTPError异常。如果状态代码指示请求成功,程序将继续运行,不会抛出异常。

进一步阅读:如果你不熟悉Python 3.6的f-strings,我建议你使用它们,因为它们是简化字符串格式的好方法。

现在,您已经非常了解如何处理从服务器返回的响应的状态代码。然而,当您发出GET请求时,您很少只关心响应的状态代码。平时,你想多看。接下来,您将看到如何在响应正文中查看服务器返回的实际数据。

回应内容

GET请求的响应通常在消息体中有一些有价值的信息,称为有效载荷。使用响应的属性和方法,您可以查看各种格式的有效负载。

要以字节格式查看响应的内容,可以使用。内容:

>>> response = requests.get( https://api.github.com )>>> response.contentb {"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","notifications_url":"https://api.github.com/notifications","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_url":"https://api.github.com/orgs/{org}","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","team_url":"https://api.github.com/teams","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}

虽然。内容允许您访问响应有效负载的原始字节,您通常希望使用UTF-8和其他字符编码将它们转换成字符串。当你来访时。text,response将为您完成此任务:

>>> response.text{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}"...}"}

因为将字节解码为字符串需要编码格式,如果您不指定它,请求将尝试根据响应头猜测编码格式。您也可以通过显式设置编码。访问前编码。文本:

>>> response.encoding = utf-8 # Optional: requests infers this internally>>> response.text{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}"...}"}

如果你看看响应,你会发现它实际上是序列化的JSON内容。要获取词典内容,您可以使用。text获取字符串,json.loads()反序列化。然而,完成这项任务的一个更简单的方法是使用。json():

>>> response.json(){ current_user_url : https://api.github.com/user , current_user_authorizations_html_url : https://github.com/settings/connections/applications{/client_id} ...} }

的返回值的类型。json()是字典类型,所以您可以通过键值对来访问对象中的值。

您可以使用状态代码和消息正文做许多事情。但是,如果您需要更多信息,例如关于响应本身的元数据,您需要查看响应头。

应答标题

响应头可以为您提供有用的信息,例如响应负载的内容类型和缓存响应的时间限制。要查看这些标题,请访问。标题:

>>> response.headers{ Server : GitHub.com , Date : Mon, 10 Dec 2018 17:49:54 GMT , Content-Type : application/json; charset=utf-8 ,...}

。标头返回类似字典的对象,允许您使用键来获取标头中的值。例如,要查看响应负载的内容类型,您可以访问内容类型:

>>> response.headers[ Content-Type ]application/json; charset=utf-8

然而,这个像字典一样的标题对象有一些特殊的功能。HTTP规范定义了标头不区分大小写,这意味着我们可以访问这些标头信息,而不用担心它们的大小写:

>>> response.headers[ content-type ]application/json; charset=utf-8

无论您使用内容类型还是内容类型,都会得到相同的值。

现在,您已经学习了响应的基础知识。你已经看到了它最有用的属性和方法。让我们后退一步,看看在定制GET请求时,您的响应是如何变化的。

查询字符串参数

自定义GET请求的一种常见方式是通过URL中的查询字符串参数传递值。若要使用get()执行此操作,请将数据传递给params。例如,您可以使用GitHub的搜索应用编程接口来查找请求库:

import requests# Search GitHub s repositories for requestsresponse = requests.get(https://api.github.com/search/repositories ,params={ q : requests+language:python },)# Inspect some attributes of the `requests` repositoryjson_response = response.json()repository = json_response[ items ][0]print(f Repository name: {repository["name"]} ) # Python 3.6+print(f Repository deion: {repository["deion"]} ) # Python 3.6+

通过将字典{q :requests+language:python}传递给的params参数,可以修改从Search API返回的结果。get()。

您可以像刚才一样,以字典或元组列表的形式将参数传递给get()。

>>> requests.get(... https://api.github.com/search/repositories ,... params=[( q , requests+language:python )],... )<Response [200]>

您甚至可以将字节作为值传递:

>>> requests.get(... https://api.github.com/search/repositories ,... params=b q=requests+language:python ,... )<Response [200]>

查询字符串对于参数化的GET请求很有用。您也可以通过添加或修改已发送请求的标题来自定义您的请求。

请求标题

要自定义请求头,可以使用headers参数将由HTTP头组成的字典传递给get()。例如,您可以通过在“接受”中指定文本匹配媒体类型来更改以前的搜索请求,以突出显示结果中的匹配搜索词:

import requestsresponse = requests.get(https://api.github.com/search/repositories ,params={ q : requests+language:python },headers={ Accept : application/vnd.github.v3.text-match+json },)# View the new `text-matches` array which provides information# about your search term within the resultsjson_response = response.json()repository = json_response[ items ][0]print(f Text matches: {repository["text_matches"]} )

Accept告诉服务器您的应用程序可以处理哪些内容类型。当您想要突出显示匹配的搜索词时,可以使用application/vnd。GitHub.v3.text-match+JSON,是GitHub专有的Accept头,内容是特殊的JSON格式。

在您了解更多定制请求的方法之前,让我们通过探索其他HTTP方法来开阔视野。

其他HTTP方法

除了GET,其他流行的HTTP方法还包括POST、put、DELETE、HEAD、PATCH和OPTIONS。Requests为每个HTTP方法提供一个方法,其结构类似get()`:

>>> requests.post( https://httpbin.org/post , data={ key : value })>>> requests.put( https://httpbin.org/put , data={ key : value })>>> requests.delete( https://httpbin.org/delete )>>> requests.head( https://httpbin.org/get )>>> requests.patch( https://httpbin.org/patch , data={ key : value })>>> requests.options( https://httpbin.org/get )

调用每个函数,使用相应的http方法向HTTPbin服务发送请求。对于每种方法,您可以像以前一样查看其响应:

>>> response = requests.head( https://httpbin.org/get )>>> response.headers[ Content-Type ]application/json>>> response = requests.delete( https://httpbin.org/delete )>>> json_response = response.json()>>> json_response[ args ]{}

每个方法的响应都会返回头、响应体、状态码等等。接下来,您将学习更多关于POST、‘put’和PATCH方法的知识,并了解它们与其他请求类型的区别。

消息正文

根据HTTP规范,POST、put和不太常见的PATCH请求通过消息体传递数据,而不是通过查询字符串参数。对于请求,您将有效负载传递给相应函数的数据参数。

数据接收字典、元组列表、字节或类文件对象。您需要将请求正文中发送的数据调整为您所交互的服务的特定格式。

例如,如果您请求的内容类型是application/x-www-form-URL encoded,您可以将表单数据作为字典发送:

>>> requests.post( https://httpbin.org/post , data={ key : value })<Response [200]>

您也可以将相同的数据作为元组列表发送:

>>> requests.post( https://httpbin.org/post , data=[( key , value )])<Response [200]>

但是,如果需要发送json数据,可以使用JSON参数。当您通过json传递JSON数据时,请求将序列化您的数据,并为您添加正确的Content-Type头。

《Httpbin.org》是肯尼斯·雷茨创作的一个很好的资源,《请求》的作者。它是一种接收测试请求并响应相关请求数据的服务。例如,您可以使用它来检查基本的开机自检请求:

>>> response = requests.post( https://httpbin.org/post , json={ key : value })>>> json_response = response.json()>>> json_response[ data ]{"key": "value"}>>> json_response[ headers ][ Content-Type ]application/json

您可以从响应中看到,当您发送请求时,服务器收到了请求数据和标头。请求还以准备请求的形式向您提供这些信息。

检查您的请求

当您发出请求时,请求库会在将请求实际发送到目标服务器之前准备好请求。请求准备包括身份验证头信息和序列化JSON内容等内容。

您可以通过访问来查看PreparedRequest。请求:

>>> response = requests.post( https://httpbin.org/post , json={ key : value })>>> response.request.headers[ Content-Type ]application/json>>> response.request.urlhttps://httpbin.org/post>>> response.request.bodyb {"key": "value"}

通过检查PreparedRequest,您可以访问关于正在进行的请求的各种信息,例如有效负载、网址、报头信息、身份验证等。

到目前为止,您已经发送了许多不同类型的请求,但是它们都有一个共同点:它们是对公共API的未经身份验证的请求。您遇到的许多服务可能希望您以某种方式进行身份验证。

证明

身份验证有助于服务知道您是谁。通常,您通过将数据传递给服务定义的授权头信息或自定义头信息来为服务器提供凭据。您在这里看到的所有请求函数都提供了一个名为auth的参数,它允许您传递凭据。

一个需要认证的应用编程接口例子是GitHub的认证用户应用编程接口。此端点提供有关已验证用户配置文件的信息。要向AuthenticatedUserAPI发送请求,您可以通过您的GitHub用户名和密码来获取元组中的():

>>> from getpass import getpass>>> requests.get( https://api.github.com/user , auth=( username , getpass()))<Response [200]>

如果您在元组中传递给auth的凭据有效,则请求成功。如果您尝试在没有凭据的情况下发出此请求,您将看到状态代码为401未经授权:

>>> requests.get( https://api.github.com/user )<Response [401]>

当您以元组的形式将用户名和密码传递给身份验证参数时,rqeuests将使用HTTP的基本访问身份验证方案来应用凭据。

因此,您可以通过使用HTTPBasicAuth传递显式基本身份验证凭据来发出相同的请求:

>>> from requests.auth import HTTPBasicAuth>>> from getpass import getpass>>> requests.get(... https://api.github.com/user ,... auth=HTTPBasicAuth( username , getpass())... )<Response [200]>

虽然您不需要显式执行基本身份验证,但您可能希望使用其他方法进行身份验证。请求提供了其他现成的身份验证方法,例如HTTPDigestAuth和HTTPProxyAuth。

您甚至可以提供自己的身份验证机制。为此,您必须首先创建AuthBase的子类。然后,实现__call __():

import requestsfrom requests.auth import AuthBaseclass TokenAuth(AuthBase):"""Implements a custom authentication scheme."""def __init__(self, token):self.token = tokendef __call__(self, r):"""Attach an API token to a custom auth header."""r.headers[ X-TokenAuth ] = f {self.token} # Python 3.6+return rrequests.get( https://httpbin.org/get , auth=TokenAuth( 12345abcde-token ))

这里,您的自定义令牌验证接收一个令牌,然后将该令牌包含在您的请求头的X-令牌验证头中。

错误的身份验证机制可能会导致安全漏洞。因此,除非服务出于某种原因需要自定义身份验证机制,否则您总是希望使用经过身份验证的身份验证方案,如基本或OAuth。

在考虑安全性时,让我们考虑使用请求来处理SSL证书。

SSL证书验证

每当您试图发送或接收的数据敏感时,安全性都很重要。通过HTTP与站点安全通信的方式是使用SSL建立加密连接,这意味着验证目标服务器的SSL证书非常重要。

好消息是,默认情况下,请求会为您执行此操作。但是,在某些情况下,您可能希望改变这种行为。

如果要禁用SSL证书验证,请将“假”传递给请求函数的验证参数:

>>> requests.get( https://api.github.com , verify=False)InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warningsInsecureRequestWarning)<Response [200]>

当您发出不安全的请求时,请求甚至会发出警告来帮助您保护数据。

表演

使用请求时,尤其是在生产应用程序环境中,考虑性能影响非常重要。超时控制、会话和重试限制可以帮助您保持应用程序平稳运行。

超时控制

当您向外部服务发出请求时,系统需要等待响应才能继续。如果您的应用程序等待响应的时间过长,它可能会阻止您的服务请求,您的用户体验可能会受到影响,或者您的后台工作可能会挂起。

默认情况下,请求会无限期等待响应,所以您应该总是指定一个超时来防止这些事情发生。要设置请求的超时,请使用超时参数。超时可以是整数或浮点数,表示超时前等待响应的秒数:

>>> requests.get( https://api.github.com , timeout=1)<Response [200]>>>> requests.get( https://api.github.com , timeout=3.05)<Response [200]>

在第一个请求中,请求将在1秒后超时。在第二个请求中,请求将在3.05秒后超时。

您也可以将元组传递给超时。第一个元素是连接超时(它允许客户端与服务器建立连接),第二个元素是读取超时(一旦您的客户端建立了连接并等待响应):

>>> requests.get( https://api.github.com , timeout=(2, 5))<Response [200]>

如果请求它在2秒内建立连接并在5秒内接收数据,响应将按原样返回。如果请求超时,该函数将引发超时异常:

import requestsfrom requests.exceptions import Timeouttry:response = requests.get( https://api.github.com , timeout=1)except Timeout:print( The request timed out )else:print( The request did not time out )

您的程序可以捕捉超时异常并做出相应的响应。

会话对象

到目前为止,您一直在处理高级请求API,比如get()和post()。这些函数是您发出请求时发生的事情的抽象。它们隐藏了实现细节,比如如何管理连接,这样你就不用担心它们了。

在这些抽象下有一个名为会话的类。如果需要微调请求控制或提高请求性能,可能需要直接使用会话实例。

会话用于跨请求保留参数。例如,如果要在多个请求中使用相同的身份验证,可以使用会话:

import requestsfrom getpass import getpass# By using a context manager, you can ensure the resources used by# the session will be released after usewith requests.Session() as session:session.auth = ( username , getpass())# Instead of requests.get(), you ll use session.get()response = session.get( https://api.github.com/user )# You can inspect the response just like you did beforeprint(response.headers)print(response.json())

每次使用会话发出请求时,一旦用身份验证凭据初始化,凭据就会被保留。

会话的主要性能优化以持久连接的形式出现。当您的应用程序使用会话与服务器建立连接时,它会将连接保留在连接池中。当您的应用程序想要再次连接到同一个服务器时,它将重用池中的连接,而不是建立新的连接。

最大重试次数

当请求失败时,您可能希望应用程序重试同一请求。但是,默认情况下,请求不会为您这样做。要应用此功能,您需要实现一个自定义传输适配器。

使用TransportAdapters,您可以为与其交互的每个服务定义一组配置。例如,假设您希望对https://api.github.com的所有请求在最终抛出Connecti之前重试三次。您将构建一个传输适配器,设置其最大重试次数参数,并将其加载到现有会话中:

import requestsfrom requests.adapters import HTTPAdapterfrom requests.exceptions import Connectigithub_adapter = HTTPAdapter(max_retries=3)session = requests.Session()# Use `github_adapter` for all requests to endpoints that start with this URLsession.mount( https://api.github.com , github_adapter)try:session.get( https://api.github.com )except Connecti as ce:print(ce)

当您将HTTPAdapter(github_adapter)装载到会话时,会话将遵循它对https://api.github.com的每个请求的配置。

超时、传输适配器和会话用于保持代码高效和应用程序健壮。

摘要

您在学习Python中强大的请求库方面已经取得了长足的进步。

您现在可以:

使用各种不同的HTTP方法发出请求,例如GET,POST和PUT通过修改请求头,身份验证,查询字符串和消息体来自定义你的请求检查发送到服务器的数据以及服务器发回给你的数据使用SSL证书验证高效的使用 requests 通过使用 max_retries, timeout, Sessions 和 TransportAdapters

因为您已经学会了如何使用请求,所以您可以使用它们提供的迷人数据来探索广泛的网络服务世界并构建优秀的应用程序。

1.《requests库 吐血总结,Python Requests库使用指南》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《requests库 吐血总结,Python Requests库使用指南》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/jiaoyu/1296290.html

上一篇

乒乓球亚锦赛爆冷 乒乓球亚锦赛爆冷 赢了马龙的韩国选手竟是中国人?

下一篇

袁卫华 首位落马的中央巡视组长 重蹈部级打虎干将覆辙

螺丝滑丝 两招对付滑丝螺丝的方法,效果不错!

  • 螺丝滑丝 两招对付滑丝螺丝的方法,效果不错!
  • 螺丝滑丝 两招对付滑丝螺丝的方法,效果不错!
  • 螺丝滑丝 两招对付滑丝螺丝的方法,效果不错!

滑丝 两招对付滑丝螺丝的方法,效果不错!

  • 滑丝 两招对付滑丝螺丝的方法,效果不错!
  • 滑丝 两招对付滑丝螺丝的方法,效果不错!
  • 滑丝 两招对付滑丝螺丝的方法,效果不错!

西服搭配 玩转西装混搭 记住四种方法

  • 西服搭配 玩转西装混搭 记住四种方法
  • 西服搭配 玩转西装混搭 记住四种方法
  • 西服搭配 玩转西装混搭 记住四种方法

王芳主持的节目 《中国诗词大会》火了 主持人王芳教你最好的方法读唐诗

央视第一个全民参加的诗歌节目《中国诗歌大会》,一炮而红!繁荣结束后,公众对吴亦舒、姜闻页、陈多儿等才女的欣赏,同时又重新激活了她们对中国古典诗词歌赋的兴趣。北京电视台主持人王芳最近出版了一本新书《读唐诗的最佳途径》。她希望通过分享她的经历,让更多的孩子热爱并阅读诗歌。在书中王芳...

主持人王芳 《中国诗词大会》火了 主持人王芳教你最好的方法读唐诗

央视第一个全民参加的诗歌节目《中国诗歌大会》,一炮而红!繁荣结束后,公众对吴亦舒、姜闻页、陈多儿等才女的欣赏,同时又重新激活了她们对中国古典诗词歌赋的兴趣。北京电视台主持人王芳最近出版了一本新书《读唐诗的最佳途径》。她希望通过分享她的经历,让更多的孩子热爱并阅读诗歌。在书中王芳...

淡奶油打过头变稀了补救方法

淡奶油打过头变稀了补救方法

淡奶油打完后变稀。可以选择加入奶粉和糖,低速慢慢打,可以省去稀奶油。鲜奶油也可以放在煤气炉上,用小火加热,不断搅拌,直到油融化。递面霜的时候可能会有各种问题。有时候你递奶油的时候可能会有很多气孔。这时要考虑是不是过霜过多,导致面霜表面粗糙。最好控制面霜扩散的程度。奶油变光滑后,开始低速搅拌,可以一直纹理化。有...

治胃疼的妙招 治疗胃疼的最好的十种方法 治疗胃疼小妙招

  • 治胃疼的妙招 治疗胃疼的最好的十种方法 治疗胃疼小妙招
  • 治胃疼的妙招 治疗胃疼的最好的十种方法 治疗胃疼小妙招
  • 治胃疼的妙招 治疗胃疼的最好的十种方法 治疗胃疼小妙招

女士喷香水的正确方法 怎样喷香水,教你喷香水的正确部位和注意事项

  • 女士喷香水的正确方法 怎样喷香水,教你喷香水的正确部位和注意事项
  • 女士喷香水的正确方法 怎样喷香水,教你喷香水的正确部位和注意事项
  • 女士喷香水的正确方法 怎样喷香水,教你喷香水的正确部位和注意事项