python异步爬虫

2023-11-15

本文主要包括以下内容           

  • 线程池实现并发爬虫
  • 回调方法实现异步爬虫
  • 协程技术的介绍
  • 一个基于协程的异步编程模型
  • 协程实现异步爬虫

线程池、回调、协程

我们希望通过并发执行来加快爬虫抓取页面的速度。一般的实现方式有三种:

  • 线程池方式:开一个线程池,每当爬虫发现一个新链接,就将链接放入任务队列中,线程池中的线程从任务队列获取一个链接,之后建立socket,完成抓取页面、解析、将新连接放入工作队列的步骤。
  • 回调方式:程序会有一个主循环叫做事件循环,在事件循环中会不断获得事件,通过在事件上注册解除回调函数来达到多任务并发执行的效果。缺点是一旦需要的回调操作变多,代码就会非常散,变得难以维护。
  • 协程方式:同样通过事件循环执行程序,利用了Python 的生成器特性,生成器函数能够中途停止并在之后恢复,那么原本不得不分开写的回调函数就能够写在一个生成器函数中了,这也就实现了协程。

线程池实现爬虫

python多线程建立线程的两种方式

#第一种:通过函数创建线程
def 函数a():
    pass
t = threading.Thread(target=函数a,name=自己随便取的线程名字)

#第二种:继承线程类
class Fetcher(threading.Thread):
    def __init__(self):
        Thread.__init__(self):
        #加这一步后主程序中断退出后子线程也会跟着中断退出
        self.daemon = True
    def run(self):
        #线程运行的函数
        pass
t = Fetcher()

参见:python 多线程就这么简单 - 虫师 - 博客园

多线程同步-队列

多线程同步就是多个线程竞争一个全局变量时按顺序读写,一般情况下要用锁,但是使用标准库里的Queue的时候它内部已经实现了锁,不用程序员自己写了。

导入队列类:

from queue import Queue
创建一个队列:

q = Queue(maxsize=0)
maxsize为队列大小,为0默认队列大小可无穷大。

队列是先进先出的数据结构:

q.put(item) #往队列添加一个item,队列满了则阻塞
q.get(item) #从队列得到一个item,队列为空则阻塞
还有相应的不等待的版本,这里略过。

队列不为空,或者为空但是取得item的线程没有告知任务完成时都是处于阻塞状态

q.join()    #阻塞直到所有任务完成
线程告知任务完成使用task_done

q.task_done() #在线程内调用

完整代码

from queue import Queue 
from threading import Thread, Lock
import urllib.parse
import socket
import re
import time

seen_urls = set(['/'])
lock = Lock()


class Fetcher(Thread):
    def __init__(self, tasks):
        Thread.__init__(self)
        self.tasks = tasks
        self.daemon = True

        self.start()

    def run(self):
        while True:
            url = self.tasks.get()
            print(url)
            sock = socket.socket()
            sock.connect(('localhost', 3000))
            get = 'GET {} HTTP/1.0\r\nHost: localhost\r\n\r\n'.format(url)
            sock.send(get.encode('ascii'))
            response = b''
            chunk = sock.recv(4096)
            while chunk:
                response += chunk
                chunk = sock.recv(4096)

            links = self.parse_links(url, response)

            lock.acquire()
            for link in links.difference(seen_urls):
                self.tasks.put(link)
            seen_urls.update(links)    
            lock.release()

            self.tasks.task_done()

    def parse_links(self, fetched_url, response):
        if not response:
            print('error: {}'.format(fetched_url))
            return set()
        if not self._is_html(response):
            return set()
        urls = set(re.findall(r'''(?i)href=["']?([^\s"'<>]+)''',
                              self.body(response)))

        links = set()
        for url in urls:
            normalized = urllib.parse.urljoin(fetched_url, url)
            parts = urllib.parse.urlparse(normalized)
            if parts.scheme not in ('', 'http', 'https'):
                continue
            host, port = urllib.parse.splitport(parts.netloc)
            if host and host.lower() not in ('localhost'):
                continue
            defragmented, frag = urllib.parse.urldefrag(parts.path)
            links.add(defragmented)

        return links

    def body(self, response):
        body = response.split(b'\r\n\r\n', 1)[1]
        return body.decode('utf-8')

    def _is_html(self, response):
        head, body = response.split(b'\r\n\r\n', 1)
        headers = dict(h.split(': ') for h in head.decode().split('\r\n')[1:])
        return headers.get('Content-Type', '').startswith('text/html')


