首页 > 编程问答 >上传多个图像时 React 前端和 Flask 后端出现 CORS 策略错误

上传多个图像时 React 前端和 Flask 后端出现 CORS 策略错误

时间:2024-07-30 12:57:34浏览次数:9  
标签:python reactjs flask cors ml

实际上,我已经在reactJs中制作了前端,在python Flask中制作了后端(使用cnn模型进行一些预测)。当我按照我的请求发送5到6张图像时,它工作正常,但是当我发送10到15张图像和一些时间时令人筋疲力尽,然后它给出了类似的错误,尽管我在下面给出的代码中设置了Cors:

192.168.151.24/:1 Access to XMLHttpRequest at 'http://192.168.151.24:2222/upload-images' from origin 'http://192.168.151.24:1111' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.Understand this error
App.js:177 Error uploading files: po {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}code: "ERR_NETWORK"config: {transitional: {…}, adapter: Array(3), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}message: "Network Error"name: "AxiosError"request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}stack: "AxiosError: Network Error\n    at u.onerror (http://192.168.151.24:1111/static/js/main.e4ed8eb8.js:2:404852)\n    at La.request (http://192.168.151.24:1111/static/js/main.e4ed8eb8.js:2:412324)\n    at async http://192.168.151.24:1111/static/js/main.e4ed8eb8.js:2:441003"[[Prototype]]: Error
overrideMethod @ console.js:288
(anonymous) @ App.js:177
Show 1 more frame
Show lessUnderstand this error
:2222/upload-images:1 
        
Failed to load resource: net::ERR_FAILED

我的反应代码:

import React, { useEffect, useRef, useState } from 'react';
import { PrimeReactProvider } from 'primereact/api';
import { Toast } from 'primereact/toast';
import { FileUpload } from 'primereact/fileupload';
import { ProgressBar } from 'primereact/progressbar';
import { Button } from 'primereact/button';
import { Tooltip } from 'primereact/tooltip';
import { Tag } from 'primereact/tag';
import { InputSwitch } from 'primereact/inputswitch';
import './App.scss';
import axios from 'axios';
import Box from '@mui/material/Box';
import LinearProgress from '@mui/material/LinearProgress';
import { getCurrentTimestamp, mapRequest } from './UtilityFunctions/pathFunctions';

