requests v0.2.0 源码解析

发表于 2年以前  | 总阅读数:591 次

1 引言

Python 向来以库丰富而广受欢迎,而 requests 库正是其中的代表。

学习 request 库有助于理解 HTTP 请求的处理流程,包括 Web 开发、测试、爬虫等多领域中都难免用到。

本系列对 requests 库进行源码解析,本文是第一篇,讲解 v0.2.0 版本。

2 requests

2.1 介绍

requests 库是 Python 中一个用于发送 HTTP 请求的第三方库。

实际上 requests 模块封装了 Python 的标准库,对外提供了更简洁的 API。

其中老版本 requests 适用于 Python2,内部使用 urllib 与 urllib2,新版本适用于 Python3,内部使用 urllib3。

简单介绍下 urllib、urllib2 与 urllib3 三者之间的关系:

  • Python 2 urllib,Python 1.2 中引入,原生 HTTP 客户端库;
  • Python2 urllib2,Python 1.6 中引入,升级版的 urllib,兼容 urllib;
  • Python3 urllib,对 Python 2 中的 urllib 进行了重写;
  • Python3 urllib3,第三方库,与 Python 的标准库没有关系。

2.2 版本

requests 库当前最早的历史版本是 v0.2.0,其中仅包含一个 __init__.py 和 core.py 文件。

因此基本上所有的代码都在 core.py 一个文件中,并且其中仅有 405 行代码。

requests 源码解析系列将从 v0.2.0 版本开始。

如下所示,拉取代码并切换到 v0.2.0 版本。

拉取最新代码
git clone https://github.com/psf/requests.git
# 切换到 v0.2.0 版本
git checkout v0.2.0
# 进入项目目录
cd requests

2.3 工具

工欲善其事必先利其器,建议使用 vscode 并安装 git history 插件,便于进行版本代码差异对比。

git-hsitory-extension

支持切换到指定分支或标签的代码。

requests-file-history

切换到 v0.2.1 版本后,支持版本代码差异对比。

request-git-diff-v0.2.0-v0.2.1

2.4 README

README 的第一部分中作者解释了开发 requests 的原因。

由于当前现有的处理 HTTP 请求的模块不好用,因此作者决定开发一个简单好用的 HTTP 模块,具体实现基于现有的内置 HTTP 模块(如 urllib、urllib2),实际上就是封装后提供更简洁的 API。

Requests: The Simple (e.g. usable) HTTP Module
==============================================

Most existing Python modules for dealing HTTP requests are insane. I have to look up *everything* that I want to do. Most of my worst Python experiences are a result of the various built-in HTTP libraries (yes, even worse than Logging). 

But this one's different. This one's going to be awesome. And simple.

Really simple.

README 的第二部分中作者介绍了 requests 库的基本功能。

具体包括支持五种请求方法、支持请求头、支持参数、可以进行简单的 HTTP 验证。

Features
--------

- Extremely simple GET, HEAD, POST, PUT, DELETE Requests
    + Simple HTTP Header Request Attachment
    + Simple Data/Params Request Attachment
- Simple Basic HTTP Authentication
    + Simple URL + HTTP Auth Registry

2.5 History

版本信息中显示 2011-02-13 发布 v0.0.1,然后第二天 2011-02-14 发布 v0.2.0。

不过当前 github 仓库中已经找不到 v0.0.1 版本了,作者也记录下了自己的心路历程,其中也经历了沮丧。

History
-------

0.2.0 (2011-02-14)
++++++++++++++++++

* Birth!


0.0.1 (2011-02-13)
++++++++++++++++++

* Frustration
* Conception

因此,requests 中给用户提供了极简的调用 HTTP 请求的方式,而所谓极简就是相对于已有的 urllib 的方法发起请求,那么,首先对比下两种方式,体验极简的魅力。

3 API 使用

首先分别使用 urllib + urllib2 与 requests 库发起 GET / POST 请求,对比 API 的使用区别。

从中可以看到 requests 模块封装了 urllib2 库,对外提供了更简洁的 API。

然后在进入源码之前,查看 test_requests 模块中提供的测试方法,其中提供了访问源码的入口。

3.1 GET 请求

3.1.1 urllib + urllib2

处理 GET 请求,流程主要包括以下三步:

  • 调用 urllib.urlencode 方法构建请求参数
  • 调用 urllib2.urlopen 方法发起请求,返回 response 对象
  • 处理响应

请求示例如下所示。


>>> import urllib
>>> import urllib2

# 请求地址与请求参数
>>> URL = "https://sl.se"
>>> params_dict = {"lat": "35.696233", "long": "139.570431"}

