urllib2 源码解析

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

1引言

Python 2 中的 requests 库基于 urllib2 模块实现,因此有必要了解 urllib2 模块的 API 使用与原理。

本文将结合 requests 库,详细介绍 urllib2 模块。本文所有描述基于 Python 2。

2 介绍

在使用 urllib2 的 API 之前,简单介绍其中两个重要概念,opener 与 handler。

2.1 opener

urllib2 中使用 opener 获取 URL(fetch a URL)。

调用 urllib2.urlopen 方法时创建默认 opener,也支持用户自定义。

opener 是 urllib2.OpenerDirector 实例,用于管理各种类型的 handler。

2.2 handler

opener 通过 handler 完成请求的调用。

handler 是抽象的处理者,定义了处理请求的相关接口,不同 handler 中对应各种方法的不同实现。

自定义 opener 实际上就是自定义 handler。

3 API 使用

分别使用 urllib2 的 API 完成无参 GTE 请求与传参请求。

3.1 无参 GET 请求

3.1.1 urllib2.urlopen

urllib2 对外提供 urlopen 函数接口,用于获取 URL。

因此,请求调用可以仅一步完成:

  • 调用 urllib2.urlopen 函数收到 response
>>> import urllib2
>>> response = urllib2.urlopen('http://python.org/') 

response 对象是一个类文件对象。

>>> response
<addinfourl at 81364488L whose fp = <socket._fileobject object at 0x0000000004CC71C8>>
>>> resp.fileno()
744L
>>> resp.fp
<socket._fileobject object at 0x0000000004CC71C8>

因此,调用 read 方法可以获取响应正文,调用 readline 方法可以获取到正文的第一行。

>>> html = response.read()
>>> response.readline()
'<!doctype html>\n'

示例中调用 urllib2.urlopen 一个函数就可以完成请求响应的全过程,内部封装了多层处理,当然这种默认处理并不满足全部场景,因此特殊场景下需要调用内部处理的方法,下面介绍内部处理中用到的部分方法。

3.1.2 urllib2.Request

请求调用可以分两步完成:

  • 实例化 urllib2.Request 构建 request 请求
  • 调用 urllib2.urlopen 函数收到 response

先有请求对象,后有响应对象,这样也比较合乎逻辑。

>>> request = urllib2.Request('http://python.org/') 
>>> response = urllib2.urlopen(request)

可见,urllib2.urlopen 函数的第一个参数 url 可以是 URL 地址,也可以是 Request 对象。

3.1.3 build_opener

上面提到,urllib2 中使用 opener 获取 URL,到目前为止,使用的都是默认的 opener。

默认的 handler 仅支持基本功能,不支持代理、cookie 等其他的 HTTP/HTTPS 高级功能,这种情况下可以使用自定义 handler。

自定义 handler 时,请求调用可以分四步完成:

  • 创建 handler
  • 调用 build_opener 方法创建 opener
  • 实例化 urllib2.Request 构建 request 请求
  • 调用 opener.open 函数收到 response

调用 urllib2.urlopen 一个函数完成的请求可以拆分为如下四步,作用相同。

# 构建 HTTPHandler 处理器对象,支持 HTTP 请求
>>> http_handler = urllib2.HTTPHandler()

# 创建支持处理 HTTP 请求的 opener 对象(OpenerDirector 实例对象)
>>> opener = urllib2.build_opener(http_handler)

>>> request = urllib2.Request('http://python.org/') 

# 调用自定义 opener 对象的 open 方法,发送 request 请求
>>> response = opener.open(request)

当然,实际业务中不会这样使用。通常是当默认 handler 无法满足需求时,在不改动源码的前提下通过自定义 handler 实现功能扩展。

比如测试过程中可以通过设置 debuglevel 参数开启 debug log,从而将请求处理中的收包和发包的报头打印在日志中,而不需要人为抓包。

# 默认 debuglevel=0,表示关闭 debug log
>>> http_handler = urllib2.HTTPHandler(debuglevel=1)

比较普遍的应用是在爬虫/反爬虫过程中通过自定义 handler 使用代理 IP。

requests v0.2.0 中就使用到了 build_opener 方法。

如下所示,调用 _get_opener 方法创建 opener 方法时,如果不需要验证,直接返回 urllib.urlopen 方法,否则调用 build_opener 方法创建自定义 opener 并返回 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

3.1.4 install_opener