function App() {
  const toast = useRef(null);
  const [totalSize, setTotalSize] = useState(0);
  const fileUploadRef = useRef(null);
  const [folderPath, setFolderPath] = useState('');
  const [uploadMethod, setUploadMethod] = useState(false);
  const [loading,setLoading] =useState(false);
  // true for "Upload Method", false for "Choose Folder Method"
  const MAX_FILE_SIZE = 50 *1024 * 1024;
  const URL = 'http://192.168.151.24:2222'
  // const URL = 'http://localhost:5000'
  const FIXED_PATH = 'D:\\V2_SourceCode\\attributesdetection\\similar-image-clustering-main\\random';
  useEffect(()=>{
    setUploadMethod(uploadMethod)
  },[uploadMethod])
  const onTemplateSelect = (e) => {
    let _totalSize = totalSize;
    let files = e.files;
    let exceeded = false;

    // Calculate total size of selected files
    Object.keys(files).forEach((key) => {
      _totalSize += files[key].size || 0;
      if (_totalSize > MAX_FILE_SIZE) {
        exceeded = true;
      }
    });

    if (exceeded) {
      toast.current.show({ severity: 'error', summary: 'Error', detail: 'Total file size exceeds 50 MB limit for upload feature, use choose folder method for bulk upload !!' });
      fileUploadRef.current.clear(); // Clear selected files if size exceeds limit
    } else {
      setTotalSize(_totalSize);
    }
  };

  const onTemplateUpload = (e) => {
    let _totalSize = 0;

    e.files.forEach((file) => {
      _totalSize += file.size || 0;
    });

    setTotalSize(_totalSize);
    toast.current.show({ severity: 'info', summary: 'Success', detail: 'File Uploaded' });
  };

  const onTemplateRemove = (file, callback) => {
    setTotalSize(totalSize - file.size);
    callback();
  };

  const onTemplateClear = () => {
    setTotalSize(0);
  };

  const headerTemplate = (options) => {
    const { className, chooseButton, uploadButton, cancelButton } = options;
    const value = totalSize / 500000;
    const formattedValue = fileUploadRef && fileUploadRef.current ? fileUploadRef.current.formatSize(totalSize) : '0 B';

    return (
      <div className={className} style={{ backgroundColor: 'transparent', display: 'flex', alignItems: 'center' }}>
        {/* {chooseButton} */}
        <label style={{ marginLeft: '10px' }}>{'Already fixed Folder Method'}</label>
        <InputSwitch
          checked={uploadMethod}
          onChange={(e) => setUploadMethod(e.value)}
          style={{ marginLeft: '10px' }}
        />
        <label style={{ marginLeft: '10px' }}>{'Upload Method'}</label>

        {uploadMethod ? (
          chooseButton
        ) : null}
        {uploadMethod && cancelButton}
        {uploadMethod && <div className="flex align-items-center gap-3 ml-auto">
          <span>{formattedValue} / 50 MB</span>
          <ProgressBar value={value} showValue={false} style={{ width: '10rem', height: '12px' }}></ProgressBar>
        </div>}
      </div>
    );
  };

  const itemTemplate = (file, props) => {
    return (
      uploadMethod && <div className="flex align-items-center flex-wrap pb-5">
        <div className="flex align-items-center" style={{ width: '40%' }}>
          <img alt={file.name} role="presentation" src={file.objectURL} width={100} />
          <span className="flex flex-column text-left ml-3">
            {file.name}
            <small>{new Date().toLocaleDateString()}</small>
          </span>
        </div>
        <Tag value={props.formatSize} severity="warning" className="px-3 py-2" />
        <Button type="button" icon="pi pi-times" className="p-button-outlined p-button-rounded p-button-danger ml-auto" onClick={() => onTemplateRemove(file, props.onRemove)} />
      </div>
    );
  };

  const emptyTemplate = () => {
    console.log(uploadMethod)
    return uploadMethod ? (
      <div className="flex align-items-center flex-column">
        <i className="pi pi-image mt-3 p-5" style={{ fontSize: '5em', borderRadius: '50%', backgroundColor: 'var(--surface-b)', color: 'var(--surface-d)' }}></i>
        <span style={{ fontSize: '1.2em', color: 'var(--text-color-secondary)' }} className="my-5">
          Drag and Drop Image Here
        </span>
      </div>
    ) : (
      <div style={{'display' : 'flex','flexDirection':'column','flexWrap':'nowrap','alignItems':'center','justifyContent':'space-around','gap' : '5rem'}}>
      <div>
        Fixed Path is :  {FIXED_PATH}    
      </div>
            <input
            type="button"
            className='btn btn-success ms-5'
            defaultValue={'Click here to recieve result for fixed folder path'}
            onClick={()=>handlePathSubmit()}
            disabled={loading}
            />
            </div>
    
    ); // If not uploadMethod, return null or alternative content
  };
  

  const handleImagesSubmit = async () => {
    if (fileUploadRef.current) {
      const files = fileUploadRef.current.getFiles();
      if(files.length<1){
        return;
      }
      const formData = new FormData();
      if (!uploadMethod) {
        formData.append('folderPath', folderPath); // Add folder path to form data if using "Choose Folder Method"
      }
      for (let i = 0; i < files.length; i++) {
        formData.append('images', files[i]);
      }
      try {
        setLoading(true);
        // mapRequest('/upload-images')
        const response = await axios.post(`${URL}/upload-images`, formData, {
          headers: {
            'Content-Type': 'multipart/form-data',
       
          },
          maxContentLength: 1000 * 1024 * 1024, // 1000 MB
          maxBodyLength: 1000 * 1024 * 1024, // 1000 MB
          responseType: 'blob', // Set response type to blob to handle file download
        });
        fileUploadRef.current.clear();
        setTotalSize(0); 
        // Create a URL for the downloaded file
        const url = window.URL.createObjectURL(new Blob([response.data]));
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', `predictions_${getCurrentTimestamp()}.xlsx`);
        document.body.appendChild(link);
        link.click();
      } catch (error) {
        console.error('Error uploading files:', error);
      } finally{
        setLoading(false)
      }
    }
  };
  const handlePathSubmit = async ()=>{
    try {
      setLoading(true);
      const formData = new FormData();
      formData.append('path',FIXED_PATH);
      const response = await axios.post(`${URL}/uploadpath`,formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
   
        },
        responseType: 'blob',
      });

      // Create a URL for the downloaded file
      const url = window.URL.createObjectURL(new Blob([response.data]));
      const link = document.createElement('a');
      link.href = url;
      link.setAttribute('download', `predictions_${getCurrentTimestamp()}.xlsx`);
      document.body.appendChild(link);
      link.click();
    } catch (error) {
      console.error('Error uploading files:', error);
    } finally{
      setLoading(false)
    }
  }

  const chooseOptions = { icon: 'pi pi-fw pi-images', iconOnly: true, className: 'custom-choose-btn p-button-rounded p-button-outlined' };
  const uploadOptions = { icon: 'pi pi-fw pi-cloud-upload', iconOnly: true, className: 'custom-upload-btn p-button-success p-button-rounded p-button-outlined' };
  const cancelOptions = { icon: 'pi pi-fw pi-times', iconOnly: true, className: 'custom-cancel-btn p-button-danger p-button-rounded p-button-outlined' };

  return (
    <PrimeReactProvider>
      <div className="App">
        {loading && 
          <div className='loadingScreen'>
            <div className='loader'>
              <Box sx={{ width: '100%' }}>
                <LinearProgress color="success" />
                <LinearProgress color="success" />
              </Box>
            </div>
            <div className='blockArea'>
              <span>
                Predicting the Values...
              </span>
            </div>
          </div>}
        <Toast ref={toast}></Toast>

        <Tooltip target=".custom-choose-btn" content="Choose" position="bottom" />
        <Tooltip target=".custom-upload-btn" content="Upload" position="bottom" />
        <Tooltip target=".custom-cancel-btn" content="Clear" position="bottom" />

        <FileUpload 
          ref={fileUploadRef} 
          name="demo[]" 
          url="/api/upload" 
          multiple 
          accept="image/*" 
          maxFileSize={10000000}
          onUpload={onTemplateUpload} 
          onSelect={onTemplateSelect} 
          one rror={onTemplateClear} 
          onClear={onTemplateClear}
          headerTemplate={headerTemplate} 
          itemTemplate={itemTemplate} 
          emptyTemplate={emptyTemplate}
          chooseOptions={chooseOptions} 
          uploadOptions={uploadOptions} 
          cancelOptions={cancelOptions} 
        />

        {uploadMethod && <div className='footer'>
          <input className='btn btn-success' type='button' defaultValue={'Submit'} onClick={() => handleImagesSubmit()} disabled={loading} />
        </div>}
      </div>
    </PrimeReactProvider>
  );
}