class ThreadPool:
    def __init__(self, num_threads):
        self.tasks = Queue()
        for _ in range(num_threads):
            Fetcher(self.tasks)

    def add_task(self, url):
        self.tasks.put(url)

    def wait_completion(self):
        self.tasks.join()

if __name__ == '__main__':
    start = time.time()
    pool = ThreadPool(4)
    pool.add_task("/")
    pool.wait_completion()
    print('{} URLs fetched in {:.1f} seconds'.format(len(seen_urls),time.time() - start))

事件驱动-回调函数实现爬虫

非阻塞I/O

如果使用非阻塞I/O,程序就不会傻傻地等在那里(比如等连接、等读取),而是会返回一个错误信息,虽然说是说错误信息,它其实就是叫你过一会再来的意思,编程的时候都不把它当错误看。

非阻塞I/O代码如下:

sock = socket.socket()
sock.setblocking(False)
try:
    sock.connect(('xkcd.com', 80))
except BlockingIOError:
    pass

单线程上的多I/O

有了非阻塞I/O这个特性,我们就能够实现单线程上多个sockets的处理了,学过C语言网络编程的同学应该都认识select这个函数吧?不认识也不要紧,select函数如果你不设置它的超时时间它就是默认一直阻塞的,只有当有I/O事件发生时它才会被激活,然后告诉你哪个socket上发生了什么事件(读|写|异常),在Python中也有select,还有跟select功能相同但是更高效的poll,它们都是底层C函数的Python实现。

不过这里我们不使用select,而是用更简单好用的DefaultSelector,是Python 3.4后才出现的一个模块里的类,你只需要在非阻塞socket和事件上绑定回调函数就可以了。

代码如下:

from selectors import DefaultSelector, EVENT_WRITE

selector = DefaultSelector()

sock = socket.socket()
sock.setblocking(False)
try:
    sock.connect(('localhost', 3000))
except BlockingIOError:
    pass

def connected():
    selector.unregister(sock.fileno())
    print('connected!')

selector.register(sock.fileno(), EVENT_WRITE, connected)

这里看一下selector.register的原型

register(fileobj, events, data=None)
其中fileobj可以是文件描述符也可以是文件对象(通过fileno得到),events是位掩码,指明发生的是什么事件,data 则是与指定文件(也就是我们的socket)与指定事件绑定在一起的数据。

如代码所示,selector.register 在该socket的写事件上绑定了回调函数connected(这里作为数据绑定)。在该socket上第一次发生的写事件意味着连接的建立,connected函数在连接建立成功后再解除了该socket上所有绑定的数据。

事件驱动

看了以上selector的使用方式,我想你会发现它很适合写成事件驱动的形式。

我们可以创建一个事件循环,在循环中不断获得I/O事件:

def loop():
    while True:
     events = selector.select()
        #遍历事件并调用相应的处理
        for event_key, event_mask in events:
         callback = event_key.data
            callback()

完整代码

from selectors import *
import socket
import re
import urllib.parse
import time


urls_todo = set(['/'])
seen_urls = set(['/'])
#追加了一个可以看最高并发数的变量
concurrency_achieved = 0
selector = DefaultSelector()
stopped = False


