Python零基础-下【详细】

news2025/1/19 23:02:55

接上篇继续:

Python零基础-中【详细】-CSDN博客

目录

十七、网络编程

1、初识socket

(1)socket理解

(2)图解socket

(3)戏说socket

(4)网络服务

(5)通信协议

2、基于UDP实现客户端与服务端通信

(1)使用socket开发的基本格式

(2)基于UDP开发服务端

(3)基于UDP开发客户端

3、基于TCP实现客户端与服务端通信

(1)基于TCP实现客户端与服务端

# 服务端实现

# 客户端实现

(2)通信改进

# 服务端实现

# 客户端实现

4、使用requests模块发生http请求

(1)安装requests模块

(2)使用requests模块发送get请求

十八、多进程与多线程

1、线程与进程的区别

(1)概念

(2)区别与联系

2、python中的多线程

(1)多线程的使用场景

(2)如何新建线程

(3)使用_thread模块创建线程

(4)使用threading创建线程

# 方式1

# 方式2

3、线程的状态及其转换

4、不可忽略的线程安全性问题

5、多进程

十九、正则表达式

1、正则表达式理解

(1)正则表达式理解

(2)正则表达式作用

(3)正则表达式特点

(4)python中使用正则表达式

# findall方法

# match方法

# search方法

2、元字符

(1)元字符理解

(2)元字符参考表

3、使用正则表达式匹配单一字符

(1)元字符参考表

(2)相关ASCII表

(3)使用正则,匹配字符串中所有的数字

(4)使用正则,匹配字符串中所有的非数字

(5)使用正则匹配换页符

(6)使用正则,匹配换行符

(7)使用正则,匹配回车符

4、正则表达式之重复出现数量匹配

(1)匹配0次到无限次

(2)匹配一次或多次

(3)匹配零次或一次

(4)匹配n次

(5)匹配至少n次

(6)匹配n次以上,m次以下

5、使用正则表达式匹配字符集

6、正则表达式之边界匹配

(1)匹配整个字符串开头

(2)匹配整个字符串的结尾位置

(3)匹配单词开头

7、正则表达式之组

(1)组的理解

(2)简单使用

(3)命名组

(4)命名组使用

8、正则表达式之贪婪与非贪婪

(1)理解

(2)应用

二十、实战之原生互联网爬虫

1、爬虫的道德与法律

2、爬虫之robots协议

(1)robots协议理解

(2)robots协议主要功能

(3)robots协议的写法

(4)解读robots协议(以小米官网为例)

3、爬虫的主要作用及其实现思路

(1)爬虫的作用

(2)实现思路

(3)爬虫五大件

4、爬虫网络环境搭建

(1)Hbuilder环境搭建

(2)爬虫应用

5、爬虫之抓取电商数据

(1)分析实现思路

(2)编写代码

# 定义结果类

# 编写主代码


十七、网络编程

1、初识socket

(1)socket理解

官方定义:套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信,网络套接字是IP地址与端口的组合。

(2)图解socket

socket是一个接口,在用户进程与TCP/IP协议之间充当中间人,完成TCP/IP协议的书写,用户只需理解接口即可

(3)戏说socket

以打电话为例:
要想打通一个电话,背后涉及到非常复杂的技术,包括但不限于手机与基站之间通信、将电信号转换成声音等,但是作为用户,我们是否需要知道这些技术如何实现,打电话时我又是否需要自己去实现这些非常复杂的东西?显然是不用的。我们只要拿起手机,按下号码和拨号键,便能与想通信的人通信。
同理,socket也是充当类似的角色,tcp、ip背后的通信机制也是非常复杂,这个时候用户并不需要理解或者实现这些东西,这些都由socket去处理了,用户所要做的,就是针对socket提供的接口去进行对应的处理(类似于拿起手机,按下号码和拨号键),就能实现网络通信了。

打电话需要以下的步骤:

  • 确定对方的手机号
  • 拨打
  • 对方接通
  • 开始通话
  • 话结束后,双方挂断

(4)网络服务

在python里面,提供了两个级别访问的网络服务

  • 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
  • 高级别的网络服务模块 SocketServer,它提供了服务器中心类,可以简化网络服务器的开发。

(5)通信协议

通信协议可分为TCP、UDP

  • TCP(Transmission Control Protocol传输控制协议)是一种面向连接的,可靠的,基于字节流的传输通信协议。
  • UDP(User Data Protocol,用户数据报协议)是无连接的,即发送数据之前不需要建立连接,UDP尽最大努力交付,即不保证可靠交付(类似于发短信,我只管发,能不能接受到跟我关系不大)

2、基于UDP实现客户端与服务端通信

(1)使用socket开发的基本格式

socket.socket(family,type,proto)
  • family: 套接字家族,可以使用AF_UNIX或者AF_INET
  • type: 套接字类型可以根据是面向连接(TCP)的还是非连接分(UDP)为 SOCK_STREAM 或 SOCK_DGRAM
  • protocol: 一般不填默认为0

AF_INET需经过多个协议层的编解码,消耗系统cpu,并且数据传输需要经过网卡,受到网卡带宽的限制。AF_UNIX数据到达内核缓冲区后,由内核根据指定路径名找到接收方socket对应的内核缓冲区,直接将数据拷贝过去,不经过协议层编解码,节省系统cpu,并且不经过网卡,因此不受网卡带宽的限制,AF_UNIX的传输速率远远大于AF_INET。
AF_INET不仅可以用作本机的跨进程通信,同样的可以用于不同机器之间的通信,其就是为了在不同机器之间进行网络互联传递数据而生。而AF_UNIX则只能用于本机内进程之间的通信。