export default App;

我的烧瓶代码:

import os
import io
import json
import numpy as np
import pandas as pd
import tensorflow as tf
from flask import Flask, request, send_file, jsonify
from flask_cors import CORS
from tensorflow.keras.models import load_model
from PIL import Image
from openpyxl import Workbook
from openpyxl.drawing.image import Image as ExcelImage

app = Flask(__name__)
CORS(app)
app.config['MAX_CONTENT_LENGTH'] = 1000 * 1024 * 1024  # 1 GB

# Load the saved models
model_paths = {
    'weave': './saved_model/model.h5',
    'shade': './saved_model/model_shade.h5',
    'majcat': './saved_model/model_majcat.h5',
    'lycra': './saved_model/model_lycra.h5',
    'fabweight': './saved_model/model_fabweight.h5',
    'collar': './saved_model/model_collar.h5',
    'yarn': './saved_model/model_yarn.h5',
    'fab': './saved_model/model_fab.h5',
    'fab2': './saved_model/model_fab2.h5',
    'weave1': './saved_model/model_weave1.h5',
    'weave3': './saved_model/model_weave3.h5',
    'placketchanging': './saved_model/model_placketchanging.h5',
    'bltmainstyle': './saved_model/model_bltmainstyle.h5',
    'substylebelt': './saved_model/model_substylebelt.h5',
    'sleevesmainstyle': './saved_model/model_sleevesmainstyle.h5',
    'btfold': './saved_model/model_btfold.h5',
    'set': './saved_model/model_set.h5',
    'neckband': './saved_model/model_neckband.h5',
    'pocket': './saved_model/model_pocket.h5',
    'fit': './saved_model/model_fit.h5',
    'pattern': './saved_model/model_pattern.h5',
    'length': './saved_model/model_length.h5',
    'mainstyle': './saved_model/model_mainstyle.h5',
    'dcsubstyle': './saved_model/model_dcsubstyle.h5',
    'dcedgeloop': './saved_model/model_dcedgeloop.h5',
    'btnmainmvgr': './saved_model/model_btnmainmvgr.h5',
    'substylebtnclr': './saved_model/model_substylebtnclr.h5',
    'zip': './saved_model/model_zip.h5',
    'zipcol': './saved_model/model_zipcol.h5',
    'addacc': './saved_model/model_addacc.h5',
    'acccol': './saved_model/model_acccol.h5',
    'printtype': './saved_model/model_printtype.h5',
    'printplacement': './saved_model/model_printplacement.h5',
    'printstyle': './saved_model/model_printstyle.h5',
    'patches': './saved_model/model_patches.h5',
    'patchtype': './saved_model/model_patchtype.h5',
    'embroidery': './saved_model/model_embroidery.h5',
    'embtype': './saved_model/model_embtype.h5',
    'placement': './saved_model/model_placement.h5',
    'addacc1': './saved_model/model_addacc1.h5',
    'addwash': './saved_model/model_addwash.h5',
    'addwashcolor': './saved_model/model_addwashcolor.h5'
}

