os.path.join()和urllib.parse.urljoin(base, url)

本文介绍了Python中的os.path.join()和urllib.parse.urljoin()两个函数,它们分别用于处理文件路径和URL的拼接。os.path.join()会根据路径组件创建完整路径,而urllib.parse.urljoin()则根据基本URL和相对URL生成绝对URL。在处理URL时,urljoin()会考虑URL的绝对性,影响最终结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

os.path.join()

os.path.join()用来使用/连接多个路径。如果最后一个路径组成部分是空的,那么将把/放到最后。如果一个路径的组成部分是绝对路径,那么之前连接的路径都会被抛弃。

# 解释os.path.join() 

# 导入模块
import os

# path
path = "/home"

# 连接各个路径
print(os.path.join(path, "User/Desktop", "file.txt"))

# Path
path = "User/Documents"

# 连接各个路径
print(os.path.join(path, "/home", "file.txt"))

# 在上边的例子,'/home'
# 代表绝对路径
# 所以在它之前组成的路径会被抛弃
# 重新在/home开始



# Path
path = "/User"

# 连接多个路径
print(os.path.join(path, "Downloads", "file.txt", "/home"))

# 在上边的例子,'/User' and '/home'
# 都是绝对路径
# 但是 '/home' 是最后一个
# 所以home之前的路径
# 将会抛弃,重新在/home开始连接

# Path
path = "/home"

# 连接多个路径
print(os.path.join(path, "User/Public/", "Documents", ""))

# 上边的例子,列表最后一个元素
# 是空的
# 所以会使用"/"作为参数连接在路径后边

/home\User/Desktop\file.txt
/home\file.txt
/home
/home\User/Public/Documents\

urllib.parse.urljoin(base, url)

官方解释:
Join a base URL and a possibly relative URL to form an absolute
interpretation of the latter.
连接一个基本的URL和一个可能相对的URL,以形成对后者的一个绝对解释。