(2)基于UDP开发服务端

import socket

# 接收方要有手机,并且开机
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("127.0.0.1", 8888))
print("UDP 服务端启动")

# 开机之后一直等着我跟他通信
while True:
    # 接收到信息,信息里面包括客户端发送的内容以及客户端的信息(ip,端口)
    data, client = server.recvfrom(1024)
    print("接受到客户端发来的消息:", data.decode('utf-8'))

    server.sendto("您好,这是服务端".encode("utf-8"), client)

(3)基于UDP开发客户端

import socket

# 同样的,要有手机
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 有对方的号码(ip,端口)
client.sendto("您好,我是客户端".encode('utf-8'), ("127.0.0.1", 8888))
# 接受服务端的响应
data, server = client.recvfrom(1024)
print(data.decode("utf-8"))
client.close()

3、基于TCP实现客户端与服务端通信

(1)基于TCP实现客户端与服务端

# 服务端实现
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8888))
server.listen(5)
print("服务端已经启动,等待客户端连接======》")
client, address = server.accept()
print("已经建立连接")
data = client.recv(1024)
print("服务端接收到的数据:", data.decode("utf-8"))
print("请输入回复的内容:")
client.send(input().encode("utf-8"))
server.close()

# 客户端实现
import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8888))
print("客户端已启动,已完成连接")
print("请输入要发送的内容:")
client.send(input().encode("utf-8"))
data = client.recv(1024)
print("接收到服务端的响应:", data.decode("utf-8"))
client.close()

(2)通信改进

修改上面的程序,使客户端可以和服务端聊天,直到客户端发送close内容,则关闭双方通信

# 服务端实现
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 9999))
# 同一时刻 ,允许多少客户端跟我通信
server.listen(5)
print("服务端已启动,等待客户端连接=======》")
client, address = server.accept()
print("客户端已经连接")

while True:
	recv = client.recv(1024)
	msg = recv.decode("utf-8")
	if msg == "close":
		print("服务端关闭=======》")
		server.close()
		break
	print("客户端发送的内容:", msg)
	print("请输入回复内容:")
	client.send(input().encode("utf-8"))

# 客户端实现
import socket

# 要有手机
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 拨打电话
client.connect(("127.0.0.1", 9999))

# 通话
while True:
    print("请输入要发送的内容:")
    send_data = input()
    client.send(send_data.encode("utf-8"))
    if send_data == "close":
        client.close()
        print("关闭连接")
        break
    data = client.recv(1024).decode("utf-8")
    print("接受到服务端响应:", data)

4、使用requests模块发生http请求

(1)安装requests模块

如果标红,则直接点击提示安装即可

(2)使用requests模块发送get请求

import requests

result = requests.get("")
text = result.content
print(text.decode('utf-8'))

十八、多进程与多线程

1、线程与进程的区别

(1)概念

进程:是系统理资源的基本单位进行分配和管
线程:进程的一个执行单元,是进程内调度的实体、是CPU调度和分派的基本单位,是比进程更小的独立运行的基本单位。线程也被称为轻量级进程,线程是程序执行的最小单位

(2)区别与联系

一个程序至少一个进程,一个进程至少一个线程(至少会有一个主线程)

进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵,而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多

线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式进行,如何处理好同步与互斥是编写多线程程序的难点

多进程程序更健壮,进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响;而线程只是一个进程中的不同执行路径,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,所以可能一个线程出现问题,进而导致整个程序出现问题

2、python中的多线程

(1)多线程的使用场景

  • 把占用时间的操作放后台处理
  • 提升用户体验
  • 加快程序运行速度

(2)如何新建线程

在python3之前的版本,可以使用thread、threading 两个模块新建线程
在python3中,可以可以使用_thread、threading 两个模块新建线程,thread 模块已被废弃,为了兼容性,Python3 将 thread 重命名为 "_thread"


threading是高级模块,对_thread进行了封装,所以在平常的开发中,更加推荐使用threading模块

(3)使用_thread模块创建线程

基本语法:

_thread.start_new_thread(函数, 函数入参)

示例:

import _thread
import time


# 为线程定义一个函数
def print_name(thread_name):
    count = 0
    while count < 5:
        time.sleep(1)
        count += 1
        print(thread_name)


_thread.start_new_thread(print_name, ("Thread-1",))
_thread.start_new_thread(print_name, ("Thread-2",))

while 1:
    pass

(4)使用threading创建线程

# 方式1

基本语法:

threading.Thread(group=None, target=None, name=None,args=(), kwargs=None, *, daemon=None)

参数说明:

  • group:在当前的版本保持为None,不要进行修改
  • target: 这个线程要干的活(函数)
  • name: 线程名
  • args: 上面target参数的入参(),tuple类型,对应taget参数中的可变参数
  • kwargs:上面target参数的入参,dict类型,对应taget参数中的关键字参数

示例:

import threading


def my_fun():
    print("Hello world")


threading.Thread(target=my_fun).start()

# 方式2

除了直接使用threading.Thread的方式创建线程之外,也可以直接通过继承

import threading


class MyThread(threading.Thread):
    def run(self):
        print("hello world")


thread = MyThread()
thread.start()

3、线程的状态及其转换

4、不可忽略的线程安全性问题