models = {key: load_model(path) for key, path in model_paths.items()}

# Load the mappings
def load_mappings(directory):
    mappings = {}
    for filename in os.listdir(directory):
        if filename.endswith('.json'):
            with open(os.path.join(directory, filename), 'r') as file:
                mapping_name = filename.split('.')[0]
                mappings[mapping_name] = json.load(file)
    return mappings

mappings = load_mappings('mappings')

# Function to preprocess image
def preprocess_image(image):
    image = image.resize((224, 224))
    image_array = np.array(image)
    image_array = np.expand_dims(image_array, axis=0)
    image_array = tf.keras.applications.resnet50.preprocess_input(image_array)
    return image_array

# Function to get image features using ResNet-50
def get_image_features(image_array):
    resnet_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False, pooling='avg')
    features = resnet_model.predict(image_array)
    return features

# Function to map the predicted indices to labels
def mapToLabels(index, mappings):
    return mappings.get(str(index), "Unknown")

# Function to predict attributes for a given image
def predict_attributes(image):
    image_array = preprocess_image(image)
    image_features = get_image_features(image_array)

    predictions = {}
    for attr, model in models.items():
        prediction = model.predict([image_array, image_features])
        label = mapToLabels(np.argmax(prediction, axis=1)[0], mappings.get(attr, {}))
        predictions[attr] = label

    return predictions

# Function to save images and predictions to Excel
def save_predictions_to_excel(predictions, images):
    columns = ['Filename'] + [f'{attr.capitalize()} Label' for attr in models.keys()]
    df = pd.DataFrame(predictions, columns=columns)

    output = io.BytesIO()
    with pd.ExcelWriter(output, engine='openpyxl') as writer:
        df.to_excel(writer, index=False, sheet_name='Predictions')

        workbook = writer.book
        worksheet = writer.sheets['Predictions']

        # Set column width to fit content including heading length
        for col in worksheet.columns:
            max_length = 0
            column = col[0].column_letter  # Get the column name
            for cell in col:
                try:
                    if cell.row == 1:  # Include header length
                        length = len(str(cell.value))
                    else:
                        length = len(str(cell.value))
                    if length > max_length:
                        max_length = length
                except:
                    pass
            adjusted_width = (max_length + 2)
            worksheet.column_dimensions[column].width = adjusted_width

        for idx, (filename, img) in enumerate(images.items()):
            try:
                img.thumbnail((100, 100))
                image_stream = io.BytesIO()
                img.save(image_stream, format='PNG')
                image_stream.seek(0)
                excel_img = ExcelImage(image_stream)
                cell_location = f'A{idx + 2}'
                worksheet.add_image(excel_img, cell_location)
                worksheet.row_dimensions[idx + 2].height = 100
            except Exception as e:
                print(f"Error processing image {filename}: {e}")

    output.seek(0)
    return output



@app.before_request
def before_request():
    if request.content_length > app.config['MAX_CONTENT_LENGTH']:
        return jsonify({'error': 'File too large'}), 413

@app.route('/upload-images', methods=['POST'])
def upload_images():
    images = request.files.getlist('images')
    predictions = []
    image_data = {}

    for image in images:
        try:
            img = Image.open(image)
            pred_labels = predict_attributes(img)
            predictions.append((image.filename, *pred_labels.values()))
            image_data[image.filename] = img
        except Exception as e:
            print(f"Error processing image {image.filename}: {e}")
            predictions.append((image.filename, *["Unknown"] * len(models)))
            image_data[image.filename] = None

    excel_file = save_predictions_to_excel(predictions, image_data)
    return send_file(excel_file, download_name='predictions.xlsx', as_attachment=True)