# 构建请求参数,拼接查询字符串
>>> params_encode = urllib.urlencode(params_dict)
>>> URL_params = "%s?%s" % (URL, params_encode)
>>> URL_params
'https://sl.se?lat=35.696233&long=139.570431'

 # 发送请求
>>> resp = urllib2.urlopen(URL_params)
>>> resp
<addinfourl at 68331400L whose fp = <socket._fileobject object at 0x0000000004388448>>
>>> type(resp)
<type 'instance'>

# 处理响应
>>> resp.info()
<httplib.HTTPMessage instance at 0x000000000412EEC8>
>>> resp.getcode()
200
>>> resp.read()
...

注意其中 urllib2.urlopen 方法的返回值 response 对象是一个类文件对象。

3.1.2 requests

处理 GET 请求,流程主要包括以下两步:

  • 调用 requests.get 方法发送请求,返回 response 对象
  • 处理响应

请求示例如下所示。

>>> import requests
>>> resp = requests.get(URL, params_dict)
>>> resp
<Response [200]>
>>> resp.headers
{'content-length': '9951', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'x-powered-by': 'ASP.NET', 'set-cookie': 'ASP.NET_SessionId=05tybaloxfjwq4gsowdsml0c; path=/; secure; HttpOnly; SameSite=Lax', 'x-aspnet-version': '4.0.30319', 'connection': 'close', 'cache-control': 'private', 'date': 'Sat, 27 Aug 2022 12:02:16 GMT', 'x-frame-options': 'sameorigin', 'referrer-policy': 'origin-when-cross-origin', 'content-type': 'text/html; charset=utf-8'}

>>> resp.status_code
200
>>> resp.content
...

3.2 POST 请求

3.2.1 urllib + urllib2

处理 POST 请求,流程主要包括以下三步:

  • 调用 urllib.urlencode 方法构建请求参数
  • 调用 urllib2.urlopen 函数收到 response
  • 处理响应

请求示例如下所示。

>>> import urllib
>>> import urllib2

>>> url = "http://fanyi.youdao.com/translate"
>>> form_data = {
        "type":"AUTO",
        "i":"i love python",
        "doctype":"json",
        "xmlVersion":"1.8",
        "keyform":"fanyi.web",
        "ue":"utf-8",
        "action":"FY_BY_ENTER",
        "typoResult":"true"
    }

>>> data = urllib.urlencode(form_data)
>>> data
'keyform=fanyi.web&ue=utf-8&action=FY_BY_ENTER&i=i+love+python&xmlVersion=1.8&type=AUTO&doctype=json&typoResult=true'

>>> resp = urllib2.urlopen(url, data)
>>> resp.getcode()
200

不同于处理 GET 请求,urllib2 处理 POST 请求时没有拼接 URL 与查询字符串,而是将 data 参数传给 urlopen 方法,data 参数作为 request body。

原因是 urllib2 中通过 data 参数区分 GET 与 POST 请求。

3.2.2 requests

处理 POST 请求,流程主要包括以下两步:

  • 调用 requests.post 方法发送请求,返回 response 对象
  • 处理响应

请求示例如下所示。

>>> import requests
>>> resp = requests.post(url, data=form_data)
>>> resp.status_code
200

3.3 对比

3.3.1 urllib2 vs requests

简单对比 urllib + urllib2 与 requests 的 API 使用方法:

  • 构建参数,第一种需要用户调用 urllib.urlencode 方法进行参数编码,第二种不需要;
  • 请求地址,第一种需要用户按照格式拼接 URL 与查询字符串,第二种不需要;
  • 请求方法,第一种需要用户调用 urllib2.urlopen 方法打开 URL 地址,第二种直接调用 requests.get 方法即可;
  • 处理响应,第一种分别调用 .info()、.getcode()、.read() 方法获取请求头、状态码、响应正文,第二种访问 .headers、.status_code、.content 属性,见名知意。

可见,requests 封装后提供的接口相比于原生 urllib2 要好用很多,不需要人工处理参数,甚至仅调用对应的请求方法一行命令就可以完成请求的全过程。

3.3.2 requests get vs post

简单对比 requests 模块处理 GET 请求与 POST 请求的区别:

  • 请求方法,前者调用 requests.get 方法,后者调用 requests.post 方法;
  • 参数类型,前者是位置参数,后者是关键字参数,参数名是 data,这也与内部封装的 urllib2 保持一致。

具体如下所示,给对应方法传入字典类型的参数,就可以完成请求处理。

>>> resp = requests.get(URL, params_dict)
>>> resp = requests.post(url, data=form_data)

3.4 测试方法