下面的程序,多次运行得到的结果都不太一样

import threading


class MyThread(threading.Thread):
    sum = 0

    def run(self):
        for i in range(1000000):
            MyThread.sum += 1


thread = MyThread()
thread2 = MyThread()
thread3 = MyThread()
thread.start()
thread2.start()
thread3.start()
thread.join()
thread2.join()
thread3.join()

print(MyThread.sum)

线程安全性问题的三要素:

  • 多线程环境下
  • 共享变量
  • 并发对共享变量进行修改

如何解决线程安全性问题

  • 单线程化
  • 不要共享变量
  • 对共享变量的操作串行化

前面两个条件在现实开发中相对不可取,那么就剩下第三个--将并发修改串行化,这个时候就要使用到锁

import threading


class MyThread(threading.Thread):
    sum = 0
    lock = threading.Lock()

    def run(self):
        with MyThread.lock:
            for i in range(1000000):
                MyThread.sum += 1


thread = MyThread()
thread2 = MyThread()
thread3 = MyThread()
thread.start()
thread2.start()
thread3.start()
thread.join()
thread2.join()
thread3.join()

print(MyThread.sum)

5、多进程

多进程使用Multiprocessing 模块,总体的使用跟多线程差不多

# 为线程定义一个函数
import multiprocessing
import time


def print_name(thread_name):
    count = 0
    while count < 5:
        time.sleep(1)
        count += 1
        print(thread_name)


if __name__ == "__main__":
    process = multiprocessing.Process(target=print_name, args=("多进程",))
    process.start()
    process.join()

十九、正则表达式

1、正则表达式理解

(1)正则表达式理解

正则表达式(英语:Regular Expression,常简写为regex、regexp或RE),又称正则表示式、正则表示法、规则表达式、常规表示法,是计算机科学的一个概念

(2)正则表达式作用

正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串,在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本

(3)正则表达式特点

  • 灵活性、逻辑性和功能性非常强
  • 可以迅速地用极简单的方式达到字符串的复杂控制

(4)python中使用正则表达式

# findall方法

python中,要使用正则表达式,需要导入re模块,基本格式如下:

re.findall(pattern, string, flags=0)

参数说明如下:

  • pattern:匹配的正则表达式
  • string:要匹配的字符串
  • flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等

flags可选值如下:

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

简单使用:

import re

str = "hell0,my name is cendy"

result = re.findall("cendy", str)
print(result)

# match方法

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回 none

import re

s = 'hello world'
result = re.match('hello', s)
print(result.group(0))

要获取匹配的结果,可以使用group(n);匹配结果有多个的时候,n从0开始递增,当匹配结果有多个的时候,也可以使用groups()一次性获取所有匹配的结果

# search方法

re.search 扫描整个字符串并返回第一个成功的匹配

import re

s = 'hello world hello'
result = re.search('hello', s)
print(result.group(0))

2、元字符

(1)元字符理解

正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符

元字符使正则表达式具有处理能力,所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)

(2)元字符参考表

