爬虫基础总结 —— 附带爬取案例
Crawler —— Learning experience
数据的传输:
在OSI七层模型中,传输层为源主机和目标主机之间提供可靠的数据传输和通信服务,在该层中,有两个重要的协议—— TCP与 UDP协议。
TCP协议(传输控制协议)
主要特点:
- 面向连接:TCP是一个面向连接的协议,这意味着在数据传输之前,发送方和接收方之间必须先建立一个可靠的连接。这个连接通过三次握手(Three-way Handshake)过程来建立,确保双方都已准备好进行数据传输。
- 可靠传输:TCP通过一系列机制来确保数据的可靠传输。
- 流量控制:TCP使用滑动窗口协议进行流量控制,防止发送方发送的数据量超过接收方的处理能力。
- 拥塞控制:TCP还包含拥塞控制机制,通过动态调整发送窗口大小来防止网络拥塞。
- 面向字节流:TCP将数据视为一个连续的字节流,而不是独立的数据报。
UDP协议(用户数据报协议)
主要特点:
- 无连接:UDP是一个无连接的协议,发送方和接收方在数据传输之前不需要建立连接。这简化了协议的实现,并减少了延迟。
- 不可靠传输:UDP不提供确认、超时和重传机制,因此它不保证数据的可靠传输。数据可能会丢失、重复或乱序到达。
- 面向报文:UDP将每个应用层数据报视为一个独立的单元,保留了数据报的边界。
- 低开销:由于UDP没有复杂的连接建立和流量控制机制,它的开销较低,适用于对实时性要求较高但对可靠性要求不高的应用。
- 支持多播和广播:UDP支持多播和广播,允许数据同时发送给多个接收方。
区别:
- TCP:面向连接、可靠传输、流量控制和拥塞控制,适用于需要可靠传输的应用。
- UDP:无连接、不可靠传输、低开销,适用于对实时性要求较高且对少量数据丢失不敏感的应用。
socket(套接字)
Python 的 socket
库提供了对 BSD socket 接口的访问,它允许你进行网络通信。socket
库支持多种类型的通信协议,包括 TCP、UDP 等。
基础知识:
- Socket(套接字):Socket用于表示网络通信的端点。在网络通信中,每个参与通信的程序都需要一个套接字来发送和接收数据。
- Address(地址):每个网络通信的参与者都有一个唯一的网络地址,通常由 IP 地址和端口号组成。
- Protocol(协议):定义了数据如何传输的规则,如 TCP、UDP。
socket
是 Python 的标准库之一,不需要额外安装,可直接导入使用。
import socket
主要函数:
函数名称 | 含义 |
---|---|
socket.socket() | 创建一个新的套接字 |
socket.bind() | 将套接字绑定到指定的地址上 |
socket.listen() | 使套接字进入监听状态,等待客户端连接 |
socket.accept() | 接受客户端连接 |
socket.connect() | 客户端使用,用于连接到服务器 |
socket.send() | 发送数据 |
socket.recv() | 接收数据 |
socket.close() | 关闭套接字 |
socket函数详解:
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
- 作用:创建一个新的套接字
- 参数:
family
:指定通信协议族,如AF_INET
(IPv4) 或AF_INET6
(IPv6)。type
:指定套接字类型,如SOCK_STREAM
(TCP) 或SOCK_DGRAM
(UDP)。proto
:指定协议,通常设置为0,表示使用默认协议。fileno
:如果指定,将现有的文件描述符包装为套接字对象。
socket.bind(address)
- 作用:将套接字绑定到指定的地址。
- 参数:
address
:一个元组(host, port)
,指定IP地址和端口号。
socket.listen(backlog)
backlog
:指定在拒绝连接之前,可以挂起的最大连接数。
socket.accept()
- 作用:使套接字进入监听状态,等待客户端连接。
- 参数:
- 返回值:返回一个新的套接字对象和客户端的地址。
socket.connect(address)
- 作用:客户端使用,用于连接到服务器。
- 参数:
address
:一个元组(host, port)
,指定服务器的IP地址和端口号。
socket.send(data, flags=0)
- 作用:发送数据。
- 参数:
data
:要发送的数据,通常是一个bytes对象。flags
:通常设置为0,表示使用默认行为。
socket.recv(bufsize, flags=0)
- 作用:接收数据。
- 参数:
bufsize
:指定接收数据的最大字节数。flags
:通常设置为0,表示使用默认行为。
socket.sendto(data, address)
- 作用:发送数据到指定地址,通常用于UDP。
- 参数:
data
:要发送的数据。address
:一个元组(host, port)
,指定接收方的地址。
socket.recvfrom(bufsize, flags=0)
作用:接收数据和发送方的地址,通常用于UDP。
参数:
bufsize
:指定接收数据的最大字节数。flags
:通常设置为0,表示使用默认行为。
socket.close()
- 作用:关闭套接字
socket.getpeername()
- 作用:返回连接到套接字的远程地址。
socket.getsockname()
- 作用:返回套接字自己的地址。
socket.setsockopt(level, optname, value)
作用:设置套接字选项。
参数:
level
:指定协议级别,如SOL_SOCKET
。
optname
:指定要设置的选项名称,如SO_REUSEADDR
。
value
:指定选项的值。
socket.getsockopt(level, optname)
作用:获取套接字选项的值。
参数:
level
:指定协议级别。
optname
:指定要获取的选项名称。
socket.shutdown(how)
作用:关闭套接字的一个方向。
参数:
how
:指定关闭的方向,SHUT_RD
表示关闭接收方向,SHUT_WR
表示关闭发送方向,SHUT_RDWR
表示关闭双向。
TCP网络通讯实例:
server.py
import socketdef server_program():# 获取主机名host = socket.gethostname()port = 5000 # 设置访问的端口号# 创建实例server_socket = socket.socket()# 绑定地址server_socket.bind((host, port))# 配置套接字,最多连接1个客户端server_socket.listen(1)print("Waiting for a connection...")# 进入监听状态conn, address = server_socket.accept()print("Connection from: " + str(address))while True:# 接收数据流,设置缓冲大小data = conn.recv(1024).decode()if not data:# 如果没有数据,跳出循环breakprint("From connected user: " + str(data))# 发送数据conn.send(data.encode())conn.close() # 关闭连接if __name__ == '__main__':server_program()
client.py
import socketdef client_program():# 使用本地主机创建sockethost = socket.gethostname()# 服务器的端口号port = 5000# 实例化套接字client_socket = socket.socket()# 连接到服务器client_socket.connect((host, port))message = input(" ->: ") # 用户输入信息while message.lower().strip() != 'bye.':client_socket.send(message.encode()) # 发送消息# 接收响应data = client_socket.recv(1024).decode() # 显示接收到的信息print("Received from server: " + data) message = input(" -> ") # 用户再次输入信息client_socket.close() # 关闭连接if __name__ == '__main__':client_program(
运行结果:
UDP网络通讯实例:
server.py
import socketdef udp_server():# 本地主机地址host = '127.0.0.1'# 端口号port = 12345# 使用UDP协议进行创建socket实例server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 绑定地址server_socket.bind((host, port))print("UDP server up and listening")while True:# 缓冲大小设为4096字节data, address = server_socket.recvfrom(4096) print(f"Received message: {data.decode()} from {address}")# sent 表示发送的字节数sent = server_socket.sendto(data, address)print(f"Sent {sent} byte(s) back to {address}")if __name__ == '__main__':udp_server()
client.py
import socketdef udp_client():# 服务器的主机地址host = '127.0.0.1' # 服务器的端口号port = 12345 client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)message = input("Enter message to send: ")while message.lower().strip() != 'exit':client_socket.sendto(message.encode(), (host, port))# 缓冲大小设为4096字节data, server = client_socket.recvfrom(4096) print(f"Received from server: {data.decode()}")message = input("Enter message to send: ")client_socket.close()if __name__ == '__main__':udp_client()
运行结果:
简单的网络架构
Python 网络爬虫技术:
HTTP
请求:urllib
:Python的标准库之一,用于处理URL和发送HTTP请求Requests
:(重要)一个非常流行的第三方库,用于发送HTTP请求。它比urllib
更易用,提供了简洁的 API。
- 网页解析:
re
:正则表达式库,是 Python 的标准库之一,允许你执行诸如字符串搜索、替换、分割和匹配等操作。lxml
:(重要)一个高性能的XML和HTML解析库,基于C语言实现,解析速度快。Beautiful Soup
:是一个利用Python标凑库构建的库,专门用于解析HTML和XML文档。它提供了简单直观的API来处理文档,并且能够自动处理文档的编码问题。
- 爬虫框架:
Scrapy
:一个用Python编写的开源网络爬虫框架,它被设计用于抓取网站数据和提取结构化数据。Scrapy使用Twisted异步网络框架来处理网络通信,这使得它能够快速地进行数据下载。PySpider
: 是一个强大的、开源的 Python 网络爬虫系统Cola
:是一个企业级应用架构的最佳实践,它旨在简化应用架构的复杂性,提供清晰的指导和约束。
urllib
主要函数:
函数名称 | 含义 |
---|---|
urllib.request.urlopen() | 打开一个 URL 并返回一个类似文件的对象 |
urllib.request.Request(url: str, headers: dict) | 用于创建一个请求对象,可以添加 HTTP 请求头等信息 |
urllib.parse.urlparse(url: str) | 将 URL 分解成组件 |
urllib.parse.urlunparse(url_ls: list) | 将分解后的组件重新组合成一个 URL |
urllib.parse.quote() | 将字符串进行 URL 编码 |
urllib.parse.unquote() | 解码 URL 编码的字符串 |
urllib.error.URLError | 当无法打开 URL 时抛出 |
urllib.error.HTTPError | 当 HTTP 请求返回错误响应时抛出 |
urllib.robotparser | 用于解析 robots.txt 文件,这些文件用于告诉网络爬虫哪些页面可以访问 |
实例
example_1.py
import urllib.request# 请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36'}
# 创建一个 Request 对象
req = urllib.request.Request('http://www.baidu.com', headers=headers)# 打开 URL
with urllib.request.urlopen(req, timeout=3) as response:# 读取响应内容html = response.read()# 打印 HTML 内容
print(html.decode('utf-8'))
对于响应的response
,可以使用response.geturl()
来获取当前所爬取的 URL 地址,还可以使用response.getcode()
来查看网页的状态码。
example_2.py
from urllib.parse import urlencode# 创建一个字典,包含查询参数
params = {'q': 'Python','page': 1
}# 编码查询字符串
query_string = urlencode(params)# 打印编码后的查询字符串
print(query_string)
post.py
# 发送post请求
from urllib import request, parse
post_data = parse.urlencode([('key1', 'value1'),('key2', 'value2')])url = request.Request('http://httpbin.org/post')
url.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36')
response = request.urlopen(url, data=post_data.encode('utf-8')).read()
robots.py
from urllib.robotparser import RobotFileParser# 创建 RobotFileParser 对象
rp = RobotFileParser()# 设置 robots.txt 文件的 URL
rp.set_url("http://www.baidu.com/robots.txt")# 读取并解析 robots.txt 文件
rp.read()# 检查某个用户代理是否可以获取指定的页面
can_fetch = rp.can_fetch("*", "http://www.baidu.com/somepage.html")
print(can_fetch)'''
can_fetch() 方法接受两个参数:
第一个是用户代理字符串(通常是 * 表示所有用户代理),第二个是想要访问的 URL。
如果返回 True,则表示可以抓取;如果返回 False,则表示不可以抓取。
'''
requests
requests 库是一个第三方库,需要自行安装。
pip3 install requests # python3
主要函数
发送请求:
requests.get(url, params=None, \**kwargs)
:- 发送一个 HTTP GET 请求。
params
: 一个字典或字节序列,将会被发送在查询字符串中。**kwargs
: 其他关键字参数将传递给Request
类。
requests.post(url, data=None, json=None, \**kwargs)
:- 发送一个 HTTP POST 请求。
data
: 要发送的表单数据,字典或字节序列。json
: 要发送的 JSON 数据。**kwargs
: 其他关键字参数将传递给Request
类。同 get 请求。
requests.put(url, data=None, \**kwargs)
:- 发送一个 HTTP PUT 请求。
data
: 要发送的请求数据。**kwargs
: 其他关键字参数将传递给Request
类。同 get 请求。
requests.delete(url, \**kwargs)
:- 发送一个 HTTP DELETE 请求。
**kwargs
: 关键字参数将传递给Request
类。同 get 请求。
requests.head(url, \**kwargs)
:- 发送一个 HTTP HEAD 请求。
**kwargs
: 关键字参数将传递给Request
类。同 get 请求。
requests.patch(url, data=None, \**kwargs)
:- 发送一个 HTTP PATCH 请求。
data
: 要发送的请求数据。**kwargs
: 关键字参数将传递给Request
类。同 get 请求。
requests.options(url, \**kwargs)
:- 发送一个 HTTP OPTIONS 请求。
**kwargs
: 关键字参数将传递给Request
类。同 get 请求。
上述发送请求的函数中可选参数 \**kwargs
基本相同,因为它们最终都会传递给 requests
库内部的 Request
类,常见的 **kwargs
参数:
params
: 用于传递 URL 的查询字符串参数。可以是字典、字节序列或元组列表。headers
: 自定义 HTTP 请求头。cookies
: 用于发送 cookies,可以是字典或RequestsCookieJar
对象。timeout
: 指定请求的超时时间。auth
: 用于 HTTP 认证的元组(username, password)
。proxies
: 指定代理服务器的字典。verify
: 用于控制 SSL 证书验证,可以是布尔值或证书路径。stream
: 如果为False
,则响应内容将立即下载。allow_redirects
: 是否自动处理重定向。data
: 用于requests.post
和其他方法,但对于requests.delete
方法,你也可以通过**kwargs
传递。json
: 用于requests.post
方法,但通常不用于requests.delete
,除非你明确需要发送 JSON 数据。files
: 用于上传文件。
import requests# 豆瓣电影API的URL,这里以获取电影《肖申克的救赎》的详细信息为例
movie_id = '1292052' # 肖申克的救赎的豆瓣ID
url = f'https://movie.douban.com/subject/{movie_id}'headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36'}
# 发送GET请求
response = requests.get(url, headers=headers, timeout=3)# 检查请求是否成功
if response.status_code == 200:print(response.text)
else:print("请求失败,状态码:", response.status_code)# 处理可能的异常
try:response.raise_for_status()
except requests.exceptions.HTTPError as e:print("HTTP错误:", e)
except requests.exceptions.ConnectionError as e:print("连接错误:", e)
except requests.exceptions.Timeout as e:print("请求超时:", e)
except requests.exceptions.RequestException as e:print("请求出错:", e)
返回内容:
使用 requests
进行请求时,返回一个response
对象,该对象包含以下属性:
status_code
:服务器响应的HTTP状态码。headers
:服务器响应的HTTP头部字段。content
:服务器响应的内容,通常是字节类型。text
:服务器响应的内容,解码为字符串(默认使用ISO编码)。json()
:如果响应内容是JSON格式,这个方法可以解析JSON并返回一个字典。
会话管理:
Session
对象允许你在多个请求之间保持某些参数,比如 cookies、headers 等。session.get(url, **kwargs)
: 在会话中发送一个 GET 请求。session.post(url, data=None, json=None, **kwargs)
: 在会话中发送一个 POST 请求。
import requests# 创建一个 Session 对象
session = requests.Session()# 你可以设置一些默认的请求参数
session.headers.update({'user-agent': 'my-app/0.0.1'})# 发送第一个请求
response_one = session.get('http://www.baidu.com')
print(response_one.text)# 发送第二个请求,Session 会自动处理 cookies
response_two = session.get('https://blog.csdn.net/')
print(response_two.text)# 关闭 Session
session.close()
请求构造:
-
Request(method, url, **kwargs)
: 创建一个请求对象,可以用于Session
对象的send
方法。- 参数详解:
- method:指定请求的 HTTP 方法。这是必需的。
- url:请求的目标 URL。这也是必需的。
- params:字典或字节序列,将会被编码为 URL 的查询字符串。
- data:作为请求体发送的字典、字节序列或文件对象。
- json:字典或列表,将会被编码为 JSON 格式并发送。
- headers:字典,包含请求头。
- cookies:字典或 CookieJar 对象,包含要发送的 cookies。
- files:字典,包含要上传的文件。
- auth:元组,包含用户认证信息。
- timeout:浮点数或元组,指定请求的超时时间。
import requests# 创建一个 Request 对象 req = requests.Request('GET', 'http://example.com')# 添加额外的参数 req.headers.update({'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36'})# 准备请求 prepared_req = req.prepare()# 创建一个 Session 对象 with requests.Session() as session:# 使用 Session 的 send 方法发送准备好的请求response = session.send(prepared_req)print(response.text)
- 参数详解:
响应对象:
- 每个请求方法返回一个
Response
对象,包含以下属性和方法:status_code
: HTTP 状态码。headers
: 响应头。text
: 响应体作为 Unicode。content
: 响应体作为字节。json()
: 解析 JSON 响应体。raise_for_status()
: 如果响应码指示错误,则抛出一个异常。
异常处理:
-
requests
定义了一系列异常,例如HTTPError
,ConnectionError
,Timeout
,RequestException
等,用于处理请求中可能出现的错误。- 常见的 requests 异常:
- RequestException:这是所有
requests
异常的基类。通常,你可以通过捕获这个异常来处理所有请求相关的错误。 - HTTPError:当响应的 HTTP 状态码表示错误时(例如 4xx 或 5xx),会抛出这个异常。
- ConnectionError:当网络连接问题导致请求失败时,会抛出这个异常。
- Timeout:当请求超过指定的超时时间时,会抛出这个异常。
- TooManyRedirects:当请求超过了最大重定向次数时,会抛出这个异常。
- MissingSchema、InvalidSchema、InvalidURL:这些异常与 URL 格式不正确有关。
- ChunkedEncodingError:当服务器声明使用分块传输编码,但实际上没有正确发送数据时,会抛出这个异常。
- ContentDecodingError:当服务器返回的内容无法正确解码时,会抛出这个异常。
- StreamConsumedError:当尝试多次读取已经读取过的响应内容时,会抛出这个异常。
- RequestException:这是所有
import requests from requests.exceptions import HTTPError, ConnectionError, Timeouturl = 'http://example.com'try:response = requests.get(url, timeout=5)# 确保响应状态码为 200response.raise_for_status() except HTTPError as http_err:print(f'HTTP error occurred: {http_err}') # HTTP错误 except ConnectionError as conn_err:print(f'Connection error occurred: {conn_err}') # 连接错误 except Timeout as timeout_err:print(f'Timeout error occurred: {timeout_err}') # 超时错误 except Exception as err:print(f'Other error occurred: {err}') # 其他错误 else:print('Success!')
- 常见的 requests 异常:
-
高级特性:
- 支持代理、Cookie、认证、SSL 证书验证等高级特性。
proxies
import requests# 代理服务 proxies = {'http': 'http://10.10.1.10:3128','https': 'http://10.10.1.10:1080', }response = requests.get('http://example.com', proxies=proxies) print(response.text)
Cookie.py
import requests# 手动设置 cookies cookies = dict(cookies_are='working')response = requests.get('http://example.com', cookies=cookies) print(response.text)
SSl.py
import requests# 禁用 SSL 证书验证 response = requests.get('https://example.com', verify=False)# 使用自定义的证书文件, verify 是SSL证书路径 response = requests.get('https://example.com', verify='/path/to/certfile')
lxml
lxml
是一个 Python 第三方库,它提供了非常高效的 XML 和 HTML 文件解析工具。lxml
基于 libxml2 和 libxslt 库,这些是 C 语言编写的高性能、低内存占用的库,因此 lxml
在解析大型文件时尤其有用。
安装:
lxml
是一个第三方库,需要自行安装。
pip3 install lxml # python3
使用方法:
-
导入
lxml.etree
模块:from lxml import etree
-
解析 HTML 时,需要先初始化 HTML 源码:
selector = etree.HTML(html) # html 表示爬取到的 HTML 源码。
-
寻找某个标签的时候,可以从根节点进行爬取,默认根节点为
//
,如要爬取<li>
标签,且该标签的路径为:<div> => <ul> => <li>
。all_li = selector.xpath('//div/ul/li') # 爬取到 <ul> 路径下的所有的 <li> 标签。
-
定位到具体的某一个标签,可以在参数最后使用
[idx]
来定位,序号从1开始如:li_1 = selector.xpath('//div/ul/li[1]')
-
提取某标签下的文本,可以在参数最后使用一级
/text()
来提取,返回一个列表对象,如提取第一个<li>
标签下的<a>
标签的文本:li_1_a_text = selector.xpath('//div/ul/li[1]/text()')
-
如果某一个标签在 HTML 源码中时唯一的,可以直接从根节点定位到该标签,而不需要一级一级的定位,如
<ul>
标签是唯一的,可以直接定位到<ul>
:li_1_a_text = selector.xpath('//ul/li[1]/text()')[0] # 这里 [0] 表示提取返回列表的第一个值。
-
通过属性定位标签,可以在对应标签后使用
[@class="..."]
来定位某个具体的标签:li_3 = selector.xpath('//ul/li[@class="item-inactive"]')
-
如果某个标签对应的属性在整个 HTML 源码里是唯一的,可以从根节点直接定位到该标签:
li_3 = selector.xpath('//*[@class="item-inactive"]') # * 表示任意标签li_3_a_text = selector.xpath('//*[@class="item-inactive"]/a/text()') # 提取 a 标签下的文本li_3_a_text = selector.xpath('//a[@class="item-inactive"]/text()') # 直接使用 a 标签的属性进行定位
-
提取某标签的属性值,如果要提取属性值,可以直接在参数中添加一级属性的名称即可,如:
li_3_a_href = selector.xpath('//ul/li[3]/a/@href') # 提取出第三个 li 标签的属性all_class = selector.xpath('//li/@class') # 提取所有 li 标签的 class 属性
-
提取以某部分开头的属性,如多个属性值都以
item-
开头,可以使用starts-with()
语法进行提取,如:li_1_5 = selector.xpath('//li[starts-with(@class, "item-")]')
-
如果提取出来的元素包含的仍然是一个代码段,可以对它继续使用 XPath 进行查找,可以将当前的节点作为根节点来进行查找,使用
.//
表示相对根节点:li_1_5_a_text = [] for li in li_1_5:li_1_5_a_text.append(li.xpath('a/text()')[0])# 下面的代码等价于上面的 li_1_5_a_text = selector.xpath('//li[starts-with(@class=, "item-")/a/text()]')
-
提取某标签下的所有文本,如 提取
<ul>
标签下的所有标签的文本:all_text = selector.xpath('string(//ul)')# 使用列表推导式来提取所有文本 all_str_text = [s.strip() for s in all_text.strip().split('\n')]
Beautiful Soup
Beautiful Soup 是一个用于解析 HTML 和 XML 文档的 Python 库。它常被用于网页抓取和数据提取。
安装:
Beautiful Soup
是一个第三方库,需要自行安装。
pip3 install beautifulsoup4
使用方法:
-
导入
BeautifulSoup
模块:from bs4 import BeautifulSoup
-
创建解析器,第一个参数是 HTML 源码,第二个参数是解析器类型:
soup = BeautifulSoup(html_doc, 'html.parser') # 使用 Python 的内置解析器 soup = BeautifulSoup(html_doc, 'lxml') # 使用 lxml 解析器 soup = BeautifulSoup(html_doc, 'html5lib') # 使用 html5lib 解析器
如果要使用
lxml
解析器 或html5lib
解析器,需要额外进行安装:pip install lxml pip3 install html5lib
-
获取所有具有特定类的
<a>
标签:links = soup.find_all('a', class_='class-name')
-
使用 CSS 选择器,
select()
方法,基本语法为elements = soup.select('CSS_SELECTOR')
,CSS_SELECTOR
是一个字符串,表示你想要使用的 CSS 选择器:-
标签选择器:通过标签名选择元素。
soup.select('div') # 选择所有 <div> 标签
-
类选择器:通过类名选择元素。
soup.select('.class-name') # 选择所有类名为 "class-name" 的元素
-
ID 选择器:通过 ID 选择特定的元素。
soup.select('#id-name') # 选择 ID 为 "id-name" 的元素
-
属性选择器:通过元素的属性选择元素。
soup.select('a[href="http://example.com"]') # 选择 href 属性为 "http://example.com" 的所有 <a> 标签
-
组合选择器:组合多个选择器来细化搜索条件。
soup.select('div.class-name#id-name') # 选择类名为 "class-name" 且 ID 为 "id-name" 的 <div> 标签
-
后代选择器:使用空格分隔,选择作为某元素后代的所有元素。
soup.select('div a') # 选择所有在 <div> 标签内的 <a> 标签
-
子元素选择器:使用
>
符号,选择作为某元素直接子元素的元素。soup.select('div > a') # 选择所有 <div> 标签的直接子元素 <a> 标签
-
相邻兄弟选择器:使用
+
符号,选择紧随指定元素之后的相邻兄弟元素。soup.select('a + p') # 选择所有紧随 <a> 标签之后的 <p> 标签
-
通用兄弟选择器:使用
~
符号,选择指定元素之后的所有兄弟元素。soup.select('a ~ p') # 选择所有在 <a> 标签之后的所有 <p> 标签
-
伪类选择器:使用 CSS 伪类选择器来选择元素。
soup.select('p:first-child') # 选择每个父元素的第一个 <p> 子元素
-
返回值
select()
方法返回一个列表,其中包含所有匹配 CSS 选择器的元素。如果没有找到匹配的元素,它将返回一个空列表。divs = soup.select('div.class-name')
-
-
搜索文档树:
-
find()
:返回第一个匹配的元素,语法:tag = soup.find(name, attrs, recursive, text, **kwargs)
,每一个参数都是一个可选参数。-
name:
- 字符串或
None
。 - 要搜索的标签名。如果设定为
None
,则忽略此参数。
- 字符串或
-
attrs:
- 字典类型。
- 要搜索的标签的属性。字典的键是属性名,值是属性值。只有当标签具有这些属性时,它才会被匹配。
-
recursive:
- 布尔值。
- 指定搜索是否包括子孙标签。默认为
True
,如果设置为False
,则只在当前标签的直接子标签中搜索。
-
text:
- 字符串或
None
。 - 要搜索的标签的文本内容。如果设定为
None
,则忽略此参数。
- 字符串或
-
kwargs:
- 关键字参数。
- 任何额外的关键字参数都会传递给解析器。
-
find()
方法只返回第一个匹配的元素。如果你想要找到所有匹配的元素,应该使用find_all()
方法。
from bs4 import BeautifulSoup# 示例 HTML 文档 html_doc = """ <html> <head><title>The title of the document</title> </head> <body><div id="div1" class="someclass"><p>This is the first paragraph</p><a href="http://example.com">Example link</a></div><div id="div2" class="someclass"><p>This is the second paragraph</p><a href="http://example.org">Example link 2</a></div> </body> </html> """# 创建 Beautiful Soup 对象 soup = BeautifulSoup(html_doc, 'html.parser')# 通过标签名查找 p_tag = soup.find('p') print(p_tag) # 输出第一个 <p> 标签# 通过属性查找 a_tag = soup.find('a', href=True) print(a_tag) # 输出第一个有 href 属性的 <a> 标签# 通过属性字典查找 a_tag_with_specific_href = soup.find('a', attrs={'href': 'http://example.com'}) print(a_tag_with_specific_href) # 输出具有特定 href 属性的 <a> 标签# 通过文本查找 text_tag = soup.find(text="This is the first paragraph") print(text_tag) # 输出包含特定文本的第一个标签# 使用关键字参数查找 div_tag = soup.find(id="div1") print(div_tag) # 输出具有特定 id 属性的 <div> 标签
-
-
find_all()
:返回所有匹配的元素列表,语法:find_all(name, attrs, recursive, string, limit, **kwargs)
, 每一个参数都是可选参数。- name:
- 字符串或正则表达式。
- 要搜索的标签名。可以使用正则表达式来匹配标签名。
- attrs:
- 字典类型。
- 要搜索的标签的属性。字典的键是属性名,值是属性值。只有当标签具有这些属性时,它才会被匹配。
- recursive:
- 布尔值。
- 指定是否递归地在子标签中查找。默认为
True
。
- text:
- 字符串或正则表达式。
- 要搜索的标签的文本内容。
- limit:
- 整数。
- 用于限制返回结果的数量。
- kwargs:
- 关键字参数。
- 任何额外的关键字参数都会被视为要匹配的元素的属性名和属性值。
find_all()
方法返回一个列表,如果没有找到匹配的元素,则返回一个空列表。
from bs4 import BeautifulSouphtml_doc = """ <html> <head><title>The Dormouse's story</title> </head> <body><div id="div1" class="someclass"><p>This is the first paragraph</p><a href="http://example.com">Example link</a></div><div id="div2" class="someclass"><p>This is the second paragraph</p><a href="http://example.org">Example link 2</a></div> </body> </html> """soup = BeautifulSoup(html_doc, 'html.parser')# 查找所有 <p> 标签 p_tags = soup.find_all('p') for p in p_tags:print(p)# 查找所有具有特定类的 <div> 标签 divs_with_class = soup.find_all('div', class_='someclass') for div in divs_with_class:print(div)# 查找所有包含特定文本的 <a> 标签 links_with_text = soup.find_all('a', text='Example link') for link in links_with_text:print(link)# 使用属性字典查找 divs_with_attrs = soup.find_all('div', attrs={'class': 'someclass'}) for div in divs_with_attrs:print(div)# 使用 limit 参数限制结果数量 limited_results = soup.find_all('p', limit=1) for result in limited_results:print(result)
- name:
-
-
获取和修改标签属性:
tag = soup.find('a', href=True) tag['href'] = 'http://newlink.com'
-
获取标签内的文本:
text = tag.get_text()
-
获取去除了标签的文本内容:
stripped_text = tag.get_text(strip=True)
-
添加新的标签:
from bs4 import BeautifulSoup# 假设我们有一段简单的 HTML html_doc = "<html><body></body></html>" soup = BeautifulSoup(html_doc, 'html.parser')# 创建一个新的 <p> 标签 new_tag = soup.new_tag('p')# 设置新标签的内容 new_tag.string = 'New paragraph'# 将新标签添加到文档的 <body> 部分 soup.body.append(new_tag)# 打印修改后的 HTML print(soup.prettify())
-
替换现有的标签:
tag.replace_with('New text')
-
格式化输出 HTML:
print(soup.prettify())
-
指定编码输出:
from bs4 import BeautifulSoup# 假设我们有一段简单的 HTML html_doc = "<html><head><title>Page Title</title></head><body><p>Some content</p></body></html>" soup = BeautifulSoup(html_doc, 'html.parser')# 将解析后的 HTML 文档转换为字符串 html_string = str(soup)# 使用字符串的 encode 方法进行编码 encoded_data = html_string.encode('latin-1')# 打印编码后的数据 print(encoded_data)
-
如果解析器无法解析文档,可以尝试更换解析器,例如从
html.parser
切换到lxml
或html5lib
。 -
使用
lxml
解析器可以提高解析速度。 -
如果处理非 UTF-8 编码的文档,可以在创建 Beautiful Soup 对象时指定
from_encoding
参数。
re 正则表达式
简介
正则表达式(Regular Expression,简称regex或regexp)是一种文本模式描述的方法,它可以用来检索、替换符合某个模式(规则)的文本。正则表达式由一系列字符组成,这些字符可以是普通字符(例如,字母a到z)、特殊字符(称为"元字符")或两者的组合。
学习链接:
博客(CSDN):Re - 正则表达式(附带大量python实例)_python正则匹配条件-CSDN博客
案例一:爬取黑马程序员网址
链接地址:python技术交流论坛 (itheima.com)
目标:爬取每一页的标题、作者、日期
第三方库:
requests
lxml
random
time
标题:
通过浏览器的调试功能(F12 快捷键)可以定位到标题元素在源码中的位置:
可以看到标题元素在<a>
标签中,并且该标签在<table id = "threadlisttableid">
标签下,并且通过观察可以看到所有的标题都在各个<table id = "threadlisttableid">
标签下,并且<table id = "threadlisttableid">
该标签是独立的(相对唯一),因此可以使用属性定位来定位到所有的<table id = "threadlisttableid">
标签,然后在向下搜索得到需要的值,代码编写如下:
title_temp = selector.xpath('//table[@id="threadlisttableid"]/tbody/tr/th/a[@class="s xst"]/text()')
作者:
通过观察可以看到各个作者标签都在在各个<div class="foruminfo">
的标签下,且是独立的(相对唯一),因此同样可以使用属性定位到该标签,之后一个标签为<span>
标签,代码编写如下:
author_temp = selector.xpath('//div[@class="foruminfo"]/i[2]/span/a/text()')
日期:
与作者分析一样,日期标签也在各个<div class="foruminfo">
标签下,因此也可以使用属性定位来找到该标签,不同的是,日期标签是<div class="foruminfo">
下的第二个子标签<i>
,因此可以使用索引的方式来进行定位,代码如下:
date_temp = selector.xpath('//div[@class="foruminfo"]/i[2]/text()')
爬取所有页面:
由于该网址是分页的,一次爬取只能得到一页的数据,但我们需要的是全部的数据,因此需要找到一种方式来爬取所有的页面。
再次分析网页,可以发现,存在下一页按钮,并且下一页按钮对应的源码中,其属性就是下一页的网址,所以我们可以不断的爬取每一页的下一页网页,然后不断请求,直到不存在下一页按钮,即返回一个空列表时,爬取结束,下一页按钮在源码中定位如下:
细心的观察,可以发现,下一页按钮只存在于<a class="nxt">
标签中,因此可以直接使用属性定位直接定位到该标签,然后取其href
属性值即可,代码编写如下:
next_temp = selector.xpath('//a[@class="nxt"]/@href')
如此,我们可以使用函数来封装请求和解析的方法,然后使用while
循环来进行不断的爬取,结束条件为while next_url != []
,这里我们使用random
和time
库,来对爬取的时间进行随机休眠,以削减爬取的频率。
完整代码:
import requests
from lxml import etree
import time
import randomURL = "http://bbs.itheima.com/forum-425-1.html"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/458.22'}
# 请求函数
def Crawling(url: str): print('Crawling URL is :', url)response = requests.get(url=url, headers=headers, timeout=3)if response.status_code == requests.codes.ok:time.sleep(random.uniform(1.0, 2.5)) # 进行1秒 ~ 2.5秒的随机休眠return responseelse:print("Error")return None
# 解析函数
def parse(response_parameter):selector = etree.HTML(response_parameter.content)# 解析标题title_temp = selector.xpath('//table[@id="threadlisttableid"]/tbody/tr/th/a[@class="s xst"]/text()')# 解析日期date_temp = selector.xpath('//div[@class="foruminfo"]/i[2]/text()')# 解析作者author_temp = selector.xpath('//div[@class="foruminfo"]/i[2]/span/a/text()')# 下一页地址next_temp = selector.xpath('//a[@class="nxt"]/@href')if next_temp != []:next_temp = next_temp[0]return title_temp, date_temp, author_temp, next_temp# 进行爬取
next_temp = URL
title_ls = []
date_ls = []
author_ls = []
next_ls = []
while next_temp != []:response = Crawling(url=next_temp)title_temp, date_temp, author_temp, next_temp = parse(response_parameter=response)title_ls.extend(title_temp)date_ls.extend(date_temp)author_ls.extend(author_temp)next_ls.extend(next_temp)# 对数据进行初步处理
title_stand = [f"{item.strip()}\n" for item in title_ls]
date_stand = [f"{item.strip()}\n" for item in date_ls]
date_stand_end = []
for date_item in date_stand:if date_item == '\n':continuedate_stand_end.append(date_item)
author_stand = [f"{item.strip()}\n" for item in author_ls]
# print(title_stand[:5])
# print(date_stand[:5])
# print(author_stand[:5])# 进行持久化存储
with open(r'./黑马网址-标题.txt', 'w', encoding='utf-8') as file:file.writelines(title_stand)with open(r'./黑马网址-作者.txt', 'w', encoding='utf-8') as file:file.writelines(author_stand)with open(r'./黑马网址-日期.txt', 'w') as file:file.writelines(date_stand_end)# 确定每列的最大宽度
max_width_tit = max(len(tit) for tit in title_stand)
max_width_aut = max(len(aut) for aut in author_stand)
max_width_dat = max(len(dat) for dat in date_stand_end)with open(r'./黑马网址-组合.txt', 'w', encoding='utf-8') as file:file.writelines(f'{title.strip():<{max_width_tit}}{author.strip():^{max_width_aut}}{date.strip():>{max_width_dat}} \n' for title, author, date in zip(title_stand, author_stand, date_stand_end))
Python 连接MySQL数据库
python 中可以使用 pymysql
第三方库来连接 MySQL。
安装 pymysql:
pip3 install pymysql
简单的使用方法:
import pymysql# 连接到MySQL数据库
connection = pymysql.connect(host='localhost', # 数据库主机地址user='yourusername', # 数据库用户名password='yourpassword', # 数据库密码database='yourdatabase' # 数据库名称
)try:# 创建一个游标对象with connection.cursor() as cursor:# 执行SQL查询sql = "SELECT * FROM your_table"cursor.execute(sql)# 获取查询结果result = cursor.fetchall()# 打印结果for row in result:print(row)finally:# 关闭数据库连接connection.close()
使用方法详解
0. 基本方法详解
connection.cursor()
:用于创建一个游标对象,以便执行SQL语句和管理结果集。
游标的参数:
cursor = connection.cursor(pymysql.cursors.Cursor) # 默认,返回元组结果。
cursor = connection.cursor(pymysql.cursors.DictCursor) # 返回字典结果,键为列名。
-
cursor.execute()
:用于执行SQL语句。 -
fetchone()
: 获取结果集的下一行。 -
fetchall()
: 获取结果集的所有行。 -
fetchmany(size=None)
: 获取结果集的多行,参数为行数。 -
close()
: 关闭游标。
connection.commit()
:用于提交数据。
1. 连接到数据库
使用 pymysql.connect()
来连接到数据库。
connection = pymysql.connect(host='hostname',user='username',password='password',database='databasename',charset='utf8mb4',cursorclass=pymysql.cursors.DictCursor)
2. 查询数据
try:with connection.cursor() as cursor:sql = "SELECT * FROM your_table"cursor.execute(sql)result = cursor.fetchall()for row in result:print(row)
finally:connection.close()
3. 插入数据
插入一行数据:
try:with connection.cursor() as cursor:sql = "INSERT INTO your_table (column1, column2) VALUES (%s, %s)"cursor.execute(sql, ('value1', 'value2'))connection.commit()
finally:connection.close()
插入多行数据
cursor.executemany(SQL, DATA: list)
来提交插入多行数据,第一个参数是SQL语句,第二个参数是一个列表,列表中每一个元素为一个元组类型的数据,每一个元组类型的数据表示向表中插入一条数据。
try:with connection.cursor() as cursor:# 插入多行数据的SQL语句insert_multiple_sql = """INSERT INTO users (username, email) VALUES (%s, %s)"""# 要插入的数据data = [('user1', 'user1@example.com'),('user2', 'user2@example.com'),('user3', 'user3@example.com')]# 执行插入操作,使用 executemanycursor.executemany(insert_multiple_sql, data)# 提交事务connection.commit()print("多行数据插入成功!")except pymysql.MySQLError as e:print(f"插入数据时发生错误:{e}")connection.rollback() # 出错回滚事务finally:connection.close() # 关闭连接
4. 更新数据
try:with connection.cursor() as cursor:sql = "UPDATE your_table SET column1 = %s WHERE column2 = %s"cursor.execute(sql, ('new_value1', 'value2'))connection.commit()
finally:connection.close()
5. 删除数据
try:with connection.cursor() as cursor:sql = "DELETE FROM your_table WHERE column2 = %s"cursor.execute(sql, ('value2',))connection.commit()
finally:connection.close()
6. 创建一个表
try:with connection.cursor() as cursor:# 创建表的SQL语句create_table_sql = """CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(50) NOT NULL,email VARCHAR(100) NOT NULL UNIQUE,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"""# 执行SQL语句cursor.execute(create_table_sql)# 提交事务connection.commit()print("表创建成功!")except pymysql.MySQLError as e:print(f"创建表时发生错误:{e}")connection.rollback() # 回滚事务以保持数据一致性finally:connection.close() # 关闭连接
7. 处理异常
import pymysqltry:connection = pymysql.connect(host='localhost',user='yourusername',password='yourpassword',database='yourdatabase')with connection.cursor() as cursor:sql = "SELECT * FROM your_table"cursor.execute(sql)result = cursor.fetchall()for row in result:print(row)except pymysql.MySQLError as e:print(f"Error: {e}")finally:if connection.open: # 如果连接保持开启connection.close()
相关文章:
爬虫基础总结 —— 附带爬取案例
Crawler —— Learning experience 数据的传输: 在OSI七层模型中,传输层为源主机和目标主机之间提供可靠的数据传输和通信服务,在该层中,有两个重要的协议—— TCP与 UDP协议。 TCP协议(传输控制协议) …...
图像处理学习笔记-20241118
文章目录 霍夫变换基本原理霍夫变换的步骤使用 OpenCV 实现直线检测示例:标准霍夫变换 示例:概率霍夫变换参数解释霍夫变换检测圆 基于GAN的样本生成GAN的基本原理基于GAN的数据增广流程实现代码示例 同态滤波(Homomorphic Filtering…...
不能打开网页,但能打开QQ、微信(三种方式)
1.VPN错误 下面三个开关全关闭 2.DNS问题 WINR 输入CMD打开命令行 命令行输入 ipconfig/flushdns 重启电脑 3.直接火绒(一键修复)...
使用 start-local 脚本在本地运行 Elasticsearch
警告:请勿将这些说明用于生产部署 本页上的说明仅适用于本地开发。请勿将此配置用于生产部署,因为它不安全。请参阅部署选项以获取生产部署选项列表。 使用 start-local 脚本在 Docker 中快速设置 Elasticsearch 和 Kibana 以进行本地开发或测试。 此设…...
计算机网络:概述知识点及习题练习
网课资源: 湖科大教书匠 1、因特网 网络之间需要路由器进行互联,互联网是网络的网络,因特网是最大的互联网,连接到网络的设备称为主机,一般不叫路由器为主机。 因特网发展:ARPNET->三级结构因特网&am…...
python蓝桥杯刷题2
1.最短路 题解:这个采用暴力枚举,自己数一下就好了 2.门牌制作 题解:门牌号从1到2020,使用for循环遍历一遍,因为range函数无法调用最后一个数字,所以设置成1到2021即可,然后每一次for循环&…...
在openi平台 基于华为顶级深度计算平台 openmind 动手实践
大家可能一直疑问,到底大模型在哪里有用。 本人从事的大模型有几个方向的业务。 基于生成式语言模型的海事航行警告结构化解析。 基于生成式语言模型的航空航行警告结构化解析。 基于生成式生物序列(蛋白质、有机物、rna、dna、mrna)的多模态…...
KF UKF
我需要Kalman 现在,主要是用来处理检测问题情况里的漏检,因为模拟了一段2D, (x,y)的数据,为了看效果,画的线尽量简单一点: import numpy as np import matplotlib.pyplo…...
中伟视界:AI智能分析算法如何针对非煤矿山的特定需求,提供定制化的安全生产解决方案
非煤矿山智能化改造,除了政策文件,上级监管单位需要安装的AI智能分析算法功能之外的,矿方真正关心的,能解决矿方安全生产隐患的AI智能分析算法功能有哪些呢? 经过与矿方的现场交流沟通,收集第一现场人员对安…...
Unity 编辑器下 Android 平台 Addressable 加载模型粉红色,类似材质丢失
Unity 编辑器下 Android 平台 Addressable 加载模型粉红色,类似材质丢失 Addressable Play Mode Script加载模式 选择 Use Existiing Build 1.Unity 切换到 PC 平台,执行 Addressable Build 运行,加载 bundle 内的预制体 显示正常 2.Unit…...
Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 优化场景定义
Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 & 优化场景定义 前言一、功能文件路径的配置1.1 全局设置功能文件路径1.2. 在场景中覆盖路径 二、避免重复输入功能文件名2.1 使用方法2.2 functools.partial 的背景 三、应用场景总…...
rust逆向初探
rust 逆向葵花宝典 rust逆向技巧 rust逆向三板斧: [!NOTE] 快速定位关键函数 (真正的main函数):观察输出、输入,字符串搜索,断点等方法。定位关键 加密区 :根据输入的flag,打硬件断点,快速捕获…...
【Linux】apt 关闭 ssl 认证
【注意】apt 关闭 ssl 认证可能会引起软件安装风险,请尽量避免关闭。 执行以下命令可以实现全局关闭 sll 验证。 echo Acquire::https::Verify-Peer "false"; >> /etc/apt/apt.conf.d/99disable-signature-verificationecho Acquire::https::Verif…...
【算法】P5018 对称二叉树
题目 P5018 对称二叉树 https://www.luogu.com.cn/problem/P5018 代码 思路:领接表存储二叉树,unordered_map存储各个节点对应的值。dfs遍历一下各个子树的大小个数,再写个递归判断是否是对称二叉树,如果是就更新全局答案。 #…...
Unifying Top-down and Bottom-up Scanpath Prediction Using Transformers
Abstract 大多数视觉注意力模型旨在预测自上而下或自下而上的控制,这些控制通过不同的视觉搜索和自由观看任务进行研究。本文提出了人类注意力变换器(Human Attention Transformer,HAT),这是一个能够预测两种形式注意力…...
JavaSE(十四)——文件操作和IO
文章目录 文件操作和IO文件相关概念Java操作文件文件系统操作文件内容操作字节流FileOutputStreamFileInputStream代码演示 字符流FileWriterFileReader代码演示 缓冲流转换流 案例练习 文件操作和IO 文件相关概念 文件 通常指的是包含用户数据的文件,如文本文件、…...
【视觉SLAM】4b-特征点法估计相机运动之PnP 3D-2D
文章目录 0. 前言1. PnP求解1.1 直接线性变换DLT1.2 P3P1.3 光束平差法BA2. 实现0. 前言 透视n点(Perspective-n-Point,PnP)问题是计算机视觉领域的经典问题,用于求解3D-2D的点运动。换句话说,当知道 N N N个世界坐标系中3D空间点的坐标以及它们在图像上的投影点像素坐标…...
android 性能分析工具(04)Asan 内存检测工具
1 Asan工具简介 1.1 Asan工具历史背景 AddressSanitizer(ASan)最初由Google开发,并作为LLVM项目的一部分。ASan的设计目的是帮助开发者检测并修复内存错误,如堆栈和全局缓冲区溢出、使用已释放的内存等,这些问题可能…...
html中select标签的选项携带多个值
搜索参考资料:SELECT标签中的选项可以携带多个值吗? 【摘抄】: 它可能有一个select选项中的多个值,如下所示。 <select id"ddlEmployee" class"form-control"> <option value"">-- S…...
Lambda表达式如何进行调试
一、概述 Java8提供了lambda表达式,方便我们对数据集合进行操作,我们使用lambda表达式的时候,是不是有这样的疑问,如何对执行过程中的中间数据进行调试呢? 二、例子 在下面的例子中,我们实现随机最多生成…...
C++ —— 剑斩旧我 破茧成蝶—C++11
江河入海,知识涌动,这是我参与江海计划的第2篇。 目录 1. C11的发展历史 2. 列表初始化 2.1 C98传统的{} 2.2 C11中的{} 2.3 C11中的std::initializer_list 3. 右值引用和移动语义 3.1 左值和右值 3.2 左值引用和右值引用 3.3 引用延长生命周期…...
HTML5好看的音乐播放器多种风格(附源码)
文章目录 1.设计来源1.1 音乐播放器风格1效果1.2 音乐播放器风格2效果1.3 音乐播放器风格3效果1.4 音乐播放器风格4效果1.5 音乐播放器风格5效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板,程序开发,在线开发,在线沟通 作者&…...
C++设计模式行为模式———迭代器模式中介者模式
文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…...
FFmpeg 4.3 音视频-多路H265监控录放C++开发十五,解码相关,将h264文件进行帧分隔变成avpacket
前提 前面我们学习了 将YUV数据读取到AVFrame,然后将AVFrame通过 h264编码器变成 AVPacket后,然后将avpacket直接存储到了本地就变成了h264文件。 这一节课,学习解码的一部分。我们需要将 本地存储的h264文件进行帧分隔,也就是变…...
力扣 LeetCode 104. 二叉树的最大深度(Day7:二叉树)
解题思路: 采用后序遍历 首先要区别好什么是高度,什么是深度 最大深度实际上就是根节点的高度 高度的求法是从下往上传,从下往上传实际上就是左右中(后序遍历) 深度的求法是从上往下去寻找 所以采用从下往上 本…...
如何高效实现汤臣倍健营销云数据集成到SQLServer
新版订单同步-(Life-Space)江油泰熙:汤臣倍健营销云数据集成到SQL Server 在企业信息化建设中,数据的高效集成和管理是提升业务运营效率的关键。本文将分享一个实际案例——如何通过新版订单同步方案,将汤臣倍健营销云…...
Vue3中使用:deep修改element-plus的样式无效怎么办?
前言:当我们用 vue3 :deep() 处理 elementui 中 el-dialog_body和el-dislog__header 的时候样式一直无法生效,遇到这种情况怎么办? 解决办法: 1.直接在 dialog 上面增加class 我试过,也不起作用,最后用这种…...
具身智能之Isaac Gym使用
0. 简介 Isaac Gym 是由 NVIDIA 提供的一个高性能仿真平台,专门用于大规模的机器人学习和强化学习(RL)任务。它结合了物理仿真、GPU加速、深度学习框架互操作性等特点,使得研究人员和开发者可以快速进行复杂的机器人仿真和训练。…...
【大数据学习 | Spark】spark-shell开发
spark的代码分为两种 本地代码在driver端直接解析执行没有后续 集群代码,会在driver端进行解析,然后让多个机器进行集群形式的执行计算 spark-shell --master spark://nn1:7077 --executor-cores 2 --executor-memory 2G sc.textFile("/home/ha…...
《Python制作动态爱心粒子特效》
一、实现思路 粒子效果: – 使用Pygame模拟粒子运动,粒子会以爱心的轨迹分布并运动。爱心公式: 爱心的数学公式: x16sin 3 (t),y13cos(t)−5cos(2t)−2cos(3t)−cos(4t) 参数 t t 的范围决定爱心形状。 动态效果: 粒子…...
国内最快的wordpress主题/南宁seo推广
今天来说一下数组中比较强大的排序方法sort。sort()将在原数组上对数组元素进行排序,即排序时不创建新的数组副本。如果调用方法sort()时没有使用参数,将按字母顺序(更为精确地说,是按照字符编码的顺序)对数组中的元素进行排序。要实现这一点…...
如何制作动态网站/上海最新事件
网上找到延长Visual assist X试用期的方法:Visual Assist X 破解报告 来源:水木社区 http://newsmth.net但是下午找了半天没找到文中说的CLSID,因为安装VAX的时间早就忘了。其实方法也很简单,使用文中提供的Registry Workshop工具࿰…...
为什么要在南极建站/最好的搜索引擎排名
软件的需求分析: 1:获取和引导需求 2:分析和定义需求: 3:验证需求 4:在软件产品的生命周期中管理需求 对于软件的需求分析.也要做不同的划分 1;产品的功能性需求 2:对产品开发过程的需求 3&…...
中国建设银行网站晋阳支行/市场调研的四个步骤
JavaBigdata学习路线 2019-05-28 07:04:33 Auther:MrZhangxd STAGE 第一阶段:JAVA基础 |-第一阶段:JAVA基础 | |-可掌握的核心能力 | | |-熟练掌握常用数据结构和算法,打基础; | | |-全面了解面向对象的编程思想,精通常…...
广州app开发公司排名/怎么制作seo搜索优化
pytest自动化测试框架之入门知识pytest自动化测试框架总结1.入门安装 pytest1.1资源获取1.2运行pytest1.3运行单个案例1.4使用命令行pytest自动化测试框架总结 1.入门 pytest 是一个使构建简单和可伸缩的测试变得容易的框架。测试具有表达性和可读性,不需要样板代…...
北京做网站建设的公司/马鞍山seo
1 本机PySpark环境配置 2 PyCharm配置Python解释器 3 应用入口:SparkContext 4 WordCount代码实战 5 代码结果解析 6 从HDFS读取数据 7 提交代码到集群执行 8 总结 1 Python语言开发Spark程序步骤? 主要是获取SparkContext对象,基于SparkContext对象作为…...