下面介绍下源码中提供的两个测试方法,作为访问源码的入口。两者都是 GET 请求,区别在于是否存在权限认证。

3.4.1 test_HTTP_200_OK_GET

test_HTTP_200_OK_GET 方法中调用 get 方法发起 HTTP 请求,并判断返回响应的状态码。

import unittest

import requests


class RequestsTestSuite(unittest.TestCase):
   """Requests test cases."""

   def test_HTTP_200_OK_GET(self):
       r = requests.get('http://google.com')
       self.assertEqual(r.status_code, 200)

3.4.2 test_AUTH_HTTPS_200_OK_GET

访问部分网站需要用户名密码,test_AUTH_HTTPS_200_OK_GET 中存在权限认证。

def test_AUTH_HTTPS_200_OK_GET(self):
   auth = requests.AuthObject('requeststest', 'requeststest')
   url = 'https://convore.com/api/account/verify.json'
   r = requests.get(url, auth=auth)

   self.assertEqual(r.status_code, 200)

下一步分析 get 方法的源码实现。

4 requests 源码

下面以 get 请求为例讲解 requests 处理请求的流程。

4.1 get

get 方法的主要流程包括:

  • 首先创建一个 Request 对象,用于封装请求过程中全部的请求参数与响应
  • 权限认证
  • 发送请求,处理响应,最终返回 Response 对象
def get(url, params={}, headers={}, auth=None):
   """Sends a GET request. Returns :class:`Response` object.

   :param url: URL for the new :class:`Request` object.
   :param params: (optional) Dictionary of GET Parameters to send with the :class:`Request`.
   :param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
   :param auth: (optional) AuthObject to enable Basic HTTP Auth.
   """

   # 封装请求参数与响应
   r = Request()

   r.method = 'GET'
   r.url = url
   r.params = params
   r.headers = headers
   # 权限认证
   r.auth = _detect_auth(url, auth)

   # 发送请求,处理响应
   r.send()

   return r.response

其中,调用 _detect_auth 方法进行权限认证,具体将在下文中讲解。

4.2 Request

4.2.1 __init__

请求对象 Request 的构造方法中首先包括所有的请求参数,比如请求地址、请求头、请求参数等,其次还有一个实例属性用于保存响应。

class Request(objct):
   """The :class:`Request` object. It carries out all functionality of
   Requests. Recommended interface is with the Requests functions.

   """

   _METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')

   def __init__(self):
      self.url = None
      self.headers = dict()
      self.method = None
      self.params = {}
      self.data = {}
      self.response = Response()
      self.auth = None
      self.sent = False

4.2.2 setattr

类属性中保存 requests 中支持的请求方法,共五种。

_METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')

属性赋值时自动调用 __setattr__方法,其中校验传入的请求方法。

def __setattr__(self, name, value):
   if (name == 'method') and (value):
      if not value in self._METHODS:
         raise InvalidMethod()

   object.__setattr__(self, name, value)

如果传入不支持的请求方法,赋值时抛出 InvalidMethod 异常。

class InvalidMethod(RequestException):
   """An inappropriate method was attempted."""   

class RequestException(Exception):
   """There was an ambiguous exception that occured while handling your request."""

4.3 send

send 是处理请求的核心逻辑,主要流程包括:

  • 请求参数编码
  • 发送请求
  • 处理响应

send 方法中不同的请求方法对应的逻辑相同,本文主要介绍 GET 请求。

下面依次介绍 send 方法中 GET 请求的主要流程。

4.3.1 urllib.urlencode

处理 GET 请求时,如果请求参数是字典类型,调用 urllib.urlencode 方法完成参数编码,实际上就是参数格式化。

然后,在 Request 对象内部创建 _Request 对象,该对象继承 urllib2.Request 类,并将请求方法与请求参数传入。

# url encode GET params if it's a dict
if isinstance(self.params, dict):
   params = urllib.urlencode(self.params)
else:
   params = self.params

req = _Request(("%s?%s" % (self.url, params)), method=self.method)

值得一提的是与 GET 请求不同,在处理 POST 请求时,首先创建 _Request 对象,然后在调用 urllib.urlencode 方法完成参数编码以后,将请求参数保存到 _Request 对象中作为 request body。

req = _Request(self.url, method='POST')

# url encode form data if it's a dict
if isinstance(self.data, dict):
    req.data = urllib.urlencode(self.data)
else:
    req.data = self.data

4.3.2 _Request

_Request 对象中调用父类 urllib2.Request 的初始化方法,该对象用于实际的请求发送。