元字符描述
\d匹配一个数字字符,等价于[0-9]。grep 要加上-P,perl正则支持
\D匹配一个非数字字符,等价于[^0-9]。grep要加上-P,perl正则支持
\f匹配一个换页符,等价于\x0c和\cL
\n匹配一个换行符,等价于\x0a和\cJ
\r匹配一个回车符,等价于\x0d和\cM
\s匹配任何不可见字符,包括空格、制表符、换页符等等,等价于[ \f\n\r\t\v]
\S匹配任何可见字符,等价于[^ \f\n\r\t\v]
\t匹配一个制表符,等价于\x09和\cI
\v匹配一个垂直制表符,等价于\x0b和\cK
\w匹配包括下划线的任何单词字符,类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集
\W匹配任何非单词字符,等价于“[^A-Za-z0-9_]”
\将下一个字符标记符、或一个向后引用、或一个八进制转义符,例如,“\\n”匹配\n,“\n”匹配换行符;序列“\”匹配“\”而“(”则匹配“(”,即相当于多种编程语言中都有的“转义字符”的概念
^匹配输入字行首,如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置
$匹配输入行尾,如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置
\B匹配非单词边界,“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”
\b匹配一个单词的边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;“\b1”可以匹配“1_23”
中的“1”,但不能匹配“21_3”中的“1”
*匹配前面的子表达式任意次,例如,zo能匹配“z”,也能匹配“zo”以及“zoo”,等价于{0,}
+匹配前面的子表达式一次或多次(大于等于1次),例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}
?匹配前面的子表达式零或一次,例如,“do(es)?”可以匹配“do”或“does” ?等价于{0,1}
{n}n是一个非负整数,匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o
{n,}n是一个非负整数,至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o;“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”
{n,m}m和n均为非负整数,其中n<=m,最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组;“o{0,1}”等价于“o?” (注意:在逗号和两个数之间不能有空格)
?当该字符紧跟在任何一个其他限制符(,+,?,{n},{n,},{n,m*})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']
.匹配除“\n”和"\r"之外的任何单个字符。要匹配包括“\n”和"\r"在内的任何字符,请使用像“[\s\S]”的模式
(pattern)匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“)”
(?:pattern)非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分时很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式
(?=pattern)非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
(?!pattern)非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”
(?<=pattern)非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
(?<!pattern)非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
x|y匹配x或y。例如,“z|food”能匹配“z”或“food”(此处请谨慎)。“[z|f]ood”则匹配“zood”或“food”
[xyz]字符集合,匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”
[^xyz]负值字符集合,匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”任一字符
[a-z]字符范围,匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身
[^a-z]负值字符范围,匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符
\xn匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码
\num匹配num,其中num是一个正整数,对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符
\n标识一个八进制转义值或一个向后引用。如果*n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n*为一个八进制转义值
\nm标识一个八进制转义值或一个向后引用。如果*nm之前至少有nm个获得子表达式,则nm为向后引用。如果*nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则*nm将匹配八进制转义值nm*
\nml如果n为八进制数字(0-7),且m和l均为八进制数字(0-7),则匹配八进制转义值nml
\un匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)
\p{P}小写 p 是 property 的意思,表示 Unicode 属性,用于 Unicode 正表达式的前缀。中括号内的“P”表示Unicode字符集七个字符属性之一:标点字符。其他六个属性:L:字母;M:标记符号(一般不会单独出现);Z:分隔符(比如空格、换行等);S:符号(比如数学符号、货币符号等);N:数字(比如阿拉伯数字、罗马数字等);C:其他字符。*注:此语法部分语言不支持,例:javascript
\<\>匹配词(word)的开始(\<)和结束(\>)。例如正则表达式<the>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"(注意:这个元字符不是所有的软件都支持的)
()将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用
|将两个匹配条件进行逻辑“或”(or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."(注意:这个元字符不是所有的软件都支持的)

3、使用正则表达式匹配单一字符

(1)元字符参考表

此次涉及到的元字符如下:

元字符描述
\d匹配一个数字字符,等价于[0-9]。grep 要加上-P,perl正则支持
\D匹配一个非数字字符,等价于[^0-9]。grep要加上-P,perl正则支持
\f匹配一个换页符,等价于\x0c和\cL
\n匹配一个换行符,等价于\x0a和\cJ
\r匹配一个回车符,等价于\x0d和\cM
\v匹配一个垂直制表符,等价于\x0b和\cK
\t匹配一个制表符,等价于\x09和\cI
\S匹配任何可见字符,等价于[^ \f\n\r\t\v]
\s匹配任何不可见字符,包括空格、制表符、换页符等等,等价于[ \f\n\r\t\v]
\w匹配包括下划线的任何单词字符,类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集
\W匹配任何非单词字符,等价于“[^A-Za-z0-9_]”

(2)相关ASCII表

(3)使用正则,匹配字符串中所有的数字

import re

str = "hell0,1my334 5 6name is cendy"

result = re.findall("\d", str)
print(result)

(4)使用正则,匹配字符串中所有的非数字

import re

str = "hell0,1my334 5 6name is cendy"

result = re.findall("\D", str)
print(result)

注意:非数字字符,包括空格等字符

(5)使用正则匹配换页符

import re

# 这里只用chr将ASCII表中换页符转换成字符
str = "hell0,1my334 5 6name is cendy&" +chr(12)

result = re.findall("\f", str)
print(result)

(6)使用正则,匹配换行符

import re

str = "hell0,1my334 5 6name is cendy&" + chr(10)

result = re.findall("\n", str)
print(result)

(7)使用正则,匹配回车符

import re

str = "hell0,1my334 5 6name is cendy&" + chr(13)

result = re.findall("\r", str)
print(result)

4、正则表达式之重复出现数量匹配

本次涉及到的元字符如下:

元字符描述
*匹配前面的子表达式任意次,例如,zo能匹配“z”,也能匹配“zo”以及“zoo”,等价于{0,}
+匹配前面的子表达式一次或多次(大于等于1次),例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}
?匹配前面的子表达式零或一次,例如,“do(es)?”可以匹配“do”或“does” ?等价于{0,1}
{n}n是一个非负整数,匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o
{n,}n是一个非负整数,至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o;“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”
{n,m}m和n均为非负整数,其中n<=m,最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组;“o{0,1}”等价于“o?” (注意:在逗号和两个数之间不能有空格)

(1)匹配0次到无限次

【hello】 中 【o】 出现的次数是0次或者无限次

import re

s = "hello world helloo hell"
print(re.findall('hello*', s))

(2)匹配一次或多次

【hello】中【o】至少出现一次或者多次

import re

s = "hello world helloo hell"
print(re.findall('hello+', s))

(3)匹配零次或一次

【hello】中【o】出现零次或者一次

import re

s = "hell"
print(re.findall('hello?', s))

(4)匹配n次

【hello】中【o】出现n次,这里为出现2次

import re

s = "Helloo"
print(re.findall('Hello{2}', s))

(5)匹配至少n次

【hello】中【o】至少出现n次,这里为至少出现2次

import re

s = "Helloooo"
print(re.findall('Hello{2,}', s))

(6)匹配n次以上,m次以下

【hello】中【o】至少出现n次,至多出现m次

import re

s = "Helloooo"
print(re.findall('Hello{2,3}', s))

示例:

str = "李白 张三 李白板 李寻欢 李小白 李白白"

1.找出里面所有姓李的人名
2.找出所有李白开头的人名

str = "李白 张三 李四 王五 李寻欢 李白白 李白板 李小白"
print(re.findall("李\S*",str))
print(re.findall("李白+",str))
print(re.findall("李白+\S*",str))

5、使用正则表达式匹配字符集

本次涉及到的元字符如下:

元字符描述
.匹配除“\n”和"\r"之外的任何单个字符。要匹配包括“\n”和"\r"在内的任何字符,请使用像“[\s\S]”的模式
x|y匹配x或y。例如,“z|food”能匹配“z”或“food”(此处请谨慎)。“[z|f]ood”则匹配“zood”或“food”
[xyz]字符集合,匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”
[^xyz]负值字符集合,匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”任一字符
[a-z]字符范围,匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身
[^a-z]负值字符范围,匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符

使用正则表达式匹配字符集合,如下:

import re

s = "abc"
print(re.findall('a|b', s))

import re

str = "110,120,210,170"

result = re.findall("1[12]0", str)
print(result)

如果是连续的范围,可以使用横杠-

import re

str = "110,120,210,170"

result = re.findall("1[1-9]0", str)
print(result)

表示不是某范围之内的,可以使用^取反

import re

str = "110,120,210,170,1c0,1a0"

result = re.findall("1[^1-9]0", str)
print(result)

6、正则表达式之边界匹配

本次涉及到的元字符如下:

元字符描述
^匹配输入字行首,如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置
$匹配输入行尾,如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置
\B匹配非单词边界,“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”
\b匹配一个单词的边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;“\b1”可以匹配“1_23”
中的“1”,但不能匹配“21_3”中的“1”

(1)匹配整个字符串开头

import re

s = "cendy"
print(re.findall('^ce', s))

(2)匹配整个字符串的结尾位置

import re

s = "cendy ss ss ss"
print(re.findall('ss$', s))

(3)匹配单词开头

import re

s = "cendy xdaaa"
print(re.findall(r'\bxd', s))

注意:这里只是匹配单词边界,而非整个单词

7、正则表达式之组

(1)组的理解

将括号() 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1到\9 的符号来引用

(2)简单使用

提取一段html代码里面的标题内容,如下:

import re

s = '''
<body>
<h1>标题问题内容</h1>
<h3 class="abc">我字体大小16px,去掉加粗</h3>
</body>
'''

print(re.findall(r'<h1>(.*)</h1>[\s\S]*<h3.*>(.*)</h3>', s))

从输出结果看,返回了一个list,里面包着元组,这个时候,如果我想取里面第一个组,则:

import re

s = '''
<body>
<h1>标题问题内容</h1>
<h3 class="abc">我字体大小16px,去掉加粗</h3>
</body>
'''

result = re.findall(r'<h1>(.*)</h1>[\s\S]*<h3.*>(.*)</h3>', s)
print(result[0][0])
print(result[0][1])

这个看起来就略显繁琐,而且也不太能看的懂,这个时候就可以使用命名组

(3)命名组

一个正则里可以有多个组,默认编号从1到9,但是具体在代码里面,都写在1,2,这样的东西,那么对阅读代码者来说就是一场噩梦。这个时候,命名分组的作用就来了:命名分组就是给具有默认分组编号的组另外再给一个别名 ,也就是说,我可以给这些编号取个容易看懂的名字

基本语法格式:

(?P<name>正则表达式)

(4)命名组使用

命名组一般是跟search方法一起使用的

import re

s = '''
<body>
<h1>标题问题内容</h1>
<h3 class="abc">我字体大小16px,去掉加粗</h3>
</body>
'''

result = re.search(r'<h1>(?P<h1>.*)</h1>[\s\S]*<h3.*>(?P<h3>.*)</h3>', s)
print(result.group('h1'))
print(result.group('h3'))

8、正则表达式之贪婪与非贪婪

(1)理解

之前进行重复数量匹配的时候,用的一系列元字符,总是会匹配最多结果;Python里数量词默认是贪婪的,总是尝试匹配尽可能多的字符 ,如果需要正则进行非贪婪的匹配,这个时候,就需要在正则表达式后面加个问号【?】

贪婪模式:

import re

s = 'hellooo '
print(re.findall(r'hello+', s))

非贪婪模式:

import re

s = 'hellooo'
print(re.findall(r'hello+?', s))

(2)应用

对于下面的文本,分别提取出<div class="news">这个div中的两个div

<div class="news">
	<div class="content">
		<h3>This is a header</h3>
	</div>
	<div class="content">
		<h3>This is a heade2r</h3>
	</div>
</div>

贪婪模式下的匹配:

import re

s = '''
<div class="news">
	<div class="content">
		<h3>This is a header</h3>
	</div>
	<div class="content">
		<h3>This is a heade2r</h3>
	</div>
</div>
'''
result = re.findall(r'<div class="content">([\s\S]*)</div>', s)
print(result)

从上图可以看到,中间的两个div的内容没有分别被提取,而是混成一块

非贪婪模式:

import re

s = '''
<div class="news">
	<div class="content">
		<h3>This is a header</h3>
	</div>
	<div class="content">
		<h3>This is a heade2r</h3>
	</div>
</div>
'''
result = re.findall(r' <div class="content">([\s\S]*?)</div>', s)
print(result)

二十、实战之原生互联网爬虫

1、爬虫的道德与法律

爬虫作为一种计算机技术就决定了它的中立性,因此爬虫本身在法律上并不被禁止,但是利用爬虫技术获取数据这一行为是具有违法甚至是犯罪的风险的

爬虫应该遵守的道德

  • 必须遵守被爬网站意愿,比如robots协议
  • 不得干扰被访问网站的正常运营