如果url是一个绝对URL(//,http://,https://),之前的会被抛弃

urljoin(‘https://www.google.com’, ‘//www.microsoft.com’)
‘https://www.microsoft.com’

否则二者会连接。

urlparse(‘http://a/b/c/d/e’)
ParseResult(scheme=‘http’, netloc=‘a’, path=’/b/c/d/e’, params=’’, query=’’, fragment=’’)

urljoin(‘http://a/b/c/d/e’, ‘f’)
‘http://a/b/c/d/f’
urlparse(‘http://a/b/c/d/e/’)
ParseResult(scheme=‘http’, netloc=‘a’, path=’/b/c/d/e/’, params=’’, query=’’, fragment=’’)

urljoin(‘http://a/b/c/d/e/’, ‘f’)
‘http://a/b/c/d/e/f’

如果url又/

urljoin(‘http://a/b/c/d/e’, ‘/f’)
‘http://a/f’

在下面代码中直接修改,以实现每10分钟保存以下下载的文件import os import csv import sys import time import json import logging import requests import subprocess from time import sleep from pathlib import Path from pprint import pprint from urllib.parse import urljoin from datetime import datetime, timedelta from logging.handlers import RotatingFileHandler 配置文件路径 BASE_DIR = os.path.dirname(os.path.abspath(file)) CREDENTIALS_PATH = os.path.join(BASE_DIR, ‘credentials.json’) REFRESH_TOKEN_PATH = os.path.join(BASE_DIR, ‘refreshTokenObjectFile.json’) CHECKPOINT_PATH = os.path.join(BASE_DIR, ‘checkpoint.json’) LAST_SYNC_PATH = os.path.join(BASE_DIR, ‘last_sync.txt’) API配置 API_HOST = ‘us2.api.concursolutions.com’ TOKEN_ENDPOINT = ‘/oauth2/v0/token’ PROFILE_ENDPOINT = ‘/profile/identity/v4/Users/’ 配置日志系统 def setup_logger(): log_dir = Path(“logs”) log_dir.mkdir(exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") log_file = log_dir / f"concur_sync_Identity_v41_{timestamp}.log" logger = logging.getLogger("ConcurAPI") logger.setLevel(logging.DEBUG) # 修正1:指定文件编码为UTF-8 file_handler = RotatingFileHandler( filename=log_file, encoding='utf-8', # 关键修正 maxBytes=10*1024*1024, backupCount=5 ) file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) # 修正2:配置控制台编码(Windows专用) if sys.platform == 'win32': from colorama import just_fix_windows_console just_fix_windows_console() console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logger.addHandler(file_handler) logger.addHandler(console_handler) return logger 初始化日志记录器 logger = setup_logger() class ConcurAuth: def init(self): self.credentials = { ‘appID’: ‘’, ‘clientSecret’: ‘’, ‘grantType’: ‘password’, ‘companyUUID’: ‘’, ‘companyRequestToken’: ‘’, ‘credentialType’: ‘authtoken’ } self.refresh_token = {} self.access_token = ‘’ self.access_expires_at = 0 # 新增:记录access_token过期时间戳 def run(self):
最新发布
03-17
优化代码:#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import re import sys import time import requests from tqdm import tqdm from bs4 import BeautifulSoup from urllib.parse import urljoin def download_vr_images( target_url, save_dir='/Users/sunhui/Downloads/tu', max_retry=3, timeout=30, concurrency=5 ): """ 全景图片下载核心函数 参数: - target_url: 目标网页URL - save_dir: 存储路径(默认:用户下载目录) - max_retry: 最大重试次数 - timeout: 请求超时时间(秒) - concurrency: 并发下载数 """ # ==================== 路径验证 ==================== if not os.path.isabs(save_dir): raise ValueError(f"路径必须为绝对路径:{save_dir}") illegal_chars = re.findall(r'[<>:"|?*]', save_dir) if illegal_chars: raise ValueError(f"路径包含非法字符:{''.join(set(illegal_chars))}") try: os.makedirs(save_dir, exist_ok=True) test_file = os.path.join(save_dir, '__perm_test.tmp') with open(test_file, 'w') as f: f.write('permission_test') os.remove(test_file) except Exception as e: raise RuntimeError(f"路径初始化失败: {str(e)}") # ==================== 网络请求 ==================== headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Referer': target_url } try: response = requests.get(target_url, headers=headers, timeout=timeout) response.raise_for_status() except requests.exceptions.RequestException as e: raise ConnectionError(f"网页请求失败: {str(e)}") # ==================== 内容解析 ==================== soup = BeautifulSoup(response.text, 'html.parser') # 两种图片链接提取策略 img_links = [] # 策略1:通过CSS类名匹配 vr_images = soup.select('div.vr-container img.full-res') # 策略2:通过正则表达式匹配 pattern = re.compile(r'(https?://\S+?\.(jpg|png|webp))', re.IGNORECASE) fallback_links = pattern.findall(response.text) if vr_images: for img in vr_images: img_url = img.get('data-src') or img.get('src') if img_url: img_links.append(urljoin(target_url, img_url)) elif fallback_links: img_links = [urljoin(target_url, link[0]) for link in fallback_links] else: raise ValueError("未检测到有效图片链接") # ==================== 下载逻辑 ==================== success_count = 0 for idx, img_url in enumerate(tqdm(img_links, desc="下载进度", unit="file"), 1): file_name = f"vr_image_{time.strftime('%Y%m%d%H%M%S')}_{idx}.{img_url.split('.')[-1]}" save_path = os.path.join(save_dir, file_name) for retry in range(max_retry + 1): try: with requests.get(img_url, stream=True, headers=headers, timeout=timeout) as r: r.raise_for_status() total_size = int(r.headers.get('content-length', 0)) with open(save_path, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): if chunk: f.write(chunk) success_count += 1 break except Exception as e: if retry == max_retry: print(f"\n⚠️ 文件下载失败:{img_url} | 错误:{str(e)}") else: time.sleep(2 ** retry) # ==================== 结果报告 ==================== print(f"\n✅ 下载完成 | 成功:{success_count}/{len(img_links)}") print(f"📁 存储路径:{save_dir}") if __name__ == "__main__": # 使用示例 try: download_vr_images( target_url="https://vr.justeasy.cn/view/1709b5704a0u64f4-1711545753.html", save_dir="/Users/sunhui/Downloads/tu" ) except Exception as e: print(f"❌ 运行错误:{str(e)}") sys.exit(1)
03-14
<think>嗯,用户给了一段Python代码,用来处理XML文件里的标签名称替换。他们想了解这段代码的作用,需要我详细分析一下。首先,我得仔细看看代码的结构各个部分的功能。 首先,代码导入了几个必要的模块:os.path、globxml.etree.ElementTree。还有一行注释掉的xml.dom.minidom,可能之前尝试过用不同的解析方法,但后来换成了ElementTree。这说明用户可能对XML处理有一定的了解,或者尝试过不同的方法。 接下来,代码设定了路径path,指向一个存放XML文件的目录。然后使用glob.glob遍历该目录下所有的.xml文件。这里用的是glob模块,所以应该是处理所有匹配到的文件。对于每个xml_file,代码用ET.parse解析生成ElementTree对象,获取根节点root。 然后,代码在根节点下查找所有名为'object'的元素,遍历这些元素。对于每个member(即object节点),它查找子节点'name'的文本内容。如果这个内容不等于'hello',就打印出来,并将其文本改为'meter',最后用tree.write写回原文件。 不过,这里有几个问题需要注意。首先,在循环内部,每次修改后都会立即写入文件。这样会导致每个修改都触发一次写操作,如果文件很多或者文件较大,可能会影响性能。另外,条件判断是objectname != 'hello',也就是所有name不是hello的object都会被改为meter。这可能是一个错误,用户的实际需求可能是将特定的标签(比如原来的'zero')改为'num'或者其他名称,但代码里却固定改成了'meter',并且条件是排除'hello'。这可能与用户问题中的描述不符,用户问题里提到要将zero改为num,但代码里却是改成了meter,并且排除了hello。这说明代码可能存在逻辑错误,或者用户提供的代码问题描述不一致。 另外,代码中注释掉的部分,比如os.path.join(path, xml_file),可能在之前的版本中用于构建路径,但现在直接用xml_file,因为glob返回的已经是完整路径。这点需要注意,确保路径处理正确。 还有,写入文件时,原代码使用了tree.write(xml_file),但可能没有指定编码方式,有些情况下会导致编码问题。例如,原XML文件如果是UTF-8编码,写入时也应明确指定encoding='utf-8',否则可能默认使用其他编码,导致文件损坏。不过,ElementTree的write方法默认可能会保留原文件的编码,但最好显式指定。 此外,用户的问题描述中提到要批量修改XML里的标签名称,比如把zero改为num,但代码中却将name改为'meter',并且排除了'hello'。这可能意味着用户提供的代码问题描述之间存在差异,或者用户可能在测试不同的替换规则。需要指出这一点,说明代码实际执行的是将非hello的object name改为meter,而不是zero到num。 总结来说,这段代码的作用是遍历指定目录下的所有XML文件,查找每个文件中的object节点,如果该节点的name不是hello,就将其改为meter,并保存修改。但可能存在性能问题、条件逻辑与需求不符的问题,以及可能的编码问题需要调整。</think>这段代码的作用是批量修改指定目录下所有XML文件中特定标签的名称。下面分步骤解析其功能及潜在问题: $\textbf{代码功能解析}$ 1. $\textbf{路径设置}$ ```path = r'D:/.../Annotations/'``` 设定存放XML文件的目录路径 2. $\textbf{遍历文件}$ ```glob.glob(path + '/*.xml')``` 使用通配符获取该目录下所有.xml文件路径 3. $\textbf{XML解析}$ ```tree = ET.parse(xml_file)``` 通过ElementTree解析每个XML文件生成DOM树 4. $\textbf{定位目标节点}$ ```root.findall('object')``` 在根节点下查找所有名为`object`的节点 5. $\textbf{标签修改逻辑}$ - ```member.find('name').text``` 获取当前标签名 - ```if objectname != 'hello'``` 判断条件:仅修改标签名非"hello"的节点 - ```member.find('name').text = 'meter'``` 将符合条件的标签名改为"meter" $\textbf{潜在问题}$ 1. $\textbf{重复写入}$ ```tree.write(xml_file)``` 每次修改立即写入文件,建议将写入操作移到循环外以提高性能 2. $\textbf{逻辑矛盾}$ - 问题描述要求修改`zero→num` - 代码实际执行`非hello→meter` - 需要检查条件判断逻辑是否符合作业需求 3. $\textbf{编码隐患}$ ```tree.write(xml_file)``` 未指定编码参数,建议改为: ```tree.write(xml_file, encoding='utf-8')``` $\textbf{改进建议}$ 1. 使用with语句确保文件安全关闭 2. 添加XML声明保留`<?xml version...?>`头信息 3. 提取硬编码的字符串(如'meter')为变量 4. 添加异常处理机制 $\textbf{修改后核心逻辑示意}$ ```python new_name = 'num' # 根据需求修改的目标名称 target_name = 'zero' # 需要被替换的原名称 for member in root.findall('object'): name_node = member.find('name') if name_node.text == target_name: name_node.text = new_name # 循环结束后统一写入 tree.write(xml_file, encoding='utf-8') ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值