@app.route('/uploadpath', methods=['POST'])
def uploadPath():
    folder_path = request.form.get('path')
    if not folder_path:
        return jsonify({'error': 'No folder path provided'}), 400

    if not os.path.isdir(folder_path):
        return jsonify({'error': 'Provided path is not a directory'}), 400

    image_files = [f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]
    predictions = []
    image_data = {}

    for image_file in image_files:
        if image_file.lower().endswith(('png', 'jpg', 'jpeg', 'gif')):
            try:
                with open(os.path.join(folder_path, image_file), 'rb') as img_file:
                    img = Image.open(img_file)
                    pred_labels = predict_attributes(img)
                    predictions.append((image_file, *pred_labels.values()))
                    image_data[image_file] = img
            except Exception as e:
                print(f"Error processing image {image_file}: {e}")
                predictions.append((image_file, *["Unknown"] * len(models)))
                image_data[image_file] = None

    excel_file = save_predictions_to_excel(predictions, image_data)
    output_folder = 'output'
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
    backup_file_path = os.path.join(output_folder, 'predictions.xlsx')
    with open(backup_file_path, 'wb') as f:
        f.write(excel_file.getvalue())
    return send_file(excel_file, download_name='predictions.xlsx', as_attachment=True)

@app.route('/', methods=['GET'])
def home():
    return "This is an API for similar detection"

@app.errorhandler(413)
def request_entity_too_large(error):
    return jsonify({'error': 'File too large'}), 413

if __name__ == '__main__':
    app.config['MAX_CONTENT_LENGTH'] = 1000 * 1024 * 1024  # 1 GB
    CORS(app)
    app.run(debug=True)

CORS 错误(跨源资源共享)发生在的 Web 应用程序(React 前端)尝试从与其来源不同的域(Flask 后端)获取资源时。在的情况下,即使已在 Flask 代码中启用了 CORS,当上传大量图像(例如 10 到 15 张)时,问题仍然存在,这表明问题的根源可能是其他地方。