class Fetcher:
    def __init__(self, url):
        self.response = b''
        self.url = url
        self.sock = None

    def fetch(self):
        global concurrency_achieved
        concurrency_achieved = max(concurrency_achieved, len(urls_todo))

        self.sock = socket.socket()
        self.sock.setblocking(False)
        try:
            self.sock.connect(('localhost', 3000))
        except BlockingIOError:
            pass
        selector.register(self.sock.fileno(), EVENT_WRITE, self.connected)

    def connected(self, key, mask):
        selector.unregister(key.fd)
        get = 'GET {} HTTP/1.0\r\nHost: localhost\r\n\r\n'.format(self.url)
        self.sock.send(get.encode('ascii'))
        selector.register(key.fd, EVENT_READ, self.read_response)

    def read_response(self, key, mask):
        global stopped

        chunk = self.sock.recv(4096)  # 4k chunk size.
        if chunk:
            self.response += chunk
        else:
            selector.unregister(key.fd)  # Done reading.
            links = self.parse_links()
            for link in links.difference(seen_urls):
                urls_todo.add(link)
                Fetcher(link).fetch()

            seen_urls.update(links)
            urls_todo.remove(self.url)
            if not urls_todo:
                stopped = True
            print(self.url)

    def body(self):
        body = self.response.split(b'\r\n\r\n', 1)[1]
        return body.decode('utf-8')

    def parse_links(self):
        if not self.response:
            print('error: {}'.format(self.url))
            return set()
        if not self._is_html():
            return set()
        urls = set(re.findall(r'''(?i)href=["']?([^\s"'<>]+)''',
                              self.body()))

        links = set()
        for url in urls:
            normalized = urllib.parse.urljoin(self.url, url)
            parts = urllib.parse.urlparse(normalized)
            if parts.scheme not in ('', 'http', 'https'):
                continue
            host, port = urllib.parse.splitport(parts.netloc)
            if host and host.lower() not in ('localhost'):
                continue
            defragmented, frag = urllib.parse.urldefrag(parts.path)
            links.add(defragmented)

        return links

    def _is_html(self):
        head, body = self.response.split(b'\r\n\r\n', 1)
        headers = dict(h.split(': ') for h in head.decode().split('\r\n')[1:])
        return headers.get('Content-Type', '').startswith('text/html')


start = time.time()
fetcher = Fetcher('/')
fetcher.fetch()

while not stopped:
    events = selector.select()
    for event_key, event_mask in events:
        callback = event_key.data
        callback(event_key, event_mask)

print('{} URLs fetched in {:.1f} seconds, achieved concurrency = {}'.format(
    len(seen_urls), time.time() - start, concurrency_achieved))

事件驱动-协程实现爬虫

什么是协程?

协程其实是比起一般的子例程而言更宽泛的存在,子例程是协程的一种特例。

子例程的起始处是惟一的入口点,一旦退出即完成了子例程的执行,子例程的一个实例只会返回一次。

协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的。

协程的起始处是第一个入口点,在协程里,返回点之后是接下来的入口点。子例程的生命期遵循后进先出(最后一个被调用的子例程最先返回);相反,协程的生命期完全由他们的使用的需要决定。

还记得我们什么时候会用到yield吗,就是在生成器(generator)里,在迭代的时候每次执行next(generator)生成器都会执行到下一次yield的位置并返回,可以说生成器就是例程。

生成器实现协程模型

虽然生成器拥有一个协程该有的特性,但光这样是不够的,做异步编程仍是困难的,我们需要先用生成器实现一个协程异步编程的简单模型,它同时也是Python标准库asyncio的简化版,正如asyncio的实现,我们会用到生成器,Future类,以及yield from语句。

首先实现Future类, Future类可以认为是专门用来存储将要发送给协程的信息的类。

class Future:
    def __init__(self):
        self.result = None
        self._callbacks = []

    def add_done_callback(self, fn):
        self._callbacks.append(fn)

    def set_result(self, result):
        self.result = result
        for fn in self._callbacks:
            fn(self)

Future对象最开始处在挂起状态,当调用set_result时被激活,并运行注册的回调函数,该回调函数多半是对协程发送信息让协程继续运行下去的函数。

我们改造一下之前从fetch到connected的函数,加入Future与yield。

这是之前回调实现的fetch:

class Fetcher:
    def fetch(self):
        self.sock = socket.socket()
        self.sock.setblocking(False)
        try:
            self.sock.connect(('localhost', 3000))
        except BlockingIOError:
            pass
        selector.register(self.sock.fileno(),
                          EVENT_WRITE,
                          self.connected)

    def connected(self, key, mask):
        print('connected!')
        # ...后面省略...