再新增一步,请求调用可以分五步完成:

  • 创建 handler
  • 调用 build_opener 方法创建 opener
  • 调用 install_opener 函数将自定义 opener 设置为全局默认 opener
  • 实例化 urllib2.Request 构建 request 请求
  • 调用 urllib2.urlopen/opener.open 函数收到 response

示例如下所示,其中在调用 install_opener 方法后调用 urllib2.urlopen 函数发起请求。

>>> http_handler = urllib2.HTTPHandler()
>>> opener = urllib2.build_opener(http_handler)
>>> urllib2.install_opener(opener)
>>> request = urllib2.Request('http://python.org/') 
>>> response = urllib2.urlopen(request)

那么,哪种场景下需要调用 install_opener 方法呢?

如果之后全部请求都需要使用自定义代理(opener) ,可以调用 install_opener 方法将其设置为全局默认,这种情况下,无论调用 urllib2.urlopen,还是 opener.open 函数,都会使用自定义代理。不过通常不需要调用该方法。

requests v0.2.1 中就使用到了 install_opener 方法,将支持文件上传功能的两个自定义 handler 设置为默认 opener。

def register_openers():
    """Register the streaming http handlers in the global urllib2 default
    opener object.

    Returns the created OpenerDirector object."""
    handlers = [StreamingHTTPHandler, StreamingHTTPRedirectHandler]
    if hasattr(httplib, "HTTPS"):
        handlers.append(StreamingHTTPSHandler)

    opener = urllib2.build_opener(*handlers)

    urllib2.install_opener(opener)

    return opener

3.2 传参请求

3.2.1 GET

urllib2 模块完成 GET 请求可以分三步完成:

  • 调用 urllib.urlencode 方法构建请求参数
  • 将参数以查询字符串的形式拼接到 URL 后面
  • 调用 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'

 # 发送请求
>>> response = urllib2.urlopen(URL_params)

# 处理响应
>>> response.getcode()
200

3.2.2 POST

urllib2 模块完成 POST 请求可以分两步完成:

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

通过 data 参数区分 GET 与 POST 请求方法。

请求示例如下所示,注意其中没有拼接查询参数与 URL,而是将参数作为 data(body)传给 urlopen 方法。

>>> 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'

>>> request = urllib2.Request(url, data=data)
>>> response = urllib2.urlopen(request)
>>> response.getcode()
200

4 源码

4.1 urlopen

调用 urlopen 方法是使用 urllib2 模块发起请求时最简单的调用方式。

函数的入参是 URL / Request 对象和数据,出参是类文件对象 response。

_opener = None

def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
            cafile=None, capath=None, cadefault=False, context=None):
    global _opener
    ...
    if context:
        https_handler = HTTPSHandler(context=context)
        opener = build_opener(https_handler)
    elif _opener is None:
        _opener = opener = build_opener()
    else:
        opener = _opener
    return opener.open(url, data, timeout)

urlopen 中调用 build_opener 方法构建全局对象 _opener 并保存,然后调用 open 方法并传入 URL 与数据。

因此,如果程序中多次调用 urlopen,不需要重复构建 opener 对象。

如果 _opener 对象不为空,表明不是第一次调用 urlopen 或者已经调用 install_opener 方法修改了全局默认对象,不需要再构建 opener 对象。

install_opener 方法的实现非常简单,就是将自定义 handler 设置为全局默认。

def install_opener(opener):
    global _opener
    _opener = opener

从注释中也可以看到,urlopen 方法的基本用法与 urllib 库相同,其中一个区别是支持传入 request 实例。

urlopen(url, data=None) -- Basic usage is the same as original
    urllib.  pass the url and optionally data to post to an HTTP URL, and
    get a file-like object back.  One difference is that you can also pass
    a Request instance instead of URL.  Raises a URLError (subclass of
    IOError); for HTTP errors, raises an HTTPError, which can also be
    treated as a valid response.

可见,调用 urlopen 或 open 两种方法均可以完成请求的发送,不过最终都是调用 open 方法,因此 open 方法是 urllib2 的核心逻辑,将在源码解析的最后部分介绍。

下面首先介绍 build_opener 方法。

4.2 opener & handler

首先创建 handler,然后将其绑定到 opener。

4.2.1 build_opener

build_opener 方法入参是 handlers,出参都是 OpenerDirector 对象,其中可以绑定各种不同的 handler。