class _Request(urllib2.Request):
   """Hidden wrapper around the urllib2.Request object. Allows for manual
   setting of HTTP methods.
   """

   def __init__(self, url,
               data=None, headers={}, origin_req_host=None,
               unverifiable=False, method=None):
      urllib2.Request.__init__( self, url, data, headers, origin_req_host,
                          unverifiable)
      self.method = method

上面提到,urllib2 中就是通过 data 参数来区分 GET 与 POST 方法,从 urllib2.Request 类的 get_method 方法中也可以看到。

class Request:
 def get_method(self):
        if self.has_data():
            return "POST"
        else:
            return "GET"

4.3.3 urllib2.urlopen

调用 _get_opener 方法创建 opener。

opener = self._get_opener()

_get_opener 方法中首先判断是否需要验证,如果不需要验证,直接返回 urllib.urlopen 方法,否则创建自定义 opener 并返回 urllib.open 方法,返回的方法用于发起请求。

实际上,urllib.urlopen 方法内部也是调用 urllib.open 方法,因此这里看起来使用了两种方法,本质上是同一种。

def _get_opener(self):
   """ Creates appropriate opener object for urllib2.
   """

   if self.auth:

      # create a password manager
      authr = urllib2.HTTPPasswordMgrWithDefaultRealm()

      authr.add_password(None, self.url, self.auth.username, self.auth.password)
      handler = urllib2.HTTPBasicAuthHandler(authr)
      opener = urllib2.build_opener(handler)

      # use the opener to fetch a URL
      return opener.open
   else:
      return urllib2.urlopen

4.4.4 发送请求

最终发送请求是调用刚返回的 opener 方法。

resp = opener(req)

实际上,如果没有权限认证,调用 opener 方法等于调用 urllib2.urlopen 方法。

resp = urllib2.urlopen(urllib2.Reuqest())

4.4.5 处理响应

处理响应其实主要是将 _Request 对象的响应组装到 Request.response 属性中。

self.response.status_code = resp.code
self.response.headers = resp.info().dict
if self.method.lower() == 'get':
   self.response.content = resp.read()

4.5 Response

Response 类中仅有两个方法,包括构建方法与 __repr__方法。

4.5.1 __init__

Response 类中保存了需要返回的响应,其中包括响应正文、状态码、响应头。

class Response(object):
   """The :class:`Request` object. All :class:`Request` objects contain a
   :class:`Request.response <response>` attribute, which is an instance of
   this class.
   """

   def __init__(self):
      self.content = None
      self.status_code = None
      self.headers = dict()

4.5.2 __repr__

__repr__方法用于格式化输出,其中仅打印状态码。

class Response(object):

    def __repr__(self):
       try:
          repr = '<Response [%s]>' % (self.status_code)
       except:
          repr = '<Response object>'
       return repr

上面介绍 send 方法中跳过了权限认证过程,下面予以详细介绍。

4.6 权限认证

4.6.1 流程

有权限认证的 HTTP 请求示例如下所示。

>>> c_auth = requests.AuthObject('kennethreitz', 'xxxxxxx')
>>> requests.add_autoauth('https://convore.com/api/', c_auth)
>>> r = requests.get('https://convore.com/api/account/verify.json')

因此,有权限认证时的实现流程相对复杂,具体包括:

  • 实例化 requests.AuthObject,保存用户名密码;
  • 调用 requests.add_autoauth,关联 URL 与用户名密码;
  • send 方法中调用 _detect_auth 方法,判断传入的 URL 与 auth 是否已授权。

4.6.2 AuthObject

首先,实例化 AuthObject 类,保存传入的用户名密码。

class AuthObject(object):
   def __init__(self, username, password):
      self.username = username
      self.password = password

4.6.3 add_autoauth

然后,调用 requests.add_autoauth 方法,其中将 URL 与 AuthObject 对象作为元组保存在全局变量 AUTOAUTHS 中。

AUTOAUTHS = []

def add_autoauth(url, authobject):
 global AUTOAUTHS

 AUTOAUTHS.append((url, authobject))

注意测试方法 test_AUTH_HTTPS_200_OK_GET 中未调用 add_autoauth 方法。

原因是对于单次授权的场景,并不需要调用该方法中修改全局变量,也可以对某一域名进行设置,类似后期增加的 session 功能。

4.6.4 _detect_auth

send 方法中调用 _detect_auth 方法,判断用户传入的 URL 与 auth 是否已授权。

r.auth = _detect_auth(url, auth)

该方法中最终调用 _get_autoauth 方法。

def _detect_auth(url, auth):
   """Returns registered AuthObject for given url if available, defaulting to
   given AuthObject."""

   return _get_autoauth(url) if not auth else auth

4.6.5 _get_autoauth