爬虫的法律问题

  • 严格遵守网站设置的robots协议
  • 在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行
  • 在设置抓取策略时,应注意避免抓取视频、音乐等可能构成作品的数据,或者针对某些特定网站批量抓取其中的用户生成内容
  • 在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除
  • 严禁抓取涉及个人隐私的数据,包括公民的姓名、身份证件号码、通信通讯联系方式、住址、账号密码、财产状况、行踪轨迹等个人信息

2、爬虫之robots协议

(1)robots协议理解

Robots协议是国际互联网界通行的道德规范,基于以下原则建立:

  • 搜索技术应服务于人类,同时尊重信息提供者的意愿,并维护其隐私权
  • 网站有义务保护其使用者的个人信息和隐私不被侵犯

(2)robots协议主要功能

Robots协议用来告知搜索引擎哪些页面能被抓取,哪些页面不能被抓取;可以屏蔽一些网站中比较大的文件,如:图片,音乐,视频等,节省服务器带宽;可以屏蔽站点的一些死链接;方便搜索引擎抓取网站内容;设置网站地图连接,方便引导爬虫爬取页面

(3)robots协议的写法

较为常见的有:

  • User-agent:指定对哪些爬虫生效
  • Disallow:指定要屏蔽的网址

(4)解读robots协议(以小米官网为例)