改造后,我们将连接建立后的部分也放到了fetch中。

class Fetcher:
    def fetch(self):
        sock = socket.socket()
        sock.setblocking(False)
        try:
            sock.connect(('localhost', 3000))
        except BlockingIOError:
            pass

        f = Future()

        def on_connected():
            #连接建立后通过set_result协程继续从yield的地方往下运行
            f.set_result(None)

        selector.register(sock.fileno(),
                          EVENT_WRITE,
                          on_connected)
        yield f
        selector.unregister(sock.fileno())
        print('connected!')

fetcher是一个生成器函数,我们创建一个Future实例,yield它来暂停fetch的运行直到连接建立f.set_result(None)的时候,生成器才继续运行。那set_result时运行的回调函数是哪来的呢?这里引入Task类:

class Task:
    def __init__(self, coro):
        #协程
        self.coro = coro
        #创建并初始化一个为None的Future对象
        f = Future()
        f.set_result(None)
        #步进一次(发送一次信息)
        #在初始化的时候发送是为了协程到达第一个yield的位置,也是为了注册下一次的步进
        self.step(f)

    def step(self, future):
        try:
            #向协程发送消息并得到下一个从协程那yield到的Future对象
            next_future = self.coro.send(future.result)
        except StopIteration:
            return

        next_future.add_done_callback(self.step)

fetcher = Fetcher('/')
Task(fetcher.fetch())

loop()

流程大致是这样的,首先Task初始化,向fetch生成器发送None信息(也可以想象成step调用了fetch,参数是None),fetch得以从开头运行到第一个yield的地方并返回了一个Future对象给step的next_future,然后step就在这个得到的Future对象注册了step。当连接建立时on_connected就会被调用,再一次向协程发送信息,协程就会继续往下执行了。

使用yield from分解协程

一旦socket连接建立成功,我们发送HTTP GET请求到服务器并在之后读取服务器响应。现在这些步骤不用再分散在不同的回调函数里了,我们可以将其放在同一个生成器函数中:

def fetch(self):
    # ... 省略连接的代码
    sock.send(request.encode('ascii'))

    while True:
        f = Future()

        def on_readable():
            f.set_result(sock.recv(4096))

        selector.register(sock.fileno(),
                          EVENT_READ,
                          on_readable)
        chunk = yield f
        selector.unregister(sock.fileno())
        if chunk:
            self.response += chunk
        else:
            # 完成读取
            break

但是这样代码也会越积越多,可不可以分解生成器函数的代码呢,从协程中提取出子协程?Python 3 的yield from能帮助我们完成这部分工作。:

>>> def gen_fn():
...     result = yield 1
...     print('result of yield: {}'.format(result))
...     result2 = yield 2
...     print('result of 2nd yield: {}'.format(result2))
...     return 'done'
...

yield from得到的子协程最后return的返回值

完整代码

from selectors import *
import socket
import re
import urllib.parse
import time


class Future:
    def __init__(self):
        self.result = None
        self._callbacks = []

    def result(self):
        return self.result

    def add_done_callback(self, fn):
        self._callbacks.append(fn)

    def set_result(self, result):
        self.result = result
        for fn in self._callbacks:
            fn(self)

    def __iter__(self):
        yield self 
        return self.result


class Task:
    def __init__(self, coro):
        self.coro = coro
        f = Future()
        f.set_result(None)
        self.step(f)

    def step(self, future):
        try:
            next_future = self.coro.send(future.result)
        except StopIteration:
            return

        next_future.add_done_callback(self.step)


urls_seen = set(['/'])
urls_todo = set(['/'])
#追加了一个可以看最高并发数的变量
concurrency_achieved = 0
selector = DefaultSelector()
stopped = False


def connect(sock, address):
    f = Future()
    sock.setblocking(False)
    try:
        sock.connect(address)
    except BlockingIOError:
        pass

    def on_connected():
        f.set_result(None)

    selector.register(sock.fileno(), EVENT_WRITE, on_connected)
    yield from f
    selector.unregister(sock.fileno())