def build_opener(*handlers):
    """Create an opener object from a list of handlers.

    The opener will use several default handlers, including support
    for HTTP, FTP and when applicable, HTTPS.

    If any of the handlers passed as arguments are subclasses of the
    default handlers, the default handlers will not be used.
    """
    ...
    opener = OpenerDirector()

    # 默认 handler
    default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
                       HTTPDefaultErrorHandler, HTTPRedirectHandler,
                       FTPHandler, FileHandler, HTTPErrorProcessor]
    if hasattr(httplib, 'HTTPS'):
        default_classes.append(HTTPSHandler)
 ... # 对比去重

    # 将 handler 绑定到 OpenerDirector 实例
    for klass in default_classes:
        opener.add_handler(klass())

    for h in handlers:
        if isclass(h):
            h = h()
        opener.add_handler(h)

    return opener

注意 handlers 是可选参数,用于支持自定义 handler,如果没有的话,将使用默认 handler,如 HTTPHandler、HTTPSHandler。

build_opener 方法中在对比去重用户传入的自定义 handler 与默认 handler 后,统一调用 add_handler 方法将每个 handler 绑定到 OpenerDirector 实例。

如下所示,判断自定义 handler 与 默认 handler 的关系,如果前者是后者的子类或实例化对象,则删除默认 handler,以用户传入的 handler 为准。

    skip = set()
    for klass in default_classes:
        for check in handlers:
            if isclass(check):  # 判断是否是类
                if issubclass(check, klass):  # 判断是否是子类
                    skip.add(klass)
            elif isinstance(check, klass):  # 判断是否是实例
                skip.add(klass)
    for klass in skip:
        default_classes.remove(klass)  # 删除默认 handler

从注释中也可以看到,build_opener 方法用于创建 OpenerDirector 实例,支持用户自定义 handler。如果自定义 handler 是默认 handler 的子类,则以自定义 handler 为准。

build_opener -- Function that creates a new OpenerDirector instance.
Will install the default handlers.  Accepts one or more Handlers as
arguments, either instances or Handler classes that it will
instantiate.  If one of the argument is a subclass of the default
handler, the argument will be installed instead of the default.

opener 用于管理 handler,而 handler 用于完成实际的请求

opener 中会绑定多个 handler,而 opener 是 urllib2.OpenerDirector 实例,那么,OpenerDirector 中是如何管理各种 handler 的呢?

4.2.2 OpenerDirector

OpenerDirector 用于管理 handler,每种 handler 实现特定的协议,如 HTTPHandler、HTTPRedirectHandler。

The OpenerDirector manages a collection of Handler objects that do
    all the actual work.  Each Handler implements a particular protocol or
    option.  The OpenerDirector is a composite object that invokes the
    Handlers needed to open the requested URL.  For example, the
    HTTPHandler performs HTTP GET and POST requests and deals with
    non-error returns.  The HTTPRedirectHandler automatically deals with
    HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler
    deals with digest authentication.

OpenerDirector 类中有 4 个实例属性,分别管理不同类型的 handler,对应请求的不同处理过程,具体包括 open、 request、response、error。

class OpenerDirector:
    def __init__(self):
  ...
        # manage the individual handlers
        self.handle_open = {}
        self.handle_error = {}
        self.process_response = {}
        self.process_request = {}

初始化时 handle 字典为空,调用 add_handler 时会根据方法名将 handler 保存到对应的 handle 字典,字典的 value 是 handler,key 是网络协议名称。

对于一个 handler,调用 add_handler 方法时,如何判断应该保存到哪个字典呢?

handler 与字典的对应关系基于方法名的约定

比如 HTTPHandler 中 http_open 方法名表示实现了 HTTP 协议 open 过程的方法。

实际上,handler 中的方法名只有以下几类:_error_open_request_response,并约定以协议名称作为前缀。

因此,实现了哪类方法就属于哪种 handler,以哪种协议作为前缀就对应 handle 字典中的哪种协议

4.2.3 add_handler

add_handler 方法中遍历 handler 中的全部方法,分别按照_分隔符拆分方法名,获取到协议名称与请求的处理过程。

def add_handler(self, handler):
    ...
    for meth in dir(handler):
        i = meth.find("_")
        protocol = meth[:i]
        condition = meth[i+1:]

获取到协议名称和处理过程以后,就可以根据处理过程,将不同的 handler 保存到对应的 handle 字典。

        ...
        elif condition == "open":
            kind = protocol
            lookup = self.handle_open
        elif condition == "response":
            kind = protocol
            lookup = self.process_response
        elif condition == "request":
            kind = protocol
            lookup = self.process_request
        ...

到现在为止,就创建好了 handler 与 opener,下面就是调用 opener.open(url, data, timeout) 方法发起请求了。