_get_autoauth 方法中遍历全局变量 AUTOAUTHS,如果 URL 已授权,则返回注册的 auth 认证,否则返回 None 表示未授权。

def _get_autoauth(url):
   """Returns registered AuthObject for given url if available.
   """

   for (autoauth_url, auth) in AUTOAUTHS:
      if autoauth_url in url: 
         return auth

   return None

注意判断 URL 是否授权时使用的是 str.in 方法,而非等值判断,原因是当前缀(如域名)相同时,仅需要一次认证。

>>> requests.add_autoauth('https://convore.com/api/', c_auth)
>>> r = requests.get('https://convore.com/api/account/verify.json')

返回的 auth 认证保存在 Request.auth 属性中。

def get(url, params={}, headers={}, auth=None):

   # 封装请求参数与响应
   r = Request()
   ...
   # 权限认证
   r.auth = _detect_auth(url, auth)

通过认证以后, _get_opener 方法中使用到了 _get_autoauth 中返回的 auth 认证,用于 urllib2 中创建 handler。

def _get_opener(self):

   if self.auth:

      # create a password manager
      authr = urllib2.HTTPPasswordMgrWithDefaultRealm()

      authr.add_password(None, self.url, self.auth.username, self.auth.password)
      handler = urllib2.HTTPBasicAuthHandler(authr)
      opener = urllib2.build_opener(handler)

      # use the opener to fetch a URL
      return opener.open
   else:
      return urllib2.urlopen

注意 add_autoauth 方法供外部用户调用,_detect_auth 与 _get_autoauth 方法都有单前置下划线,表示是私有方法,类对象与子类可以访问。

5 结论

requests 模块封装了 Python 的标准库(Python 2:urllib 与 urllib2;Python 3:urllib3),简化了用户调用,由此可见 API 简洁的重要性。

requests 库当前最早的历史版本是 v0.2.0,其中仅包含一个 __init__.py 和 core.py 文件。

因此基本上所有的代码都在 core.py 一个文件中,并且其中仅有 405 行代码。

实际上,仅有的 405 代码也有冗余代码待优化,而如今,随着功能的丰富,requests 的代码量已经远超当初,可见迭代的重要性,代码是一步步优化出来的。

6 知识点

6.1 网络

6.1.1 URL

URL(Uniform Resource Locator,统一资源定位符)是当前最通用的网络资源定位方式。

URL 的基本格式如下所示:

<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<fragment>

其中:

  • schema / protocol,传输协议,最常见的是 HTTP 协议;
  • user,连接服务器使用的用户名;
  • password,连接服务器使用的密码;
  • host,主机名,存放资源的服务器的域名系统(DNS)主机名或 IP 地址;
  • port,端口号,省略时使用传输协议的默认端口,如 HTTP 的默认端口是 80;
  • path,路径,由零或多个“/”符号隔开的字符串,一般用来表示主机上的一个目录或文件地址;
  • params,参数,用于指定特殊参数的可选项;
  • query,查询,用于给动态网页传递参数,如果有多个参数,使用 & 符号隔开,每个参数的名与值使用 = 符号隔开;
  • fragment,信息片段,用于指定网络资源中的片段。例如一个网页中有多个名词解释,可使用 fragment 直接定位到某一名词解释。

6.2 Python

6.2.1 __setattr__

属性赋值时自动调用 __setattr__方法,其中校验传入的请求方法。

def __setattr__(self, name, value):
   if (name == 'method') and (value):
      if not value in self._METHODS:
         raise InvalidMethod()

   object.__setattr__(self, name, value)

重写 __setattr__方法的应用场景是什么呢?

如下所示,通过重写 __setattr__方法实现属性赋值时对值的验证。

class Student(object):
    def __init__(self, name):
        self.name = name

    def __setattr__(self, key, value):
        if len(value) < 3:
            raise ValueError("Name length need >= 3")


s = Student(name="To")  # ValueError

6.2.2 自定义异常类

Request 类中 __setattr__方法属性赋值时校验传入的请求方法,如果是不支持的请求方法,在赋值时抛出 InvalidMethod 异常。

注意其中自定义异常类的类体为空,而不会报错语法错误。

class InvalidMethod(RequestException):
   """An inappropriate method was attempted."""   

class RequestException(Exception):
   """There was an ambiguous exception that occured while handling your request."""

7 待办

  • urllib:opener、handler

参考教程

  • Github psf / requests
  • Github wangshunping / read_requests
  • What are the differences between the urllib, urllib2, urllib3 and requests module?
  • python的requests类库(一)requests库和urllib包对比:一个简单get请求

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/4iy7BxXWjZIA6qICjBLbIA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237268次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8108次阅读
 目录