def read(sock):
    f = Future()

    def on_readable():
        f.set_result(sock.recv(4096))  # Read 4k at a time.

    selector.register(sock.fileno(), EVENT_READ, on_readable)
    chunk = yield from f
    selector.unregister(sock.fileno())
    return chunk


def read_all(sock):
    response = []
    chunk = yield from read(sock)
    while chunk:
        response.append(chunk)
        chunk = yield from read(sock)

    return b''.join(response)


class Fetcher:
    def __init__(self, url):
        self.response = b''
        self.url = url

    def fetch(self):
        global concurrency_achieved, stopped
        concurrency_achieved = max(concurrency_achieved, len(urls_todo))

        sock = socket.socket()
        yield from connect(sock, ('localhost', 3000))
        get = 'GET {} HTTP/1.0\r\nHost: localhost\r\n\r\n'.format(self.url)
        sock.send(get.encode('ascii'))
        self.response = yield from read_all(sock)

        self._process_response()
        urls_todo.remove(self.url)
        if not urls_todo:
            stopped = True
        print(self.url)

    def body(self):
        body = self.response.split(b'\r\n\r\n', 1)[1]
        return body.decode('utf-8')



    def _process_response(self):
        if not self.response:
            print('error: {}'.format(self.url))
            return
        if not self._is_html():
            return
        urls = set(re.findall(r'''(?i)href=["']?([^\s"'<>]+)''',
                              self.body()))

        for url in urls:
            normalized = urllib.parse.urljoin(self.url, url)
            parts = urllib.parse.urlparse(normalized)
            if parts.scheme not in ('', 'http', 'https'):
                continue
            host, port = urllib.parse.splitport(parts.netloc)
            if host and host.lower() not in ('localhost'):
                continue
            defragmented, frag = urllib.parse.urldefrag(parts.path)
            if defragmented not in urls_seen:
                urls_todo.add(defragmented)
                urls_seen.add(defragmented)
                Task(Fetcher(defragmented).fetch())

    def _is_html(self):
        head, body = self.response.split(b'\r\n\r\n', 1)
        headers = dict(h.split(': ') for h in head.decode().split('\r\n')[1:])
        return headers.get('Content-Type', '').startswith('text/html')


start = time.time()
fetcher = Fetcher('/')
Task(fetcher.fetch())

while not stopped:
    events = selector.select()
    for event_key, event_mask in events:
        callback = event_key.data
        callback()

print('{} URLs fetched in {:.1f} seconds, achieved concurrency = {}'.format(
    len(urls_seen), time.time() - start, concurrency_achieved))

总结

至此,我们在学习的过程中掌握了:

  • 线程池实现并发爬虫
  • 回调方法实现异步爬虫
  • 协程技术的介绍
  • 一个基于协程的异步编程模型
  • 协程实现异步爬虫

三种爬虫的实现方式中线程池是最坏的选择,因为它既占用内存,又有线程竞争的危险需要程序员自己编程解决,而且产生的I/O阻塞也浪费了CPU占用时间。再来看看回调方式,它是一种异步方法,所以I/O阻塞的问题解决了,而且它是单线程的不会产生竞争,问题好像都解决了。然而它引入了新的问题,它的问题在于以这种方式编写的代码不好维护,也不容易debug。看来协程才是最好的选择,我们实现的协程异步编程模型使得一个单线程能够很容易地改写为协程。那是不是每一次做异步编程都要实现Task、Future呢?不是的,你可以直接使用asyncio官方标准协程库,它已经帮你把Task、Future封装好了,你根本不会感受到它们的存在,是不是很棒呢?如果你使用Python 3.5那更好,已经可以用原生的协程了,Python 3.5追加了async def,await等协程相关的关键词。

参考链接:Python - Python实现基于协程的异步爬虫 - 实验楼