4.3 open

open 方法中主要是分别使用 OpenerDirector 中的三个 handle 字典,包括 handle_open、process_response、process_request。

其中 process_request 用于请求预处理,process_response 用于处理响应,handle_open 用于发起请求获取响应,具体在 _open 方法中实现。

def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
    ...
    # 正则匹配 URL 识别网络协议
 protocol = req.get_type()

 # pre-process request
 meth_name = protocol+"_request"
 for processor in self.process_request.get(protocol, []):
  meth = getattr(processor, meth_name)
  req = meth(req)

 response = self._open(req, data)

 # post-process response
 meth_name = protocol+"_response"
 for processor in self.process_response.get(protocol, []):
  meth = getattr(processor, meth_name)
  response = meth(req, response)

 return response

根据 URL 的网络协议获取字典中保存的 handler,然后依次遍历对应协议的 handler 进行处理。

比如对于 request 处理,首先会从 process_request 字典中获取出可以处理 HTTP 请求的 handler,HTTP 请求默认只有一个处理器:HTTPHandler, 因此会使用 HTTPHandler 中的 http_request 方法来处理,其余两个处理过程类似。

上面提到,urllib2.urlopen 函数的第一个参数 url 可以是 URL 也可以是 Request 对象。

原因是 open 方法中首先会判断参数类型,如果是 URL,会自动创建 Request 对象。

    # accept a URL or a Request object
    if isinstance(fullurl, basestring):
        req = Request(fullurl, data)
    else:
        req = fullurl
        if data is not None:
            req.add_data(data)
    ...

4.4 _open

_open 方法中主要是调用 _call_chain 方法执行网络协议对应的 handler。

def _open(self, req, data=None):
    # 使用默认 handler
 result = self._call_chain(self.handle_open, 'default',
         'default_open', req)
 if result:
  return result

    # 使用协议对应的 handler
 protocol = req.get_type()
 result = self._call_chain(self.handle_open, protocol, protocol +
         '_open', req)
 if result:
  return result

    # 协议异常或没有协议的 handler
 return self._call_chain(self.handle_open, 'unknown',
       'unknown_open', req)

_call_chain 中最终调用 handler 中实现的方法。

4.5 _call_chain

_call_chain 方法类似职责链模式,遍历协议对应的全部 handler,查找可以处理该请求的方法,然后调用。

def _call_chain(self, chain, kind, meth_name, *args):
 # Handlers raise an exception if no one else should try to handle
 # the request, or return None if they can't but another handler
 # could.  Otherwise, they return the response.
 handlers = chain.get(kind, ())
 for handler in handlers:
  func = getattr(handler, meth_name)

  result = func(*args)
  if result is not None:
   return result

5 知识点

5.1 职责链模式

责任链模式(Chain of Responsibility)是一种设计模式。

思想是通过将多个处理器(handler)串成链处理请求,请求在链上传递,直到其中某个处理成功为止。

在实际场景中,财务审批就是一个责任链模式。假设某个员工需要报销一笔费用,审核者可以分为:

  • Manager:只能审核1000元以下的报销;
  • Director:只能审核10000元以下的报销;
  • CEO:可以审核任意额度。

用责任链模式设计此报销流程时,每个审核者只关心自己责任范围内的请求,并且处理它。对于超出自己责任范围的,扔给下一个审核者处理,这样,将来继续添加审核者的时候,不用改动现有逻辑。

职责链模式的优点是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。

类似 workflow 的流程性事务处理过程就可以通过职责链模式实现。不同场景下的业务实现对应不同的 handler,将其组合成链以后,通过提供统一入口兼容变化的需求,扩展性强。

需要注意的是链中每个 handler 的顺序很重要,顺序不对可能导致处理结果异常。

职责链模式的实现方式有多种,其中 urlib2 中 _call_chain 方法中遍历全部 handler,如果返回 None,则交给下一个 handler 处理。

def _call_chain(self, chain, kind, meth_name, *args):
 handlers = chain.get(kind, ())
 for handler in handlers:
  func = getattr(handler, meth_name)

  result = func(*args)
  if result is not None:
   return result

可见,urllib2 基于职责链模式处理请求,并对外提供接口,以支持用户自定义 handler。

因此,urllib2 可以在不修改已有代码的前提下实现功能的扩展,这也符合开闭原则的要求。

Software entities should be open for extension,but closed for modification.

5.2 urllib vs urllib2

5.2.1 urllib API