让我们解决潜在的原因和解决方案:

  1. Flask CORS 配置:

  2. 特定来源: 确保的 Flask CORS 配置允许来自的 React 应用程序的确切来源。而不是使用通配符( * ),请明确指定来源:

    ```python from flask_cors import CORS

    app = Flask( name ) cors = CORS(app, resources={r"/*": {"origins": "http://192.168.151.24:1111"}}) # ... 的代码 ... ```

  3. 凭据: 如果的 React 应用程序发送 cookie 或 HTTP 身份验证,则需要启用凭据:

    python cors = CORS(app, resources={r"/*": {"origins": "http://192.168.151.24:1111", "supports_credentials": True}})

  4. 请求超时:

  5. Flask 超时: 上传大量图像可能会导致请求超时,尤其是在网络连接速度较慢或图像文件较大的情况下。增加 Flask 服务器的超时设置。可以探索使用 Flask 扩展(如 flask-timeout )或通过配置 WSGI 服务器(如 Gunicorn 或 uWSGI)来实现此目的。

  6. Axios 超时: 同样,增加的 Axios 客户端的超时以匹配服务器端调整:

    javascript axios.post(`${URL}/upload-images`, formData, { // ... 的配置 ... timeout: 60000, // 例如,超时设置为 60 秒 });

  7. 图像大小和处理:

  8. 图像压缩: 在将图像发送到服务器之前压缩图像可以显着减少上传时间和带宽消耗。请考虑在的 React 应用程序中使用客户端库来压缩图像。

  9. 后端优化: 分析的 Flask 后端以了解潜在的瓶颈。在处理和保存预测时优化图像处理、模型预测和 Excel 生成步骤。

  10. 分块上传:

  11. 对于大量图像,请实现分块上传,将图像分成更小的块,并单独上传。此方法可以在很大程度上防止超时,并提供更可靠的上传体验。

  12. 浏览器限制:

  13. 浏览器对最大上传文件大小和并发请求数有限制。检查的浏览器设置,并根据需要进行调整。

调试步骤:

  • 网络检查: 使用浏览器的开发人员工具(网络选项卡)检查请求和响应标头。查找任何 CORS 错误或超时指示。

  • 服务器日志: 检查的 Flask 服务器日志以获取有关请求处理和任何潜在错误的详细信息。

  • 逐步测试: 从少量图像开始,然后逐渐增加数量以确定问题是何时开始出现的。这可以帮助查明是否存在性能瓶颈或特定阈值。

通过仔细检查的 CORS 配置、优化请求超时、考虑图像大小和必要时实现分块上传,可以有效地解决上传大量图像时遇到的 CORS 错误,并确保 React 前端和 Flask 后端之间的无缝集成。请记住彻底测试和分析以查明问题的根本原因并实施适当的解决方案。

标签:python,reactjs,flask,cors,ml
From: 78809866

相关文章

  • python - 从文本生成音乐
    请给我一些建议为了解释一下,我输入“深度睡眠的睡眠音乐”,它将返回一个wav文件:https://www.youtube.com/watch?v=1wAdQhFJy54或者我给出一个wav文件,它会返回相同的现在这是我尝试过的:https://github.com/facebookresearch/audiocraft......
  • 从零开始的Python开发日记(7):短信验证功能开发流程
    短信验证功能开发流程在开发一个包含登录、注册以及短信验证的功能时,你需要遵循一个系统的开发流程。以下是实现这一功能的基本步骤,包括所需的技术和代码示例。1.环境配置首先,确保你的开发环境已经配置好,并安装了必要的库和工具。pipinstallfastapiuvicornsqlalche......
  • 【Python数值分析】革命:引领【数学建模】新时代的插值与拟合前沿技术
    目录​编辑第一部分:插值的基本原理及应用1.插值的基本原理1.1插值多项式1.2拉格朗日插值 1.3牛顿插值 1.4样条插值2.插值的Python实现2.1使用NumPy进行插值2.2使用SciPy进行插值2.2.1一维插值​编辑2.2.2二维插值3.插值的应用场景3.1数据平......
  • 了解Web标准,HTML 语法规范,使用 HBuilder X 构建文档骨架,HBuilder X 生成骨架标签新增
    Web标准是由W3C组织和其他标准化组织制定的一系列标准的集合。W3C(万维网联盟)是国际最著名的标准化组织。遵循Web标准可以让不同的开发人员写出的页面更标准、更统一外,还有以下优点:1.让Web的发展前景更广阔。2.内容能被更广泛的设备访问。3.更容易被搜寻引擎搜索。......
  • 在家用电脑上设置 Python 和 Jupyter,尝试打开 Jupyter 笔记本并显示错误,无法获取
    我有最新的Python版本3.12.4和以下版本的Jupyter:SelectedJupytercorepackages...IPython:8.26.0ipykernel:6.29.5ipywidgets:notinstalledjupyter_client:8.6.2jupyter_core:5.7.2jupyter_server:2.14.2jupyterlab......
  • Python - Reloading a module
    Eachmoduleisloadedintomemoryonlyonceduringaninterpretersessionorduringaprogramrun,regardlessofthenumberoftimesitisimportedintoaprogram.Ifmultipleimportsoccur,themodule’scodewillnotbeexecutedagainandagain.Suppose......
  • vscode python 3.7 pylance debugpy 插件 vsix
    可能报错  crashed5timesinthelast3minutes.Theserverwillnotberestarted.  ---pylance 可能报错  cannotreadpropertiesofundefinedreadingresolveEnvironment   --- debugger可能      vscodepython3.7调试没有反应......
  • Python获取秒级时间戳与毫秒级时间戳的方法[通俗易懂]
    参考资料:https://cloud.tencent.com/developer/article/21581481、获取秒级时间戳与毫秒级时间戳、微秒级时间戳代码语言:javascript复制importtimeimportdatetimet=time.time()print(t)#原始时间数据print(int(t))......
  • CEFPython
    在Tkinter界面中直接嵌入Selenium的浏览器视图并不是一件直接的事情,因为Selenium本身并不提供图形界面嵌入的功能。Selenium主要用于自动化web浏览器,但它并不直接控制浏览器窗口的显示方式,而是依赖于WebDriver来与浏览器交互。然而,你可以使用一些替代方案来在Tkinter应用中模拟或......
  • 《最新出炉》系列初窥篇-Python+Playwright自动化测试-58 - 文件下载
    1.简介前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件。因此宏哥就接着讲解和分享一下:自动化测试下载文件。可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就是访问到下载页面,然后定位到要下载的文件的下载按钮后,点击按钮就可以了。其实......