转载于:https://www.cnblogs.com/jjx2013/p/6223576.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python异步爬虫 的相关文章

  • 消息队列状态:struct msqid_ds

    Linux的消息队列 queue 实质上是一个链表 它有消息队列标识符 queue ID msgget创建一个新队列或打开一个存在的队列 msgsnd向队列末端添加一条新消息 msgrcv从队列中取消息 取消息是不一定遵循先进先出的 也可以
  • Mybatis学习

    mybatis面向接口编程 1 mybatis配置文件
  • 为什么pnpm比npm、yarn使用更好

    performant npm 意味高性能的 npm pnpm由 npm yarn 衍生而来 解决了 npm yarn 内部潜在的bug 极大的优化了性能 扩展了使用场景 被誉为 最先进的包管理工具 我们按照包管理工具的发展历史开始讲起 np
  • 转载--Windows下比较两个不同版本的二进制文件

    接手前人的软件 发现主程序依赖的动态库文件的源码没有包含在工程里面 花了好长时间找到了源代码 但是不知道它是不是最新版本的源代码 发现现有用到的动态库有两个版本的 其中一个修改时间旧一点的动态库文件在源代码的Release目录中可以找到 可
  • C# 自定义Label实现 指定字符串(关键词)高亮显示(字体、颜色)

    C 自定义Label实现 指定字符串 关键词 高亮显示 字体 颜色 原来是搞android的 本来自己就菜 现在由于项目需要开始着手弄C WPF 虽然了解一些 毕竟只是皮毛 唉 苦不堪言啊 还是得倚靠万能的互联网啊 需求 提示用户的文字 但
  • 机器学习--支持向量机(sklearn)

    机器学习 支持向量机 1 1 线性可分支持向量机 硬间隔支持向量机 训练数据集 T x 1 y 1 x 2 y 2 x N y N 当 y i 1 y i 1
  • Flutter页面不流畅,难道是使用姿势有问题?

    作者 檀婷婷 三莅 出品 阿里巴巴新零售淘系技术部 背景 高性能高流畅度一直是Flutter团队宣传的一大亮点 也是当初闲鱼选择Flutter的重要因素之一 但是随着复杂业务的应用落地 通过Flutter页面和原生页面滑动流畅度对比 我们开
  • 使用Azure Data Factory REST API和

    题解 给数组加一 class Solution public 代码中的类名 方法名 参数名已经指定 请勿修改 直接返回方法规定的值即可 题解 统计每种性别的人数 字符串子串函数的使用 substring index profile 1 SE
  • listView闪烁的问题

    用了一个ListView来实时的显示数据传输情况 于是问题就来了 当数据量比较大 而且处理速度很快时 这该死的界面闪得人眼花 废话不多说 直接上代码 首先 自定义一个类ListViewNF 继承自 System Windows Forms
  • stata 数据处理

    目录 按类别求均值 然后创建一个新的变量 缩尾处理 日期处理 连续变量处理成虚拟变量 按条件删除数据 按类别求均值 然后创建一个新的变量 bysort year industry egen meanvariable mean variabl
  • MySQL系列---事务与锁详解

    table of contents 1 背景 2 事务隔离级别 2 1 事务及其ACID属性 2 2 并发事务带来的问题 2 3 数据库事务隔离级别 3 锁机制 3 1 定义 3 2 分类 3 2 1 性能上划分 悲观乐观 3 2 2 从对
  • 解决微信小程序button的hover-class不生效问题

    在小程序开发过程中我们会遇到button添加style样式后即使添加hover class样式也没有点击效果的问题 产生该问题的原因为 在css中 内联样式style的优先级要高于class选择器的优先级 所以在我们添加style标签后即使
  • RabbitMq 报 An unexpected connection driver error occured和socket close异常处理

    进入rabbitMQ后台 1 后台地址为http localhost 15672 如果state状态为无法访问 那么我们就需要把这个链接给关掉 2 点击地址 找到close this connection 选择force close强制关闭
  • Centos7配置静态IP

    Centos7配置服务器静态IP 1 使用 ip addr 查看当前网卡信息 通过执行结果我们可以看到我们使用的网卡名称为ens33 2 配置服务器静态IP vi etc sysconfig network scripts ifcfg en
  • STL list

    文章目录 一 list 类的模拟实现 list 是一个带头双向循环链表 可以存储任意类型 模板参数 T 表示存储元素的类型 Alloc 是空间配置器 一般不用传 一 list 类的模拟实现 iterator 和 const iterator
  • 傅里叶图像相关性匹配-《医学图像处理》小作业五-Python代码/Matlab代码

    天津中医药大学 20级医学信息工程 教师 王翌 学生 邓集亲 学长我是用的python写的 matlab同样可以参考 实验五 相关性匹配 作业要求 参考 傅里叶变换 课的内容 采用快速傅里叶变换 FFT 进行相关性匹配 如下图示例输出结果图
  • 数据结构(第2版)陈越主编课后习题_【课后习题答案】离散数学(第2版)—课后习题答案...

    资 源 介 绍 本次分享内容为课程课后习题答案 教材名称 离散数学 第2版 主编作者 屈婉玲 耿素云 张立昂 出版社 高等教育出版社 ISBN 9787040419085 课后习题答案 01 习题一 02 习题二 03 习题三 04 习题四
  • java.io.IOException: Connection reset by peer

    接口要是返回的是字节 1 首先查看本地调用是否能正常返回 2 其次判断同样的参数测试环境是否正常返回 3 本地要是正常 测试环境异常的话 很大可能就是http协议版本不一致导致 解决办法 在nginx conf的location里加上 pr
  • Angular4基础开发文档

    Angular4基础开发文档
  • netstat命令详解

    命令介绍 netstat命令用于显示与IP TCP UDP和ICMP协议相关的统计数据 一般用于检验本机各端口的网络连接情况 netstat是在内核中访问网络及相关信息的程序 它能提供TCP连接 TCP和UDP监听 进程内存管理的相关报告