urllib2 是增强版的 urllib,因此实际上 urllib 也可以完成基础的请求调用。

如下所示,分别使用 urllib 的 API 发起 GET 与 POST 请求。

GET 请求

>>> import urllib
>>> params = urllib.urlencode({"lat": "35.696233", "long": "139.570431"})
>>> f = urllib.urlopen("https://sl.se?%s" % params)
>>> f.getcode()
200

POST 请求

>>> import urllib
>>> params = urllib.urlencode({"lat": "35.696233", "long": "139.570431"})
>>> f = urllib.urlopen("https://sl.se?", params)
>>> f.getcode()
200

5.2.2 关系

urllib2 可以认为是增强版的 urllib,如:

  • urllib.openurl 方法仅支持 URL,urllib2.openurl 同时支持 URL 与 Request 对象。因此 urllib2 中可以设置请求头,比如修改用户代理、设置 cookie;
  • urllib 仅支持自定义简单的 handler,比如不支持身份验证,urllib2 支持。

而对于 urllib 中提供的功能,urllib2 中也没有重新造轮子,如:

  • urllib.urlencode 方法用于生成查询字符串;
  • urllib.addinfourl 类用于封装响应。

如下所示,urllib2.AbstractHTTPHandler 类中将请求结果保存到 urllib.addinfourl 类,可见最终返回的 response 是 addinfourl 对象。

class AbstractHTTPHandler(BaseHandler):
    def do_open(self, http_class, req, **http_conn_args):
        ...
        from urllib import addinfourl

        resp = addinfourl(fp, r.msg, req.get_full_url())
        resp.code = r.status
        resp.msg = r.reason
        return resp

因此,Python 2 中通常 urllib 与 urllib2 两者结合使用。

5.3 正则表达式

不同的网络协议对应不同的处理过程,Request 类的 get_type 方法中通过拆分 URL 识别对应的网络协议。

    def get_type(self):
        if self.type is None:
            self.type, self.__r_type = splittype(self.__original)
            if self.type is None:
                raise ValueError, "unknown url type: %s" % self.__original
        return self.type

实际上是调用 urllib.splittype 函数,其中通过调用 re.match 方法匹配 URL 拆分网络协议与路径。

如果正则表达式不匹配,表明 URL 格式不合法,返回 None,否则返回协议名称。

_typeprog = None
def splittype(url):
    global _typeprog
    if _typeprog is None:
        import re
        _typeprog = re.compile('^([^/:]+):')

    match = _typeprog.match(url)
    if match:
        scheme = match.group(1)
        return scheme.lower(), url[len(scheme) + 1:]
    return None, url

不过如果当前 URL 对应的网络协议暂不支持会怎么处理呢?

上面提到,_open 方法中会调用三次 _call_chain 方法,分别是默认 handler、协议对应的 handler、异常 handler。当协议不支持时,对应异常 handler。

def _open(self, req, data=None):
    ...
    # 协议异常或没有协议的 handler
 return self._call_chain(self.handle_open, 'unknown',
       'unknown_open', req)

UnknownHandler 类的 unknown_open 方法中抛出 URLError 异常。

class UnknownHandler(BaseHandler):
    def unknown_open(self, req):
        type = req.get_type()
        raise URLError('unknown url type: %s' % type)

6 结论

urllib2 是增强版的 urllib,Python 2 中通常 urllib 与 urllib2 两者结合使用。

urllib2 中有两个重要概念,opener 与 handler。其中:

  • opener 是 urllib2.OpenerDirector 实例,用于管理各种类型的 handler;
  • handler 是抽象的处理者,定义了处理请求的相关接口。

urllib2 最简单的调用方式是直接调用 urllib2.urlopen 方法,其中将使用默认 opener。

urllib2 基于职责链模式处理请求,并对外提供接口,以支持用户自定义 handler。

每种 handler 对应不同的网络协议,其中可以自定义请求处理过程中的具体实现,并约定方法名的格式为【网络协议 + 处理过程】。

7 待办

  • 类文件对象

本文来自读者朋友「鸟山明」的合作投稿

参考教程

  • https://cloud.tencent.com/developer/article/1571148
  • https://zhuanlan.zhihu.com/p/421767293
  • https://blog.csdn.net/lixinkuan328/article/details/104289376
  • https://blog.csdn.net/jiduochou963/article/details/87564467
  • https://www.liaoxuefeng.com/wiki/1252599548343744/1281319474561057

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

 相关推荐

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

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

发布于: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年以前  |  237226次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8062次阅读
 目录