懒加载、self.a和_a

//
//  ViewController.m
//  LazyLoading
//

//查看:http://www.jianshu.com/p/417d3a25aaf4

#import "ViewController.h"

@interface ViewController ()
@property(nonatomic,strong) NSArray * personArray;
@end

@implementation ViewController

// 重写getter方法


//使用懒加载的好处:
//
//(1)不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强
//
//(2)每个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合
//

//第一部分:self.personArray是一个getter
//第二部分:不能写成 !self.personArray 这也是一个getter,getter中有getter会造成死循环
//第三部分:可以使用self.personArray,这是一个setter
//第四部分:不能使用self.personArray,这也是一个getter,getter中再有getter,自己引用自己造成死循环
-(NSArray *)personArray{  // 1
    if (!_personArray) {  // 2
        _personArray = [NSArray arrayWithObjects:@"1", nil];   // 3
    }
    return _personArray;    // 4
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // 懒加载时需要注意的是当第一次使用对象时,需要调用self.因为只有这样才能调用对应的getter⽅法,对象才会被创建
    NSLog(@"%ld", _personArray.count);  // count: 0
    NSLog(@"%ld", self.personArray.count);  // count: 1
}


@end
帮我优化下面的代码:import numpy as np import pandas as pd from PyQt5.QtWidgets import * from PyQt5.QtChart import QChart, QChartView, QLineSeries, QValueAxis from PyQt5.QtCore import Qt, QTimer import os def generate_test_data(filename): time = np.arange(0, 3600, 1) df = pd.DataFrame({ 'timestamp': pd.date_range(start='2023-01-01', periods=3600, freq='S'), 'speed': np.random.normal(500, 150, 3600), 'altitude': np.random.randint(8000, 40000, 3600), 'fuel': np.linspace(5000, 0, 3600) }) df.to_csv(filename, index=False) class CSVHandler: def __init__(self, filename): self.df = pd.read_csv(filename) self.current_index = 0 def get_next_row(self): """模拟实时数据流""" if self.current_index < len(self.df): row = self.df.iloc[self.current_index] self.current_index += 1 return row.to_dict() return None class MainWindow(QMainWindow): def __init__(self): super().__init__() self.init_ui() self.alarm_ranges = {'speed': (200, 900), 'altitude': (5000, 45000), 'fuel': (1000, 5000)} # 初始化定时器 self.timer = QTimer() self.timer.timeout.connect(self.update_data) self.timer.start(1000) # 1秒更新一次 def init_ui(self): self.setWindowTitle("飞行状态监控") self.central_widget = QWidget() self.setCentralWidget(self.central_widget) # 创建图表区域 self.speed_chart = self.create_chart("速度 (km/h)") self.altitude_chart = self.create_chart("高度 (m)") self.fuel_chart = self.create_chart("油量 (L)") # 布局设置 layout = QGridLayout() layout.addWidget(self.speed_chart, 0, 0) layout.addWidget(self.altitude_chart, 0, 1) layout.addWidget(self.fuel_chart, 1, 0) self.central_widget.setLayout(layout) def update_data(self): data = csv_handler.get_next_row() if data: # 更新速度图表 self.update_chart(self.s
03-27
import sys import pymysql from PyQt5.QtWidgets import ( QApplication, QMainWindow, QWidget, QDialog, QTableWidgetItem, QMessageBox, QHeaderView, QPushButton, QLineEdit, QComboBox, QLabel, QVBoxLayout, QHBoxLayout, QFormLayout, QGroupBox, QTableWidget ) from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon, QIntValidator, QRegExpValidator from PyQt5.QtCore import QRegExp # 数据库配置,与原系统保持一致 DB_CONFIG = { 'host': '127.0.0.1', 'user': 'root', 'password': '123456', 'database': 'studentscore', 'charset': 'utf8mb4' } class LoginDialog(QDialog): """登录窗口""" def __init__(self): super().__init__() self.setWindowTitle("专业管理系统登录") self.setFixedSize(350, 200) # 设置登录窗口的背景颜色为淡蓝色 self.setStyleSheet("background-color: #e0f7fa;") # 创建布局 layout = QVBoxLayout() # 表单组 form_group = QGroupBox("数据库登录信息") form_layout = QFormLayout() self.txt_host = QLineEdit() self.txt_host.setText(DB_CONFIG['host']) self.txt_host.setPlaceholderText("数据库主机地址") self.txt_user = QLineEdit() self.txt_user.setText(DB_CONFIG['user']) self.txt_user.setPlaceholderText("数据库用户名") self.txt_pass = QLineEdit() self.txt_pass.setText(DB_CONFIG['password']) self.txt_pass.setPlaceholderText("数据库密码") self.txt_pass.setEchoMode(QLineEdit.Password) self.txt_db = QLineEdit() self.txt_db.setText(DB_CONFIG['database']) self.txt_db.setPlaceholderText("数据库名称") form_layout.addRow("主机:", self.txt_host) form_layout.addRow("用户名:", self.txt_user) form_layout.addRow("密码:", self.txt_pass) form_layout.addRow("数据库:", self.txt_db) form_group.setLayout(form_layout) # 按钮组 btn_layout = QHBoxLayout() self.btn_login = QPushButton("登录") self.btn_login.clicked.connect(self.attempt_login) self.btn_exit = QPushButton("退出") self.btn_exit.clicked.connect(self.reject) btn_layout.addWidget(self.btn_login) btn_layout.addWidget(self.btn_exit) # 添加到主布局 layout.addWidget(form_group) layout.addLayout(btn_layout) self.setLayout(layout) def attempt_login(self): """尝试登录""" config = { 'host': self.txt_host.text().strip(), 'user': self.txt_user.text().strip(), 'password': self.txt_pass.text().strip(), 'database': self.txt_db.text().strip(), 'charset': 'utf8mb4' } if not all(config.values()): QMessageBox.warning(self, "输入不完整", "请填写所有数据库连接信息") return try: # 测试数据库连接 conn = pymysql.connect(**config) # 检查bmajor表是否存在 with conn.cursor() as cursor: cursor.execute("SHOW TABLES LIKE 'bmajor'") if not cursor.fetchone(): QMessageBox.critical(self, "表不存在", "数据库中未找到bmajor表") return conn.close() # 更新全局配置 global DB_CONFIG DB_CONFIG = config self.accept() # 关闭对话框并返回QDialog.Accepted except pymysql.Error as e: QMessageBox.critical(self, "连接失败", f"数据库连接失败:\n{str(e)}") class MajorManager(QMainWindow): """专业管理主窗口""" def __init__(self): super().__init__() self.setWindowTitle("专业信息管理系统") self.setGeometry(100, 100, 1000, 600) # 创建中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 主布局 main_layout = QVBoxLayout(central_widget) # 搜索功能区 search_group = QGroupBox("专业搜索") search_layout = QHBoxLayout() self.lbl_search = QLabel("专业名称:") self.txt_search = QLineEdit() self.txt_search.setPlaceholderText("输入专业名称关键字...") self.btn_search = QPushButton("搜索") self.btn_search.clicked.connect(self.search_major) self.btn_refresh = QPushButton("刷新数据") self.btn_refresh.clicked.connect(self.load_data) search_layout.addWidget(self.lbl_search) search_layout.addWidget(self.txt_search) search_layout.addWidget(self.btn_search) search_layout.addWidget(self.btn_refresh) search_group.setLayout(search_layout) # 数据表格 self.table = self.create_table() # 操作按钮区 btn_group = QGroupBox("专业操作") btn_layout = QHBoxLayout() self.btn_add = QPushButton("添加专业") self.btn_add.clicked.connect(self.add_major) self.btn_edit = QPushButton("编辑专业") self.btn_edit.clicked.connect(self.edit_major) self.btn_delete = QPushButton("删除专业") self.btn_delete.clicked.connect(self.delete_major) btn_layout.addWidget(self.btn_add) btn_layout.addWidget(self.btn_edit) btn_layout.addWidget(self.btn_delete) btn_group.setLayout(btn_layout) # 添加到主布局 main_layout.addWidget(search_group) main_layout.addWidget(self.table, 1) # 表格占据更多空间 main_layout.addWidget(btn_group) # 状态栏 self.statusBar().showMessage("就绪") # 加载数据 self.load_data() def create_table(self): """创建专业表格""" table = QTableWidget() table.setColumnCount(4) table.setHorizontalHeaderLabels(["专业ID", "专业名称", "院系ID", "院系名称"]) table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) table.verticalHeader().setVisible(False) table.setSelectionBehavior(QTableWidget.SelectRows) table.setEditTriggers(QTableWidget.NoEditTriggers) table.setSortingEnabled(True) return table def get_connection(self): """获取数据库连接""" return pymysql.connect(**DB_CONFIG) def load_data(self): """从数据库加载专业数据""" try: conn = self.get_connection() with conn.cursor() as cursor: # 查询bmajor表的所有数据 cursor.execute("SELECT major_id, major_name, depart_id, depart_name FROM bmajor") result = cursor.fetchall() self.table.setRowCount(len(result)) for row_idx, row in enumerate(result): for col_idx, col in enumerate(row): item = QTableWidgetItem(str(col) if col is not None else "") self.table.setItem(row_idx, col_idx, item) conn.close() self.statusBar().showMessage(f"成功加载 {len(result)} 条专业记录") except pymysql.Error as e: QMessageBox.critical(self, "数据库错误", f"加载数据失败:\n{str(e)}") self.statusBar().showMessage("数据加载失败") def add_major(self): """添加新专业""" dialog = MajorDialog(self) if dialog.exec_() == QDialog.Accepted: major_data = dialog.get_data() try: conn = self.get_connection() with conn.cursor() as cursor: sql = """INSERT INTO bmajor (major_id, major_name, depart_id, depart_name) VALUES (%s, %s, %s, %s)""" cursor.execute(sql, major_data) conn.commit() self.load_data() self.statusBar().showMessage(f"成功添加专业: {major_data[1]}") except pymysql.IntegrityError as e: if "Duplicate entry" in str(e): QMessageBox.critical(self, "添加失败", "专业ID或专业名称已存在") else: QMessageBox.critical(self, "添加失败", f"添加专业失败:\n{str(e)}") except pymysql.Error as e: QMessageBox.critical(self, "添加失败", f"添加专业失败:\n{str(e)}") def edit_major(self): """编辑选中专业""" selected = self.table.currentRow() if selected < 0: QMessageBox.warning(self, "未选择", "请先选择要编辑的专业") return major_id = self.table.item(selected, 0).text() dialog = MajorDialog(self, major_id) if dialog.exec_() == QDialog.Accepted: major_data = dialog.get_data() try: conn = self.get_connection() with conn.cursor() as cursor: sql = """UPDATE bmajor SET major_name = %s, depart_id = %s, depart_name = %s WHERE major_id = %s""" # 注意顺序: name, depart_id, depart_name, id cursor.execute(sql, ( major_data[1], major_data[2], major_data[3], major_data[0] )) conn.commit() self.load_data() self.statusBar().showMessage(f"成功更新专业: {major_data[1]}") except pymysql.Error as e: QMessageBox.critical(self, "更新失败", f"更新专业失败:\n{str(e)}") def delete_major(self): """删除选中专业""" selected = self.table.currentRow() if selected < 0: QMessageBox.warning(self, "未选择", "请先选择要删除的专业") return major_id = self.table.item(selected, 0).text() major_name = self.table.item(selected, 1).text() reply = QMessageBox.question(self, "确认删除", f"确定要删除专业 '{major_name}' (ID: {major_id}) 吗?", QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: try: conn = self.get_connection() with conn.cursor() as cursor: cursor.execute("DELETE FROM bmajor WHERE major_id = %s", (major_id,)) conn.commit() self.load_data() self.statusBar().showMessage(f"已删除专业: {major_name}") except pymysql.Error as e: # 检查是否有外键约束 if "foreign key constraint" in str(e).lower(): QMessageBox.critical(self, "删除失败", "该专业可能被其他表引用,无法删除") else: QMessageBox.critical(self, "删除失败", f"删除专业失败:\n{str(e)}") def search_major(self): """搜索专业""" keyword = self.txt_search.text().strip() if not keyword: self.load_data() return try: conn = self.get_connection() with conn.cursor() as cursor: cursor.execute("SELECT major_id, major_name, depart_id, depart_name " "FROM bmajor WHERE major_name LIKE %s", (f"%{keyword}%",)) result = cursor.fetchall() self.table.setRowCount(len(result)) for row_idx, row in enumerate(result): for col_idx, col in enumerate(row): item = QTableWidgetItem(str(col) if col is not None else "") self.table.setItem(row_idx, col_idx, item) conn.close() self.statusBar().showMessage(f"找到 {len(result)} 条匹配专业") except pymysql.Error as e: QMessageBox.critical(self, "搜索失败", f"搜索专业失败:\n{str(e)}") class MajorDialog(QDialog): """专业编辑对话框""" def __init__(self, parent=None, major_id=None): super().__init__(parent) self.setWindowTitle("添加专业" if not major_id else "编辑专业") self.setFixedSize(400, 350) # 主布局 layout = QVBoxLayout() # 表单布局 form_layout = QFormLayout() self.txt_id = QLineEdit() self.txt_id.setPlaceholderText("2位专业代码") # 设置专业ID验证器,必须是2位字母或数字 id_validator = QRegExpValidator(QRegExp(r'^[a-zA-Z0-9]{2}$')) self.txt_id.setValidator(id_validator) self.txt_name = QLineEdit() self.txt_name.setPlaceholderText("输入专业名称") self.txt_depart_id = QLineEdit() self.txt_depart_id.setPlaceholderText("2位院系代码") # 设置院系ID验证器,必须是2位字母或数字 depart_id_validator = QRegExpValidator(QRegExp(r'^[a-zA-Z0-9]{2}$')) self.txt_depart_id.setValidator(depart_id_validator) self.txt_depart_name = QLineEdit() self.txt_depart_name.setPlaceholderText("输入院系名称") form_layout.addRow("专业ID:", self.txt_id) form_layout.addRow("专业名称:", self.txt_name) form_layout.addRow("院系ID:", self.txt_depart_id) form_layout.addRow("院系名称:", self.txt_depart_name) # 按钮布局 btn_layout = QHBoxLayout() self.btn_save = QPushButton("保存") self.btn_save.clicked.connect(self.validate_and_accept) self.btn_cancel = QPushButton("取消") self.btn_cancel.clicked.connect(self.reject) btn_layout.addStretch() btn_layout.addWidget(self.btn_save) btn_layout.addWidget(self.btn_cancel) # 添加到主布局 layout.addLayout(form_layout) layout.addLayout(btn_layout) self.setLayout(layout) # 如果是编辑模式,加载现有数据 self.major_id = major_id if major_id: self.load_major_data() self.txt_id.setEnabled(False) # 禁止编辑专业ID def load_major_data(self): """加载专业数据到表单""" try: conn = pymysql.connect(**DB_CONFIG) with conn.cursor() as cursor: cursor.execute("SELECT * FROM bmajor WHERE major_id = %s", (self.major_id,)) result = cursor.fetchone() if result: self.txt_id.setText(result[0]) self.txt_name.setText(result[1]) self.txt_depart_id.setText(result[2] if result[2] is not None else "") self.txt_depart_name.setText(result[3] if result[3] is not None else "") conn.close() except pymysql.Error as e: QMessageBox.critical(self, "加载失败", f"加载专业数据失败:\n{str(e)}") def get_data(self): """从表单获取数据""" # 处理可能为空的值 def get_value_or_none(text): return text.strip() if text.strip() else None return ( self.txt_id.text().strip(), self.txt_name.text().strip(), get_value_or_none(self.txt_depart_id.text()), get_value_or_none(self.txt_depart_name.text()) ) def validate_and_accept(self): """验证表单数据并接受""" # 验证专业ID格式 major_id = self.txt_id.text().strip() if len(major_id) != 2: QMessageBox.warning(self, "格式错误", "专业ID必须是2位字符") return # 验证必填字段 if not self.txt_name.text().strip(): QMessageBox.warning(self, "输入不完整", "专业名称不能为空") return # 验证院系ID depart_id = self.txt_depart_id.text().strip() if depart_id and len(depart_id) != 2: QMessageBox.warning(self, "格式错误", "院系ID必须是2位字符") return super().accept() if __name__ == "__main__": app = QApplication(sys.argv) # 显示登录窗口 login = LoginDialog() if login.exec_() != QDialog.Accepted: sys.exit() # 显示主窗口 window = MajorManager() window.show() sys.exit(app.exec_())
06-24
我的程序有接收can数据并显示在tab1上的功能,但它的刷新逻辑有一点冗余,每次都全部重新显示,我希望它仅仅在已有的canid中的数据变化或是有新的canid加入时才会更新数据 ,应该怎么做? 这个两个函数是修改的关键所在,一个是刷新界面的函数,一个是解析报文的函数,负责canobditemlist的更新。 def CanOBDdatarefresh(self): # 检查数据接收是否超时(1秒阈值) current_time = time.time() if current_time - self.LSerial.last_data_time > 1.0: # 清空缓冲区并跳过刷新 global frame_buffer frame_buffer = bytearray() return filtered_cycles = self.get_checked_cycles() # 清空表格旧数据(保留表头) for row in range(self.tableWidget.rowCount()): for col in range(self.tableWidget.columnCount()): item = self.tableWidget.item(row, col) if item: item.setText("") row_index = 0 for indexitem in CanOBDItemList: can_id = indexitem[1] # 周期过滤逻辑 if can_id in self.LSerial.cycle_dict: cycle = self.LSerial.cycle_dict[can_id] # 检查是否在过滤周期范围内(±10%容差) skip = False for filtered_cycle in filtered_cycles: tolerance = filtered_cycle * 0.1 # 10%容差 if abs(cycle - filtered_cycle) <= tolerance: skip = True break if skip: continue # 跳过这个帧 # 显示数据 self.tableWidget.setItem(row_index, 0, QtWidgets.QTableWidgetItem(str(indexitem[0]))) self.tableWidget.setItem(row_index, 1, QtWidgets.QTableWidgetItem(str(indexitem[1]))) self.tableWidget.setItem(row_index, 2, QtWidgets.QTableWidgetItem(str(indexitem[2]))) self.tableWidget.setItem(row_index, 3, QtWidgets.QTableWidgetItem(str(indexitem[3]))) self.tableWidget.setItem(row_index, 4, QtWidgets.QTableWidgetItem(str(indexitem[4]))) row_index += 1 self.tableWidget.show() def Frame_analoy_process(self, Framedata): # 检查帧类型 (0x0C 0x98) if len(Framedata) < 8 or Framedata[4] != 0x0C or Framedata[5] != 0x98: return try: FrameNum = int(Framedata[7]) except IndexError: return # 检查是否有足够数据 if len(Framedata) < 12 * FrameNum + 8: return current_time = time.time() # 获取当前精确时间戳 for index in range(FrameNum): # 时间戳 Cantime = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] # 提取ID字节 try: id_bytes = [ Framedata[12 * index + 11], # LSB Framedata[12 * index + 10], Framedata[12 * index + 9], Framedata[12 * index + 8] # MSB ] except IndexError: continue # 格式化为8位大写十六进制 CanID = ''.join(format(b, '02X') for b in id_bytes) CanFramType = "Cycle" Len = 8 # 提取数据部分 try: CanDataSpace = ' '.join( format(Framedata[12 * index + 12 + posindex], '02X') for posindex in range(8) ) except IndexError: continue CanItemData = [Cantime, CanID, CanFramType, Len, CanDataSpace] if CanID in self.last_frame_time: last_time = self.last_frame_time[CanID] cycle = (current_time - last_time) * 1000 # 转换为毫秒 # 平滑处理:使用加权平均减少抖动 if CanID in self.cycle_dict: old_cycle = self.cycle_dict[CanID] self.cycle_dict[CanID] = 0.7 * old_cycle + 0.3 * cycle else: self.cycle_dict[CanID] = cycle else: self.cycle_dict[CanID] = 0 # 首次出现,周期设为0 self.last_frame_time[CanID] = current_time # 更新列表 if not CanOBDItemList or CanOBDItemList[0][0] == 0: if CanOBDItemList and CanOBDItemList[0][0] == 0: CanOBDItemList.pop(0) CanOBDItemList.insert(0, CanItemData) else: Listpos = self.find_in_2d_list(CanOBDItemList, CanID) if Listpos is not None: CanOBDItemList[Listpos[0]] = CanItemData else: CanOBDItemList.append(CanItemData) self.last_data_time = time.time() # 解析到有效帧时更新时间
最新发布
07-12
把以下代码改写成最新写法class ImageDataset(Dataset): def __init__(self, root, transforms_=None, unaligned=False, mode="train"):# 初始化数据集参数:根目录路径、预处理变换组合、是否非对齐模式、数据集模式(训练/验证等) self.transform = transforms.Compose(transforms_)# 将多个数据增强操作组合成序列 self.unaligned = unaligned # 设置是否使用非对齐图像对(用于CycleGAN等模型) self.files_A = sorted(glob.glob(os.path.join(root, "%s/A" % mode) + "/*.*")) # 使用glob模块递归查找指定路径下所有文件,按文件名排序后获取A类图像文件列表 self.files_B = sorted(glob.glob(os.path.join(root, "%s/B" % mode) + "/*.*")) # 同理获取B类图像文件列表,路径中的%s会被mode参数替换(如train/val) def __getitem__(self, index):# 这是定义如何获取数据集中单个样本的方法。index参数用于指定获取第几个样本 image_A = Image.open(self.files_A[index % len(self.files_A)])#使用循环索引加载A类图像,确保当index超过文件数量时,能够循环到文件列表的开头。 if self.unaligned: #检查是否启用未对齐模式。如果启用,则从B类文件中随机选择一个图像。 image_B = Image.open(self.files_B[random.randint(0, len(self.files_B) - 1)])#在未对齐模式下,随机选择一个B类图像,打破图像对的对应关系。 else: image_B = Image.open(self.files_B[index % len(self.files_B)])#在对齐模式下,按相同索引加载B类图像,保持图像对的对应关系。 # Convert grayscale images to rgb if image_A.mode != "RGB": image_A = to_rgb(image_A)#检查A类图像是否为RGB格式,如果不是,则转换为RGB格式 if image_B.mode != "RGB": image_B = to_rgb(image_B)#检查B类图像是否为RGB格式,如果不是,则转换为RGB格式 item_A = self.transform(image_A)#对A类图像应用预处理变换,如随机裁剪、颜色抖动等。 item_B = self.transform(image_B)#对B类图像应用相同的预处理变换 return {"A": item_A, "B": item_B}#返回包含两个处理后的图像的字典,供DataLoader加载。 def __len__(self): return max(len(self.files_A), len(self.files_B))# 返回数据集长度(取A/B类文件中较大的数量,确保完整遍历所有图像)
03-26
``` import os import requests from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse from tqdm import tqdm class ImageCrawler: def __init__(self, base_url, save_dir='images', max_depth=2): self.base_url = base_url self.save_dir = save_dir self.max_depth = max_depth self.visited = set() self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } # 创建保存目录 os.makedirs(self.save_dir, exist_ok=True) def is_valid_url(self, url): """验证URL是否合法""" parsed = urlparse(url) return bool(parsed.netloc) and bool(parsed.scheme) def get_filename(self, url): """从URL提取有效文件名""" path = urlparse(url).path return os.path.basename(path).split('?')[0] or 'default.jpg' def download_image(self, url): """下载单个图片""" try: response = requests.get(url, headers=self.headers, stream=True, timeout=10) if response.status_code == 200: filename = self.get_filename(url) filepath = os.path.join(self.save_dir, filename) # 避免重复下载 if not os.path.exists(filepath): with open(filepath, 'wb') as f: for chunk in response.iter_content(chunk_size=1024): if chunk: f.write(chunk) return True except Exception as e: print(f"下载失败 {url}: {str(e)}") return False def extract_images(self, url): """提取页面中的所有图片""" try: response = requests.get(url, headers=self.headers, timeout=10) soup = BeautifulSoup(response.text, 'html.parser') img_tags = soup.find_all('img') for img in img_tags: img_url = img.attrs.get('src') or img.attrs.get('data-src') if not img_url: continue # 处理相对URL img_url = urljoin(url, img_url) if self.is_valid_url(img_url): yield img_url except Exception as e: print(f"页面解析失败 {url}: {str(e)}") def crawl(self, url=None, depth=0): """递归爬取页面""" if depth > self.max_depth: return current_url = url or self.base_url if current_url in self.visited: return self.visited.add(current_url) print(f"正在爬取: {current_url}") # 下载当前页面的图片 for img_url in self.extract_images(current_url): if self.download_image(img_url): print(f"成功下载: {img_url}") # 递归爬取子链接 try: response = requests.get(current_url, headers=self.headers, timeout=10) soup = BeautifulSoup(response.text, 'html.parser') for link in soup.find_all('a'): href = link.get('href') if href and href not in self.visited: absolute_url = urljoin(current_url, href) if self.is_valid_url(absolute_url): self.crawl(absolute_url, depth+1) except Exception as e: print(f"链接爬取失败: {str(e)}") if __name__ == "__main__": # 使用示例 crawler = ImageCrawler( base_url="https://example.com", # 替换为目标网站 save_dir="downloaded_images", max_depth=2 ) crawler.crawl()```请解释这个代码
03-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值