随机推荐

  • java/php/net/pythonMES生产线控制系统设计

    本系统带文档lw万字以上 答辩PPT 查重 如果这个题目不合适 可以去我上传的资源里面找题目 找不到的话 评论留下题目 或者站内私信我 有时间看到机会给您发 生产线控制系统 的设计主要是为了满足生产线管理员的实际需求 因此 它需要通过Int
  • 移动应用开发期末总结

    移动应用开发 什么是intent 问答题 Intent是一个动作的完整描述 包含了动作的产生组件 接收组件和传递的数据信息 Intent为Activity Service和BroadcastReceiver等组件提供交互能力 将一个组件的数
  • 使用Modelarts快速开发Hilens Kit实现人脸识别功能

    导语 在华为云平台上线的Modelarts模型训练平台结合华为智能终端产品Hilens kit 对Hilens Kit进行开发 实现产品的快速使用以及功能的实现 自从2020年疫情开始 使得人与人的接触变得更加不方便 间接促使了人工智能产业
  • Java中9种常见的CMS GC问题分析与解决

    目前 互联网上 Java 的 GC 资料要么是主要讲解理论 要么就是针对单一场景的 GC 问题进行了剖析 对整个体系总结的资料少之又少 前车之鉴 后事之师 美团的几位工程师历时一年多的时间 搜集了内部各种 GC 问题的分析文章 并结合个人的
  • unity开发小贴士之三 UGUI-Lua Component回收

    ugui tolua local test test b gameobjecttest c gameobject GetComponent typeof UnityEngine UI Button 首先调用UnityEngine GameO
  • Java Logging

    最后一次实验要求用日志来记录信息 学习的内容整理如下 Java 中的 Logging API 让 Java 应用可以记录不同级别的信息 它在debug过程中非常有用 如果系统因为各种各样的原因而崩溃 崩溃原因可以在日志中清晰地追溯 日志工作
  • 在小程序中使用图标

    因为最近在自学微信小程序 掌握了他的基础的使用 包括小程序的语法 小程序的自有组件 小程序的自有API及小程序的自定义组件 在学玩以上的各方面的知识体系后 我就想着学了这么就的微信小程序 自己总要写出点什么东西来才对的起自己这段时间来的努力
  • Android退出应用程序方法总结

    Android退出应用程序方法总结 在Android开发中 我们运行了应用程序后 都需要退出应用的 那么该如何退出应用 又都有哪些实现方式呢 今天就为大家整理分享一些退出应用程序的方法 一起来看看吧 更新内容 Ver v1 任务管理器方法补
  • 简短的char*与char[]

    include
  • 这就是搜索引擎——索引压缩

    对于海量数据 建立倒排索引往往需要较大的磁盘空间 尤其是一些常见的单词 这些单词对应的倒排列表可能有几百兆 如果搜索引擎在相应用户查询的时候 用户查询包含了常见的单词 就需要将大量的倒排列表信息从磁盘读入内存 由于磁盘读写速度往往是个瓶颈
  • RLHF 技术:如何能更有效?又有何局限性?

    编者按 自ChatGPT推出后 基于人类反馈的强化学习 RLHF 技术便成为大模型构建和应用人员关注的热点 但该方法一些情况下效果却差强人意 有些基础模型经RLHF调优后反而表现更差 RLHF技术的适用性和具体操作细节似乎成谜 这篇文章探讨
  • 平板电脑黑苹果EFI_保姆级别教你安装黑苹果,提供大量EFI与工具驱动!

    最近无聊 在电脑上装了个黑苹果 可能是我的机型比较好找吧 安装的过程中没有遇到太大的问题 接下教大家安装 也为大家准备了大量的EFI N卡驱动 蓝牙驱动 键盘驱动等工具 需要工具的小伙伴可以点击此处 所需要工具 1 黑果镜像 2 U盘 3
  • Python音视频剪辑库MoviePy1.0.3中文教程导览及可执行工具下载

    前往老猿Python博文目录 一 简介 MoviePy是一个用于视频编辑的Python模块 可用于进行视频的基本操作 如剪切 拼接 标题插入 视频合成 也称非线性编辑 视频处理或创建高级效果 它可以读写最常见的视频格式 MoviePy能处理
  • Xshell连接Ubuntu详细过程

    一 打开虚拟机VMware 二 启动虚拟机 输入密码 进入Ubuntu系统界面 三 鼠标右键 选择 在终端打开 出现终端对话框 四 在命令行输入 sudo apt get install openssh server 安装openssh s
  • 2017校招Java开发笔试题集

    美丽联合 二分查找要求结点 A 有序 顺序存储 B 有序 链接存储 C 无序 顺序存储 D 无序 链接存储 答案选 A 引入线索二叉树的目的是 A 加快查找结点的前驱或后继结点的速度 B 为了能在二叉树中方便插入和删除 C 为了能方便找到双
  • 使用STM32CubeMx配置STM32输入捕获功能

    输入捕获原理 在输入捕获模式下 当检测到ICx信号上相应的边沿后 计数器的当前值被锁存到捕获 比较寄存 器 TIMx CCRx 中 当发生捕获事件时 相应的CCxIF标志 TIMx SR寄存器 被置1 如果开放 了中断或者DMA操作 则将产
  • tools:context=".MainActivity的作用

    http blog csdn net caiwenfeng for 23 article details 8373569
  • Spring AOP 的搭建与源码分析

    AOP 面向切面编程 一 AOP 概述 AOP 的实现步骤 示例 二 通过 EnableAspectJAutoProxy 了解 AOP 原理 1 分析 EnableAspectJAutoProxy 2 复习 Spring 容器启动后 bea
  • Jq手搓一个简易版分页器

    分页器的效果如下 1 分页器dom结构 前面要有你将渲染列表的dom结构 这里省略 div class news trends fy div class left img src imags nof icon left png 左箭头 di
  • python异步爬虫

    本文主要包括以下内容 线程池实现并发爬虫 回调方法实现异步爬虫 协程技术的介绍 一个基于协程的异步编程模型 协程实现异步爬虫 线程池 回调 协程 我们希望通过并发执行来加快爬虫抓取页面的速度 一般的实现方式有三种 线程池方式 开一个线程池