打开官网,在后面加上robots.txt,即可获得robots协议 [https://www.mi.com/robots.txt]

# robots.txt for http://www.mi.com
# 2016/2/15

User-agent: *

# 禁止/后带问号的
Disallow: /?*
# 禁止带search_的
Disallow: /search_*
# 禁止带/item/
Disallow: /item/
# 禁止带/comment/
Disallow: /comment/
# 禁止带/accessories/
Disallow: /accessories/
# 禁止带/cart/
Disallow: /cart/
# 禁止带/misc/
Disallow: /misc/

3、爬虫的主要作用及其实现思路

(1)爬虫的作用

在我们浏览网页时,浏览器会渲染输出 HTML 、 JS 、 CSS 等信息;通过这些元素,我们就可以看到我们想要查看的新闻、图片、电影、评论、商品等等。一般情况下我们看到自己需要的内容,图片可能会复制文字并且下载图片保存,但是如果面对大量的文字和图片,我们人工是处理不过来的,同时比如类似百度需要每天定时获取大量网站最新文章并且收录,这些大量数据与每天的定时的工作我们是无法通过人工去处理的,这时候爬虫的作用就体现出来了

(2)实现思路

(3)爬虫五大件

  • 爬虫调度器:主要是配合调用其他四个模块,所谓调度就是去调用其他的模板
  • URL管理器:就是负责管理URL链接的,URL链接分为已经爬取的和未爬取的,这就需要URL管理器来管理它们,同时它也为获取新URL链接提供接口
  • HTML下载器:就是将要爬取的页面的HTML下载下来
  • HTML解析器::就是将要爬取的数据从HTML源码中获取出来,同时也将新的URL链接发送给URL管理器以及将处理后的数据发送给数据存储器
  • 数据存储器:就是将HTML下载器发送过来的数据存储到本地

4、爬虫网络环境搭建

(1)Hbuilder环境搭建

Hbuilder官网地址:https://www.dcloud.io/

进入官网后,点击下面图标,然后根据所需进行下载

下载完成后解压双击exe文件打开即可

(2)爬虫应用

获取测试的目标源码:https://codeload.github.com/Keraun/xiaomi/zip/master

打开Hbuilder,点击[文件--->导入--->从本地目录导入],选择打开解压后的网页源码文件夹,点击[运行--->运行到浏览器--->选择chrome],之后会自动为我们打开浏览器并加载页面

5、爬虫之抓取电商数据

(1)分析实现思路

  • 明确抓取的需求
  • 仔细观察待抓取网页特点
  • 拉取网页
  • 提取数据

(2)编写代码

# 定义结果类

首先,新建[product.py]文件,该文件中定义一个类,用来存储数据

# 结果类
class Product:

    def __init__(self, name, desc, price):
        self.name = name
        self.desc = desc
        self.price = price

    def __eq__(self, o: object) -> bool:
        return self.name == o.name

    def __hash__(self) -> int:
        return hash(self.name)

    def __str__(self) -> str:
        return '产品名:%s 描述:%s 价格:%s' % (self.name, self.desc, self.price)

# 以下是测试代码,可忽略
# print(Product(1,1,1))

# 编写主代码

第一步,新建数据存储器-[data_storage.py]

# 数据存储器
class DataStorage:
	def storage(self, products):
		"""
		数据存储 为了方便直接打印提取的结果
		:param products: set结构
		:return:
		"""
		for i in products:
			print(i)

第二步,新建url管理器-[url_manager.py]

url管理器中有一些旧的以及新的url来表示爬取过的和未爬取过的,都用set()结构来存储

#url管理器
class UrlManager:
	def __init__(self):
		self.new_urls = set()
		self.old_urls = set()

	def get_new_url(self):
		"""
		获取新的url
		:return: url
		"""
		return self.new_urls.pop()

	def add_new_url(self, url):
		"""
		添加新的url
		:param url: 新的url
		:return:
		"""
		self.new_urls.add(url)

	def add_old_url(self, url):
		"""
		添加已经爬取过的url
		:param url: 爬取过的url
		:return:
		"""

	    self.old_urls.add(url)

第三步,新建html下载器-[html_downloader.py]

该模块写好后进行测试,测试url="http://127.0.0.1:8848/xiaomi-master/index.html"

import requests


# html下载器
class HtmlDownloader:
    def download(self, url):
        """
        根据给定的url下载网页
        :param url: url
        :return: 下载好的文本
        """
        result = requests.get(url)
        return result.content.decode("utf-8")

# 以下是测试代码,可忽略
# downloader = HtmlDownloader()
# result = downloader.download("http://127.0.0.1:8848/xiaomi-master/index.html")
# print(result)

第四步,新建html解析器-[html_parser.py]

import re

from pythonProject.spider.html_downloader import HtmlDownloader
from pythonProject.spider.product import Product


class HtmlParser:
    item_pattern = r'<li class="brick-item">[\s\S]*?</li>'
    title_pattern = r'<h3 class="title"><a href="javascript:;">([\s\S]*?)</a></h3>'
    desc_pattern = r'<p class="desc">([\s\S]*?)</p>'
    price_pattern = r'<span class="num">([\s\S]*?)</span>'

    def parser(self, html):
        """
        解析给定的html
        :param html: html
        :return: product set
        """
        items = re.findall(self.item_pattern, html)
        # print(items)

        #定义一个result,放入最终要返回的数据
        result = set()
        for i in items:
            title = re.findall(self.title_pattern, i)
            desc = re.findall(self.desc_pattern, i)
            price = re.findall(self.price_pattern, i)
            result.add(Product(title[0], desc[0], price[0]))

        return result

# 这里是测试代码,可忽略
# downloader = HtmlDownloader()
# html = downloader.download("http://127.0.0.1:8848/xiaomi-master/index.html")
# htmlParse = HtmlParser()
# result = htmlParse.parser(html)
# for i in result:
#     print(i)

第五步,新建爬虫管理器-[spider_main.py]

from pythonProject.spider.data_storage import DataStorage
from pythonProject.spider.html_downloader import HtmlDownloader
from pythonProject.spider.html_parser import HtmlParser
from pythonProject.spider.url_manager import UrlManager


class SpiderMain:

    def __init__(self):
        """
        初始化方法,主要是将其他组件实例化
        """
        self.url_manager = UrlManager()
        self.html_downloader = HtmlDownloader()
        self.html_parser = HtmlParser()
        self.data_storage = DataStorage()

    def start(self):
        """
        爬虫的主启动方法
        :return:
        """
        self.url_manager.add_new_url("http://127.0.0.1:8848/xiaomi-master/index.html")
        # 从url管理器里面获取url
        url = self.url_manager.get_new_url()
        # 将获取到的url使用下载器进行下载
        html = self.html_downloader.download(url)
        # 将html进行解析
        result = self.html_parser.parser(html)
        # 数据存储
        self.data_storage.storage(result)


if __name__ == "__main__":
    main = SpiderMain()
    main.start()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1719656.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker安装Redis(云服务器)

准备&#xff1a; 在云服务器中开启6370端口号 docker run -d --name redis -p 6379:6379 redis 这条命令使用docker运行一个名为"redis"的容器&#xff0c;映射容器的6379端口到主机的6379端口&#xff0c;并且使用redis镜像来运行容器。REDIS是一个开源的内存数据…

18.Redis之哨兵

1.哨兵机制的介绍 通过自动化的手段,来解决主节点挂了的问题~~ 哨兵机制, 是通过独立的 进程 来体现的.和之前 redis-server 是不同的进程!! redis-sentine| 不负责存储数据,只是对其他的 redis-server 进程起到监控的效果~~ 通常哨兵节点,也会搞一个集合~~(多个哨兵节点构成的…

TQSDRPI开发板教程:实现LWIP自环

项目资源分享 链接&#xff1a;https://pan.baidu.com/s/1QckFYSwua-Fpxk3tewledQ 提取码&#xff1a;bewt 打开vivado2019.1创建工程 下一步 设置工程名称与地址。勾选reate…选项&#xff0c;会创建一个新的文件夹&#xff0c;项目保存在其中&#xff0c;文件夹名称与工程名…

伦敦金1手指的是什么?

就算没有进行过伦敦金投资&#xff0c;甚至是没有做过任何金融产品投资的朋友&#xff0c;相信也听过别人做交易决定&#xff0c;他们都是1手两手那样头买入。有些朋友就好奇了&#xff0c;这伦敦金1手指的是什么意思&#xff1f;下面我们就来讨论一下。 首先&#xff0c;如果我…

云计算与 openstack

文章目录 一、 虚拟化二、云计算2.1 IT系统架构的发展2.2 云计算2.3 云计算的服务类型 三、Openstack3.1 OpenStack核心组件 一、 虚拟化 虚拟化使得在一台物理的服务器上可以跑多台虚拟机&#xff0c;虚拟机共享物理机的 CPU、内存、IO 硬件资源&#xff0c;但逻辑上虚拟机之…

【论文导读】Grid Graph Reduction for Efficient Shortest Pathfinding(2023 Access)

Grid Graph Reduction for Efficient Shortest Pathfinding 作者&#xff1a;CHAN-YOUNG KIM AND SANGHOON SULL 文章提出了一种“基于模式识别的网格阻塞”&#xff08; Pattern-Based Blocking on grid graphs&#xff0c;PBGG&#xff09;的预处理方法&#xff0c;以加快最…

【康耐视国产案例】智能AI相机联合OSARO为Zenni眼镜实现订单履约自动化

在电商潮流下&#xff0c;Zenni眼镜作为全球领先的在线眼镜零售商&#xff0c;每年销售超过600万副眼镜&#xff0c;却面临着一个独特而复杂的问题——需要通过扫描眼镜盒内的条形码来处理订单。传统手动处理已经到达流程瓶颈&#xff0c;急需一种更加自动化、可扩展的方法。为…

小数第n位【蓝桥杯】

小数第n位 模拟 思路&#xff1a;arr数组用来记录已经出现过的a&#xff0c;在循环时及时退出。易知题目的3位即a%a后的第n-1,n,n1位。该代码非常巧妙&#xff0c;num记录3位的输出状况。 #include<iostream> #include<map> using namespace std; typedef long l…

Python实现定时任务的三种方案——schedule、APScheduler、Celery

schedule schedule是一个轻量级的Python库&#xff0c;用于定期执行任务&#xff0c;即定时任务调度。它提供了一种简单直观的方式来自定义任务执行的时间规则&#xff0c;而无需复杂的线程或进程管理知识。schedule适用于那些需要在后台定期执行某些功能的Python应用程序&…

【Tlias智能学习辅助系统】03 部门管理 前后端联调

Tlias智能学习辅助系统 03 部门管理 前后端联调 前端环境 前端环境 链接&#xff1a;https://pan.quark.cn/s/8720156ed6bf 提取码&#xff1a;aGeR 解压后放在一个不包含中文的文件夹下&#xff0c;双击 nginx.exe 启动服务 跨域的问题已经被nginx代理转发了&#xff0c;所以…

推荐一款开源电子签章/电子合同系统

文章目录 前言一、项目介绍二、项目地址三、技术架构四、代码结构介绍五、功能模块六、功能界面首页面手写签名面板电子印章制作数字证书生成 总结 前言 大家好&#xff01;我是智航云科技&#xff0c;今天为大家分享一个免费开源的电子签字系统。 一、项目介绍 开放签电子签…

MyBatis延迟加载缓存分页逆向工程

文章目录 延迟加载概述步骤 缓存一级缓存介绍原理 二级缓存介绍 设置缓存对象策略原理开启步骤属性解释是否使用一级缓存 分页插件使用步骤 逆向工程介绍搭建使用增删修改查 延迟加载 概述 延迟加载本身是依赖于多表查询的 延迟加载中返回值要选择resultMap返回的结果一定是D…

[ROS 系列学习教程] 建模与仿真 - Xacro 语法

ROS 系列学习教程(总目录) 本文目录 一、属性与属性块二、数学表达式三、宏3.1 宏的基本使用3.2 属性块做为宏的入参3.3 任意数量元素做为宏的入参3.4 指定多个块元素的处理顺序3.5 宏嵌套3.6 默认参数3.7 局部属性 四、Rospack 命令五、包含其他 xacro 文件六、条件语句七、YA…

连锁超市能源能效管理方案

1.概述 连锁超市的能源消耗是其运营成本的重要组成部分。有效的能源能效管理不仅可以降低运营成本&#xff0c;也有助于环保&#xff0c;实现可持续发展。本文将探讨几种关键的能源能效管理策略。 2.智能照明系统 采用智能照明系统是提高能效的关键一步。这些系统可以根据店…

运维开发详解之指标收集

一、指标收集 运维开发中的指标收集是指收集、监控和分析系统运行的各种指标数据&#xff0c;用于评估系统的性能、健康状况和可靠性。这些指标可以包括服务器的 CPU 使用率、内存利用率、磁盘空间使用情况、网络流量等等。 指标收集的目的是为了及时发现系统存在的问题&…

【题解 | 分享】2023年十四届蓝桥杯国赛(Java B组)

互质 答案&#xff1a;640720414 参考&#xff1a; public class Main {static int mod 1000000007;public static void main(String[] args) {long sum power(2023, 2023);long p1 ((sum % mod) * power( 7, mod - 2)) % mod;long p2 ((sum % mod) * power( 17, mod -…

【Python爬虫--scrapy+selenium框架】超详细的Python爬虫scrapy+selenium框架学习笔记(保姆级别的,非常详细)

六&#xff0c;selenium 想要下载PDF或者md格式的笔记请点击以下链接获取 python爬虫学习笔记点击我获取 Scrapyselenium详细学习笔记点我获取 Python超详细的学习笔记共21万字点我获取 1&#xff0c;下载配置 ## 安装&#xff1a; pip install selenium## 它与其他库不同…

docker compose完成简单项目部署

1. 项目环境 centos7 docker mysql redis ruoyi项目 ruoyi项目链接&#xff1a;https://gitee.com/y_project/RuoYi-Vue.git 2. 进行项目前后端代码打包 后端打包&#xff1a; 修改mysql连接的相关配置文件 RuoYi-Vue/ruoyi-admin/src/main/resources/application-dru…

软件和系统集成项目确认测试报告的费用需要多少?

确认测试报告 软件和系统集成项目确认测试报告的费用因多种因素而异&#xff0c;包括项目的规模、复杂度、测试范围、测试周期等。第三方软件测试机构价格区间一般是几千到几万不等&#xff0c;还有些会根据建设费用的2-5%进行收费。 一般来说&#xff0c;软件和系统集成项目…

2024年5月架构试题

2024年5月份架构师考试真题完整版 截至2024-5-28 19:24:14已全部收录完成 共75道选择题&#xff0c;5道案例题&#xff0c;4道论文题。题目顺序不分先后。 全网最全的2024年5月份架构师考试真题回忆版&#xff0c;包含答案和解析。 选择题 计算机基础 操作系统调度算法 选先来先…