欢迎光临UUpython
最大、最新、最全的Python代码收集站

苏宁易购网站数据爬取

uupython阅读(531)

这段代码使用了 Selenium 库来进行自动化的网页数据抓取。它通过打开 Chrome 浏览器,访问苏宁易购的 iPhone 14 搜索页面,然后模拟滚动窗口、提取数据、保存数据、点击下一页等操作来抓取商品信息。

以下是代码的主要部分解释:

  1. 导入所需的库和模块,包括 webdrivertimeBycsv
  2. 打开一个 CSV 文件,用于存储抓取的数据。在打开文件时,使用了 'a' 模式表示追加写入,以免覆盖之前的数据。
  3. 创建一个 CSV writer 对象,用于写入数据行。
  4. 创建一个 Chrome 浏览器的 WebDriver 对象。
  5. 使用 WebDriver 打开指定的网址。
  6. 使用 execute_script 方法滚动窗口到底部,以加载更多商品信息。
  7. 使用 find_elements 方法根据 CSS 选择器提取商品信息的父级元素。
  8. 对于每个商品父级元素,使用 find_element 方法结合 CSS 选择器提取商品的价格、标题、链接、评价和店铺信息。
  9. 打印提取的商品信息,以便在控制台查看。
  10. 使用 CSV writer 对象将提取的商品信息写入 CSV 文件。
  11. 使用 execute_script 方法点击下一页按钮,加载并抓取下一页的数据。
  12. 最后,使用 input() 函数防止程序退出,以便手动终止程序。

需要注意的是,Selenium 在模拟浏览器操作时较为耗费资源,因此不适合进行大规模的数据抓取。如果需要更稳定和高效的数据抓取,可以考虑使用网络请求库(如 requests)结合页面解析库(如 BeautifulSoup)来进行抓取。此外,网站的页面结构可能会变化,导致选择器无法正常提取数据,需要根据实际情况进行调整。

from selenium import webdriver
import time
from selenium.webdriver.common.by import By
import csv
 
f = open('su//'+'suning5.csv', mode='a', encoding='utf-8', newline='')
writer = csv.writer(f)
writer.writerow(['价格', '标题', '链接', '评价', '店铺'])
# 打开chrome浏览器
driver = webdriver.Chrome()
# 打开网址
driver.get('https://search.suning.com/iphone14/')
driver.implicitly_wait(3)
# 滚动窗口
for page in range(2):
    driver.execute_script('document.querySelector("body > div.ng-footer > div.ng-s-footer").scrollIntoView()')
    time.sleep(2)
    # 提取数据
    divs = driver.find_elements(By.CSS_SELECTOR, '.product-box')
    for div in divs:
        price = div.find_element(By.CSS_SELECTOR, '.price-box').text
        title = div.find_element(By.CSS_SELECTOR, '.title-selling-point').text
        href = div.find_element(By.CSS_SELECTOR, '.title-selling-point a').get_attribute('href')
        evaluate = div.find_element(By.CSS_SELECTOR, '.info-evaluate').text
        store = div.find_element(By.CSS_SELECTOR, '.store-stock').text
 
        print(price, title, href, evaluate, store)
        # 保存数据
        writer.writerow([price, title, href, evaluate, store])
        # 点击下一页
    driver.execute_script('document.querySelector("#nextPage").click()')
input()

天翼云盘签到脚本,多用户

uupython阅读(1214)

这段代码是一个用于进行中国电信189云盘签到的脚本,它会模拟登录、签到和抽奖的操作。以下是代码的主要部分解释:

  1. 引入所需的库和模块,包括 timerejsonbase64hashliburllib.parsehmacrsarequestsrandom
  2. 创建一个 requests.Session 对象 s,用于维持会话状态。
  3. 在代码中填入有效的账号和密码(仅支持手机号)。
  4. 定义了一个 int2char 函数,将数字转换为 Base64 字符。
  5. 定义了一个 b64tohex 函数,将 Base64 编码的字符串转换为十六进制表示。
  6. 定义了一个 rsa_encode 函数,使用 RSA 加密给定的字符串。
  7. 定义了一个 calculate_md5_sign 函数,用于计算参数的 MD5 签名。
  8. 定义了一个 login 函数,用于模拟登录到云盘账号。
  9. 定义了一个 main 函数,主要用于签到、抽奖操作以及通过钉钉机器人推送签到结果。
  10. 最后,使用 if __name__ == "__main__": 来判断是否在主程序环境下执行签到操作。

需要注意的是,这段代码模拟了对中国电信189云盘的签到操作,涉及到了用户隐私信息和云盘服务的相关操作。请确保你的账号和密码的隐私安全,以及遵守中国电信云盘的服务条款和法律法规。另外,这个脚本可能会受到电信云盘的接口变动而失效,如果在未来出现问题,可能需要根据电信云盘的接口变动进行相应的调整。

import time
import re
# import json
import base64
import hashlib
# from urllib import parse
  
import rsa
import requests, logging
import random
from os import environ, system, path
import os
import sys
 
 
# 加载通知
def load_send() -> None:
    logging.info("加载推送功能中...")
    global send
    send = None
    cur_path = os.path.abspath(os.path.dirname(__file__))
    sys.path.append(cur_path)
    if os.path.exists(cur_path + "/notify.py"):
        try:
            from notify import send
        except Exception:
            send = None
            logging.info(f"❌加载通知服务失败!!!\n{traceback.format_exc()}")
    else:
        logging.info(f"❌加载通知服务失败!!!\n")
         
BI_RM = list("0123456789abcdefghijklmnopqrstuvwxyz")
  
B64MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  
s = requests.Session()
  
# 在下面两行的引号内贴上账号(仅支持手机号)和密码
username = ""
password = ""
  
_ = """
if(username == "" or password == ""):
    username = input("账号:")
    password = input("密码:")
# """
  
assert username and password, "在第39、40行填入有效账号和密码"
  
def int2char(a):
    return BI_RM[a]
  
  
def b64tohex(a):
    d = ""
    e = 0
    c = 0
    for i in range(len(a)):
        if list(a)[i] != "=":
            v = B64MAP.index(list(a)[i])
            if 0 == e:
                e = 1
                d += int2char(v >> 2)
                c = 3 & v
            elif 1 == e:
                e = 2
                d += int2char(c << 2 | v >> 4)
                c = 15 & v
            elif 2 == e:
                e = 3
                d += int2char(c)
                d += int2char(v >> 2)
                c = 3 & v
            else:
                e = 0
                d += int2char(c << 2 | v >> 4)
                d += int2char(15 & v)
    if e == 1:
        d += int2char(c << 2)
    return d
  
  
def rsa_encode(j_rsakey, string):
    rsa_key = f"-----BEGIN PUBLIC KEY-----\n{j_rsakey}\n-----END PUBLIC KEY-----"
    pubkey = rsa.PublicKey.load_pkcs1_openssl_pem(rsa_key.encode())
    result = b64tohex((base64.b64encode(rsa.encrypt(f'{string}'.encode(), pubkey))).decode())
    return result
  
  
def calculate_md5_sign(params):
    return hashlib.md5('&'.join(sorted(params.split('&'))).encode('utf-8')).hexdigest()
  
  
def login(username, password):
    #https://m.cloud.189.cn/login2014.jsp?redirectURL=https://m.cloud.189.cn/zhuanti/2021/shakeLottery/index.html
    url=""
    urlToken="https://m.cloud.189.cn/udb/udb_login.jsp?pageId=1&pageKey=default&clientType=wap&redirectURL=https://m.cloud.189.cn/zhuanti/2021/shakeLottery/index.html"
    s = requests.Session()
    r = s.get(urlToken)
    pattern = r"https?://[^\s'\"]+"  # 匹配以http或https开头的url
    match = re.search(pattern, r.text)  # 在文本中搜索匹配
    if match:  # 如果找到匹配
        url = match.group()  # 获取匹配的字符串
        # print(url)  # 打印url
    else:  # 如果没有找到匹配
        print("没有找到url")
  
    r = s.get(url)
    # print(r.text)
    pattern = r"<a id=\"j-tab-login-link\"[^>]*href=\"([^\"]+)\""  # 匹配id为j-tab-login-link的a标签,并捕获href引号内的内容
    match = re.search(pattern, r.text)  # 在文本中搜索匹配
    if match:  # 如果找到匹配
        href = match.group(1)  # 获取捕获的内容
        # print("href:" + href)  # 打印href链接
    else:  # 如果没有找到匹配
        print("没有找到href链接")
  
    r = s.get(href)
    captchaToken = re.findall(r"captchaToken' value='(.+?)'", r.text)[0]
    lt = re.findall(r'lt = "(.+?)"', r.text)[0]
    returnUrl = re.findall(r"returnUrl= '(.+?)'", r.text)[0]
    paramId = re.findall(r'paramId = "(.+?)"', r.text)[0]
    j_rsakey = re.findall(r'j_rsaKey" value="(\S+)"', r.text, re.M)[0]
    s.headers.update({"lt": lt})
  
    username = rsa_encode(j_rsakey, username)
    password = rsa_encode(j_rsakey, password)
    url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0',
        'Referer': 'https://open.e.189.cn/',
    }
    data = {
        "appKey": "cloud",
        "accountType": '01',
        "userName": f"{{RSA}}{username}",
        "password": f"{{RSA}}{password}",
        "validateCode": "",
        "captchaToken": captchaToken,
        "returnUrl": returnUrl,
        "mailSuffix": "@189.cn",
        "paramId": paramId
    }
    r = s.post(url, data=data, headers=headers, timeout=5)
    if (r.json()['result'] == 0):
        print(r.json()['msg'])
    else:
        print(r.json()['msg'])
    redirect_url = r.json()['toUrl']
    r = s.get(redirect_url)
    return s
  
  
def main():
    s=login(username, password)
    rand = str(round(time.time() * 1000))
    surl = f'https://api.cloud.189.cn/mkt/userSign.action?rand={rand}&clientType=TELEANDROID&version=8.6.3&model=SM-G930K'
    url = f'https://m.cloud.189.cn/v2/drawPrizeMarketDetails.action?taskId=TASK_SIGNIN&activityId=ACT_SIGNIN'
    url2 = f'https://m.cloud.189.cn/v2/drawPrizeMarketDetails.action?taskId=TASK_SIGNIN_PHOTOS&activityId=ACT_SIGNIN'
    url3 = f'https://m.cloud.189.cn/v2/drawPrizeMarketDetails.action?taskId=TASK_2022_FLDFS_KJ&activityId=ACT_SIGNIN'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; SM-G930K Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36 Ecloud/8.6.3 Android/22 clientId/355325117317828 clientModel/SM-G930K imsi/460071114317824 clientChannelId/qq proVersion/1.0.6',
        "Referer": "https://m.cloud.189.cn/zhuanti/2016/sign/index.jsp?albumBackupOpened=1",
        "Host": "m.cloud.189.cn",
        "Accept-Encoding": "gzip, deflate",
    }
    response = s.get(surl, headers=headers)
    netdiskBonus = response.json()['netdiskBonus']
    if (response.json()['isSign'] == "false"):
        print(f"未签到,签到获得{netdiskBonus}M空间")
        res1 = f"未签到,签到获得{netdiskBonus}M空间"
    else:
        print(f"已经签到过了,签到获得{netdiskBonus}M空间")
        res1 = f"已经签到过了,签到获得{netdiskBonus}M空间"
  
    headers = {
        'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; SM-G930K Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36 Ecloud/8.6.3 Android/22 clientId/355325117317828 clientModel/SM-G930K imsi/460071114317824 clientChannelId/qq proVersion/1.0.6',
        "Referer": "https://m.cloud.189.cn/zhuanti/2016/sign/index.jsp?albumBackupOpened=1",
        "Host": "m.cloud.189.cn",
        "Accept-Encoding": "gzip, deflate",
    }
    response = s.get(url, headers=headers)
    if ("errorCode" in response.text):
        print(response.text)
        res2 = ""
    else:
        description = response.json()['description']
        print(f"链接1抽奖获得{description}")
        res2 = f"链接1抽奖获得{description}"
    response = s.get(url2, headers=headers)
    if ("errorCode" in response.text):
        print(response.text)
        res3 = ""
    else:
        description = response.json()['description']
        print(f"链接2抽奖获得{description}")
        res3 = f"链接2抽奖获得{description}"
  
    response = s.get(url3, headers=headers)
    if ("errorCode" in response.text):
        print(response.text)
        res4 = ""
    else:
        description = response.json()['description']
        print(f"链接3抽奖获得{description}")
        res4 = f"链接3抽奖获得{description}"  
    #推送
    result = f"{res1}, {res2}, {res3}, {res4}"
    send('天翼云盘签到', result)
  
if __name__ == "__main__":
     # 加载通知
    load_send()
    time.sleep(random.randint(5, 25))
    main()

下载网易云音乐

uupython阅读(550)

这段代码是一个用于下载网易云音乐中的音乐文件的脚本。代码中使用了 execjs 库来执行 JavaScript 代码,模拟了从网易云音乐获取音乐链接的过程。以下是代码的主要部分解释:

  1. 导入所需的库和模块,包括 subprocessexecjsrequestsfunctools.partial
  2. 使用 functools.partialsubprocess.Popen 进行了部分参数锁定,将 encoding 参数设置为 "utf-8"
  3. 定义了要请求的 URL 和 POST 数据。
  4. 使用 with open 语句读取 wyy.js 文件中的 JavaScript 代码。
  5. 使用 execjs.compile 编译 JavaScript 代码,准备执行。
  6. 调用 JavaScript 函数 fu 来获取数据,js_exec.call("fu", data) 返回的是一个包含 encTextencSecKey 的字典。
  7. 使用获取到的数据来构建 POST 请求,发送给 url
  8. 获取音乐链接并使用 requests.get 下载音乐文件。
  9. 将下载的音乐文件保存到名为 'ネオンサインが呼んでる.m4a' 的文件中。

需要注意的是,这段代码涉及到网易云音乐的一些加密操作和请求过程,可能受到网易云音乐的接口变动影响而失效。如果你的代码在未来出现问题,可能需要根据网易云音乐的接口变动进行相应的调整。另外,网易云音乐的使用可能受到其服务条款和法律法规的限制,务必遵守相关规定。

var CryptoJS = require("crypto-js");
var bitsPerDigit = 16
var biHalfRadix = 32768
var maxDigitVal = 65535
var highBitMasks = [0, 32768, 49152, 57344, 61440, 63488, 64512, 65024, 65280, 65408, 65472, 65504, 65520, 65528, 65532, 65534, 65535]
var biRadix = 65536
var biRadixSquared = 4294967296
var biRadixBits = 16
var lowBitMasks = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535]
var hexToChar = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']

function setMaxDigits(a) {
    maxDigits = a,
        ZERO_ARRAY = new Array(maxDigits);
    for (var b = 0; b < ZERO_ARRAY.length; b++)
        ZERO_ARRAY[b] = 0;
    bigZero = new BigInt,
        bigOne = new BigInt,
        bigOne.digits[0] = 1
}

function reverseStr(a) {
    var c, b = "";
    for (c = a.length - 1; c > -1; --c)
        b += a.charAt(c);
    return b
}

function BarrettMu_modulo(a) {
    var i, b = biDivideByRadixPower(a, this.k - 1), c = biMultiply(b, this.mu), d = biDivideByRadixPower(c, this.k + 1),
        e = biModuloByRadixPower(a, this.k + 1), f = biMultiply(d, this.modulus),
        g = biModuloByRadixPower(f, this.k + 1), h = biSubtract(e, g);
    for (h.isNeg && (h = biAdd(h, this.bkplus1)),
             i = biCompare(h, this.modulus) >= 0; i;)
        h = biSubtract(h, this.modulus),
            i = biCompare(h, this.modulus) >= 0;
    return h
}

function BarrettMu_powMod(a, b) {
    var d, e, c = new BigInt;
    for (c.digits[0] = 1,
             d = a,
             e = b; ;) {
        if (0 != (1 & e.digits[0]) && (c = this.multiplyMod(c, d)),
            e = biShiftRight(e, 1),
        0 == e.digits[0] && 0 == biHighIndex(e))
            break;
        d = this.multiplyMod(d, d)
    }
    return c
}

function encryptedString(a, b) {
    for (var f, g, h, i, j, k, l, c = new Array, d = b.length, e = 0; d > e;)
        c[e] = b.charCodeAt(e),
            e++;
    for (; 0 != c.length % a.chunkSize;)
        c[e++] = 0;
    for (f = c.length,
             g = "",
             e = 0; f > e; e += a.chunkSize) {
        for (j = new BigInt,
                 h = 0,
                 i = e; i < e + a.chunkSize; ++h)
            j.digits[h] = c[i++],
                j.digits[h] += c[i++] << 8;
        k = a.barrett.powMod(j, a.e),
            l = 16 == a.radix ? biToHex(k) : biToString(k, a.radix),
            g += l + " "
    }
    return g.substring(0, g.length - 1)
}

function biMultiply(a, b) {
    var d, h, i, k, c = new BigInt, e = biHighIndex(a), f = biHighIndex(b);
    for (k = 0; f >= k; ++k) {
        for (d = 0,
                 i = k,
                 j = 0; e >= j; ++j,
                 ++i)
            h = c.digits[i] + a.digits[j] * b.digits[k] + d,
                c.digits[i] = h & maxDigitVal,
                d = h >>> biRadixBits;
        c.digits[k + e + 1] = d
    }
    return c.isNeg = a.isNeg != b.isNeg,
        c
}

function biModuloByRadixPower(a, b) {
    var c = new BigInt;
    return arrayCopy(a.digits, 0, c.digits, 0, b),
        c
}

function biToHex(a) {
    var d, b = "";
    for (biHighIndex(a),
             d = biHighIndex(a); d > -1; --d)
        b += digitToHex(a.digits[d]);
    return b
}

function digitToHex(a) {
    var b = 15
        , c = "";
    for (i = 0; 4 > i; ++i)
        c += hexToChar[a & b],
            a >>>= 4;
    return reverseStr(c)
}

function biDivideByRadixPower(a, b) {
    var c = new BigInt;
    return arrayCopy(a.digits, b, c.digits, 0, c.digits.length - b),
        c
}

function BarrettMu_multiplyMod(a, b) {
    var c = biMultiply(a, b);
    return this.modulo(c)
}

function biShiftRight(a, b) {
    var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt;
    for (arrayCopy(a.digits, c, d.digits, 0, a.digits.length - c),
             e = b % bitsPerDigit,
             f = bitsPerDigit - e,
             g = 0,
             h = g + 1; g < d.digits.length - 1; ++g,
             ++h)
        d.digits[g] = d.digits[g] >>> e | (d.digits[h] & lowBitMasks[e]) << f;
    return d.digits[d.digits.length - 1] >>>= e,
        d.isNeg = a.isNeg,
        d
}

function biMultiplyDigit(a, b) {
    var c, d, e, f;
    for (result = new BigInt,
             c = biHighIndex(a),
             d = 0,
             f = 0; c >= f; ++f)
        e = result.digits[f] + a.digits[f] * b + d,
            result.digits[f] = e & maxDigitVal,
            d = e >>> biRadixBits;
    return result.digits[1 + c] = d,
        result
}

function biSubtract(a, b) {
    var c, d, e, f;
    if (a.isNeg != b.isNeg)
        b.isNeg = !b.isNeg,
            c = biAdd(a, b),
            b.isNeg = !b.isNeg;
    else {
        for (c = new BigInt,
                 e = 0,
                 f = 0; f < a.digits.length; ++f)
            d = a.digits[f] - b.digits[f] + e,
                c.digits[f] = 65535 & d,
            c.digits[f] < 0 && (c.digits[f] += biRadix),
                e = 0 - Number(0 > d);
        if (-1 == e) {
            for (e = 0,
                     f = 0; f < a.digits.length; ++f)
                d = 0 - c.digits[f] + e,
                    c.digits[f] = 65535 & d,
                c.digits[f] < 0 && (c.digits[f] += biRadix),
                    e = 0 - Number(0 > d);
            c.isNeg = !a.isNeg
        } else
            c.isNeg = a.isNeg
    }
    return c
}

function biCompare(a, b) {
    if (a.isNeg != b.isNeg)
        return 1 - 2 * Number(a.isNeg);
    for (var c = a.digits.length - 1; c >= 0; --c)
        if (a.digits[c] != b.digits[c])
            return a.isNeg ? 1 - 2 * Number(a.digits[c] > b.digits[c]) : 1 - 2 * Number(a.digits[c] < b.digits[c]);
    return 0
}

function biMultiplyByRadixPower(a, b) {
    var c = new BigInt;
    return arrayCopy(a.digits, 0, c.digits, b, c.digits.length - b),
        c
}

function biFromHex(a) {
    var d, e, b = new BigInt, c = a.length;
    for (d = c,
             e = 0; d > 0; d -= 4,
             ++e)
        b.digits[e] = hexToDigit(a.substr(Math.max(d - 4, 0), Math.min(d, 4)));
    return b
}

function biCopy(a) {
    var b = new BigInt(!0);
    return b.digits = a.digits.slice(0),
        b.isNeg = a.isNeg,
        b
}

function arrayCopy(a, b, c, d, e) {
    var g, h, f = Math.min(b + e, a.length);
    for (g = b,
             h = d; f > g; ++g,
             ++h)
        c[h] = a[g]
}

function biShiftLeft(a, b) {
    var e, f, g, h, c = Math.floor(b / bitsPerDigit), d = new BigInt;
    for (arrayCopy(a.digits, 0, d.digits, c, d.digits.length - c),
             e = b % bitsPerDigit,
             f = bitsPerDigit - e,
             g = d.digits.length - 1,
             h = g - 1; g > 0; --g,
             --h)
        d.digits[g] = d.digits[g] << e & maxDigitVal | (d.digits[h] & highBitMasks[e]) >>> f;
    return d.digits[0] = d.digits[g] << e & maxDigitVal,
        d.isNeg = a.isNeg,
        d
}

function biDivideModulo(a, b) {
    var f, g, h, i, j, k, l, m, n, o, p, q, r, s, c = biNumBits(a), d = biNumBits(b), e = b.isNeg;
    if (d > c)
        return a.isNeg ? (f = biCopy(bigOne),
            f.isNeg = !b.isNeg,
            a.isNeg = !1,
            b.isNeg = !1,
            g = biSubtract(b, a),
            a.isNeg = !0,
            b.isNeg = e) : (f = new BigInt,
            g = biCopy(a)),
            new Array(f, g);
    for (f = new BigInt,
             g = a,
             h = Math.ceil(d / bitsPerDigit) - 1,
             i = 0; b.digits[h] < biHalfRadix;)
        b = biShiftLeft(b, 1),
            ++i,
            ++d,
            h = Math.ceil(d / bitsPerDigit) - 1;
    for (g = biShiftLeft(g, i),
             c += i,
             j = Math.ceil(c / bitsPerDigit) - 1,
             k = biMultiplyByRadixPower(b, j - h); -1 != biCompare(g, k);)
        ++f.digits[j - h],
            g = biSubtract(g, k);
    for (l = j; l > h; --l) {
        for (m = l >= g.digits.length ? 0 : g.digits[l],
                 n = l - 1 >= g.digits.length ? 0 : g.digits[l - 1],
                 o = l - 2 >= g.digits.length ? 0 : g.digits[l - 2],
                 p = h >= b.digits.length ? 0 : b.digits[h],
                 q = h - 1 >= b.digits.length ? 0 : b.digits[h - 1],
                 f.digits[l - h - 1] = m == p ? maxDigitVal : Math.floor((m * biRadix + n) / p),
                 r = f.digits[l - h - 1] * (p * biRadix + q),
                 s = m * biRadixSquared + (n * biRadix + o); r > s;)
            --f.digits[l - h - 1],
                r = f.digits[l - h - 1] * (p * biRadix | q),
                s = m * biRadix * biRadix + (n * biRadix + o);
        k = biMultiplyByRadixPower(b, l - h - 1),
            g = biSubtract(g, biMultiplyDigit(k, f.digits[l - h - 1])),
        g.isNeg && (g = biAdd(g, k),
            --f.digits[l - h - 1])
    }
    return g = biShiftRight(g, i),
        f.isNeg = a.isNeg != e,
    a.isNeg && (f = e ? biAdd(f, bigOne) : biSubtract(f, bigOne),
        b = biShiftRight(b, i),
        g = biSubtract(b, g)),
    0 == g.digits[0] && 0 == biHighIndex(g) && (g.isNeg = !1),
        new Array(f, g)
}

function biNumBits(a) {
    var e, b = biHighIndex(a), c = a.digits[b], d = (b + 1) * bitsPerDigit;
    for (e = d; e > d - bitsPerDigit && 0 == (32768 & c); --e)
        c <<= 1;
    return e
}

function biDivide(a, b) {
    return biDivideModulo(a, b)[0]
}

function BarrettMu(a) {
    this.modulus = biCopy(a),
        this.k = biHighIndex(this.modulus) + 1;
    var b = new BigInt;
    b.digits[2 * this.k] = 1,
        this.mu = biDivide(b, this.modulus),
        this.bkplus1 = new BigInt,
        this.bkplus1.digits[this.k + 1] = 1,
        this.modulo = BarrettMu_modulo,
        this.multiplyMod = BarrettMu_multiplyMod,
        this.powMod = BarrettMu_powMod
}

function charToHex(a) {
    var h, b = 48, c = b + 9, d = 97, e = d + 25, f = 65, g = 90;
    return h = a >= b && c >= a ? a - b : a >= f && g >= a ? 10 + a - f : a >= d && e >= a ? 10 + a - d : 0
}

function biHighIndex(a) {
    for (var b = a.digits.length - 1; b > 0 && 0 == a.digits[b];)
        --b;
    return b
}

function hexToDigit(a) {
    var d, b = 0, c = Math.min(a.length, 4);
    for (d = 0; c > d; ++d)
        b <<= 4,
            b |= charToHex(a.charCodeAt(d));
    return b
}

function BigInt(a) {
    this.digits = "boolean" == typeof a && 1 == a ? null : ZERO_ARRAY.slice(0),
        this.isNeg = !1
}

function a(a) {
    var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
    for (d = 0; a > d; d += 1)
        e = Math.random() * b.length,
            e = Math.floor(e),
            c += b.charAt(e);
    return c
};

function b(a, b) {
    var c = CryptoJS.enc.Utf8.parse(b)
        , d = CryptoJS.enc.Utf8.parse("0102030405060708")
        , e = CryptoJS.enc.Utf8.parse(a)
        , f = CryptoJS.AES.encrypt(e, c, {
        iv: d,
        mode: CryptoJS.mode.CBC
    });
    return f.toString()
}

function c(a, b, c) {
    var d, e;
    return setMaxDigits(131),
        d = new RSAKeyPair(b, "", c),
        e = encryptedString(d, a)
}

function RSAKeyPair(a, b, c) {
    this.e = biFromHex(a),
        this.d = biFromHex(b),
        this.m = biFromHex(c),
        this.chunkSize = 2 * biHighIndex(this.m),
        this.radix = 16,
        this.barrett = new BarrettMu(this.m)
}

function d(d, e, f, g) {
    var h = {}
        , i = a(16);
    return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
}
// 测试
// let i0x = {
//     "csrf_token": "",
//     "encodeType": "aac",
//     "ids": "[1461402136]",
//     "level": "standard"
// }

function fu(i0x){
    var ccccc = d(JSON.stringify(i0x), '010001', '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7', '0CoJUm6Qyw8W8jud');
    console.log(ccccc);
    return ccccc;
};
from functools import partial  # 锁定参数
import subprocess
import execjs   # 此时再引入execjs的时候. 里面就可以自动使用你的subprocess.Popen
import requests

subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")

url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='

data = {
    "csrf_token": "",
    "encodeType": "aac",
    "ids": "[1461402136]",
    "level": "standard"
}

# 读取js代码
with open("wyy.js", mode="r", encoding="utf-8") as f:
    js_code = f.read()  # 读取所有js代码

# 加载代码
js_exec = execjs.compile(js_code)

# 用js_exec去执行js代码中的函数
r = js_exec.call("fu", data)  # 没有代码提示.  lxml
print(r)
res = requests.post(url,data={
    'params':r['encText'],
    'encSecKey':r['encSecKey']
},headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
})
a = res.json()['data'][0]['url']

req = requests.get(a,headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
})

with open('ネオンサインが呼んでる.m4a','wb') as f:
    f.write(req.content)

多线程下载故宫壁纸

uupython阅读(503)

这段代码是一个简单的爬虫脚本,用于从网页上下载故宫壁纸图片。它使用了多线程池来提高图片下载的效率。

主要功能:

  1. 导入所需的库和模块,包括 timeosrequestslxmlmultiprocessing.dummy(多线程池)。
  2. 定义了一个全局变量 count,用于给下载的图片文件编号。
  3. 创建一个函数 get_img_urls(url),用于从指定的 URL 中获取图片的链接,并创建一个多线程池来并发下载图片。
  • 使用 requests 获取网页内容。
  • 使用 etree 解析 HTML 内容。
  • 从解析后的 HTML 中提取图片的标题和链接。
  • 创建一个多线程池,每个线程调用 down_load 函数进行图片下载。
  1. 创建另一个函数 down_load(name, url),用于实际下载图片文件。
  • 使用 requests 获取图片内容。
  • 在指定的文件夹路径下保存下载的图片,文件名以 count 编号和图片标题命名。
  • 递增全局变量 count
  • 为了避免频繁下载,每下载一个图片后休眠 0.1 秒。
  1. __main__ 部分:
  • 记录开始时间。
  • 循环遍历 1 到 10 页,构建 URL,并调用 get_img_urls 函数下载每页的图片。
  • 输出总下载时间。

这段代码通过多线程池并发下载图片,可以有效地提高下载速度。不过需要注意,多线程下载可能会对网站造成一定程度的压力,需要合理使用。另外,代码中使用了全局变量 count 来为下载的图片编号,这可能在多线程情况下会有竞争条件,你可以考虑使用锁机制来避免可能的问题。

import time
import os
import requests
from lxml import etree
from multiprocessing.dummy import Pool
 
headers = {
    "Accept": "*/*",
    "Referer": "https://www.dpm.org.cn/lights/royal.html",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
}
 
 
def get_img_urls(url):
    response = requests.get(url, headers=headers)
    response.encoding = 'utf-8'
    # print(response.text)
    html = etree.HTML(response.text)
    imgs = html.xpath('//div[@class="pic"]/a/img')
    multi_pool = Pool(16)  # 创建16个线程池
    for i in imgs:
        title = i.xpath('./@title')[0]
        src = i.xpath('./@src')[0]
        multi_pool.apply_async(down_load, args=(title, src))
    multi_pool.close()
    multi_pool.join()
 
 
def down_load(name, url):
    res = requests.get(url, headers=headers)
    global count
    path = './故宫壁纸'  # 下载路径(当前文件夹下‘故宫壁纸’文件夹)
    if not os.path.exists(path):
        os.mkdir(path)
    print(f'正在下载>>> {count}_{name}.jpg   ...')
    with open(f'{path}/{count}_{name}.jpg','wb') as f:
        f.write(res.content)
        print(f'图_{count} 下载完成!')
        count += 1
        time.sleep(0.1)
 
 
if __name__ == '__main__':
    star_t = time.time()
    count = 1
    url = "https://www.dpm.org.cn/lights/royal/p/{}.html"
    for page in range(1, 11):  # 下载1-10页的图
        get_img_urls(url.format(page))
 
    print(f'All Done in {time.time()-star_t:.3f} seconds')

获取知乎热榜

uupython阅读(517)

这段代码定义了一个 Zhihu 类,用于获取知乎的热榜和推荐内容,并对数据进行清洗,以便于进一步处理和显示。

主要功能:

  • 使用 requests 库发送 HTTP 请求来获取知乎热榜和推荐数据。
  • 清洗获取的数据,提取标题和对应的链接。

具体步骤解释:

  1. 初始化方法 __init__
  • 设置热榜和推荐的 API 地址。
  • 设置请求头部信息,模拟浏览器请求。
  • 调用 get_hot_listsget_recommend_lists 方法获取未处理的热榜和推荐数据。
  • 调用 wash_hot_listswash_recommend_lists 方法对数据进行清洗,提取标题和链接。
  • 最终保存处理后的热榜和推荐数据。
  1. get_hot_lists 方法:
  • 构建请求参数,包括限制条目数量和不使用浏览器模式。
  • 使用 requests.get 发送 GET 请求,获取热榜数据的 JSON 响应。
  1. get_recommend_lists 方法:
  • 构建请求参数,包括推荐相关参数,如动作、广告间隔、后续 ID 和页码。
  • 使用 requests.get 发送 GET 请求,获取推荐数据的 JSON 响应。
  1. wash_hot_lists 方法:
  • 遍历热榜数据的每个条目,提取标题和链接。
  • 对链接进行处理,将 API 地址转换为浏览器中的地址。
  • 将处理后的标题和链接以 Markdown 格式保存。
  1. wash_recommend_lists 方法:
  • 遍历推荐数据的每个条目,尝试提取问题标题和链接。
  • 如果提取失败,则获取条目的标题和链接。
  • 对链接进行处理,将 API 地址转换为浏览器中的地址。
  • 将处理后的标题和链接以 Markdown 格式保存。

这段代码的目的是将获取的热榜和推荐数据清洗为 Markdown 格式的链接,方便在其他地方进行展示或者生成文本。你可以根据需要,进一步扩展和使用这个 Zhihu 类,例如将清洗后的数据展示在 GUI 界面中,或者生成 HTML 文件等。

import requests

class Zhihu:
    """
    知乎热榜
    """

    def __init__(self):
        self.hot_lists_api = 'https://api.zhihu.com/topstory/hot-lists/total'  # 热榜api
        self.recommend_lists_api = 'https://api.zhihu.com/topstory/recommend'  # 推荐api
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
        }
        self.hot = self.get_hot_lists()  # 热榜未处理数据
        self.recommend = self.get_recommend_lists()  # 推荐未处理数据
        self.hot_data = self.wash_hot_lists()  # 热榜处理后数据
        self.recommend_data = self.wash_recommend_lists()  # 推荐处理后数据

    def get_hot_lists(self):
        """
        获取知乎热榜
        :return: json
        """
        params = {'limit': '10',
                  'is_browser_model': '0'}
        response = requests.get(url=self.hot_lists_api, headers=self.headers, params=params)
        return response.json()

    def get_recommend_lists(self):
        """
        获取随机推荐
        :return:
        """
        params = {
            "action": "down",
            "ad_interval": "-10",
            "after_id": '1',  # TODO:
            "page_number": '1',  # TODO:
            "session_token": "99872c210b53364be1ede4bf459e8005", }
        response = requests.get(url=self.recommend_lists_api, headers=self.headers, params=params)
        return response.json()

    def wash_hot_lists(self):
        """
        清洗热榜数据
        :return:['[title](url)',....]
        """
        hot_lists = []
        for data in self.hot['data']:
            title = data['target']['title']
            url = data['target']['url'].replace('api.zhihu.com/questions', 'zhihu.com/question')
            hot_lists.append(f'[{title}]({url})')
        return hot_lists

    def wash_recommend_lists(self):
        """
        清洗推荐数据
        :return:
        """
        hot_lists = []
        for data in self.recommend['data']:
            try:
                title = data['target']['question']['title']
                url = data['target']['question']['url'].replace('api.zhihu.com/questions', 'zhihu.com/question')
            except KeyError:
                title = data['target']['title']
                url = data['target']['url'].replace('api.zhihu.com/questions', 'zhihu.com/question')
            hot_lists.append(f'[{title}]({url})')
        return hot_lists

zhihu = Zhihu()

[GUI]局域网大文件分享传输工具

uupython阅读(433)

这段代码实现了一个基于 PyQt5 的文件分享应用,可以选择使用 HTTP 或 FTP 协议分享文件,以便其他设备通过链接进行下载。

主要界面:

  • 可选择使用 HTTP 或 FTP 协议进行文件分享。
  • 显示当前设备的 IP 地址。
  • 提供设置端口号、选择文件夹、选择文件等功能。
  • 启动按钮用于启动文件分享服务。
  • 提供复制分享链接的文本框。

主要功能:

  • 启动 HTTP 服务器:基于 Flask 框架,可以通过浏览器访问链接下载文件。
  • 启动 FTP 服务器:使用 pyftpdlib 库,可以通过 FTP 客户端访问链接下载文件。
  • 自动获取当前设备的 IP 地址。

你可以根据需要对界面进行修改和美化,添加更多功能和选项,例如选择其他协议、增加传输目录、增加进度显示等。

要运行这个应用,确保已经安装了 PyQt5netifacesflaskpyftpdlib 这些库。如果没有安装,可以使用以下命令进行安装:

pip install PyQt5 netifaces flask pyftpdlib

然后运行这个 Python 脚本,即可启动文件分享应用。注意,这个应用涉及到网络服务,可以在局域网内进行使用,不建议在公共网络中使用,以防安全风险。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from PyQt5.QtWidgets import QApplication, QRadioButton, QMainWindow, QLabel, QComboBox, QLineEdit, QPushButton, QMessageBox, QToolButton, QFileDialog, QDialog
from PyQt5.QtCore import QDir, Qt, QThread, pyqtSignal
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtGui import QIcon,QFont
import sys
import os 
import random
import netifaces
from flask import Flask, send_file
from concurrent.futures import ThreadPoolExecutor
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import ThreadedFTPServer
from ftplib import FTP
from urllib.parse import urlparse
 
class HelpWindow(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("帮助")
        self.setFixedSize(300, 280)
        self.center()
        self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowTitleHint)
        self.label = QLabel(self)
        self.label.setText("1、选择文件传输协议;2、输入端口号(建议10000~60000,一般默认即可); \
                            3、选择文件所在目录;4、选择要分享的文件;5、点击启动按钮,\
                           若防火墙弹出警示点击允许访问即可;6、将生成的链接复制发送给接收方, \
                           接收方通过浏览器访问即可下载。若通过FTP协议分享还可通过FTP客户端软件  \
                            或本程序的“接收FTP文件”按钮下载。注意:传输完成后请及时关闭程序, \
                            以免造成数据泄露和网络安全隐患。")
        self.label.setGeometry(10, 10, 280, 260)
        self.label.setWordWrap(True)
        font = QFont()
        font.setPointSize(10)   
        font.setBold(True)   
        self.label.setFont(font)
         
    def center(self):
        screen = QApplication.desktop().screenGeometry()
        window = self.geometry()
        x = (screen.width() - window.width()) // 2
        y = (screen.height() - window.height()) // 2
        self.move(x, y)
 
class DownloadThread(QThread):
    finished = pyqtSignal(bool)
    ftp = FTP()
 
    def __init__(self, ftp_url):
        super().__init__()
        self.ftp_url = ftp_url
 
    def run(self):
        parsed_url = urlparse(self.ftp_url)
        username = parsed_url.username
        password = parsed_url.password
        host = parsed_url.hostname
        port = parsed_url.port
        filename = parsed_url.path.rsplit('/', 1)[-1]
        if len(filename)==0:
            downloadfile = False
        else:
            downloadfile = True
        try:
            self.ftp.connect(host, port)
            self.ftp.encoding = 'utf-8'
            self.ftp.login(user=username, passwd=password)
 
            current_dir = os.getcwd()
            current_dir = current_dir.replace("\\", "/")
            random_int = random.randint(100, 999)
            mulu = "接收文件_" + str(random_int)
            LocalDir = current_dir + "/" + mulu
            if not os.path.exists(LocalDir):
                os.makedirs(LocalDir)
            if downloadfile:#下载一个文件
                Local = os.path.join(LocalDir, filename)
                self.DownLoadFile(Local,filename)
            else:#下载整个目录
                self.DownLoadFileTree(LocalDir,"/")
            self.ftp.quit()
            self.finished.emit(True)
        except Exception as e:
            self.finished.emit(False)
 
    def DownLoadFile(self, LocalFile, RemoteFile):  # 下载单个文件
        with open(LocalFile, 'wb') as file_handler:
            self.ftp.retrbinary('RETR ' + RemoteFile, file_handler.write)
        file_handler.close()
        return True
 
    def DownLoadFileTree(self, LocalDir, RemoteDir):  # 下载整个目录下的文件
        print("远程文件夹remoteDir:", RemoteDir)
        if not os.path.exists(LocalDir):
            os.makedirs(LocalDir)
        self.ftp.cwd(RemoteDir)
        RemoteNames = self.ftp.nlst()
        print("远程文件目录:", RemoteNames)
        for file in RemoteNames:
            Local = os.path.join(LocalDir, file)
            #print("正在下载", self.ftp.nlst(file))
            try:
                self.ftp.cwd(file)
                self.ftp.cwd("..")
                if not os.path.exists(Local):
                    os.makedirs(Local)
                self.DownLoadFileTree(Local, file)
            except:
                self.DownLoadFile(Local, file)
        self.ftp.cwd("..")
        return
     
class FTPWindow(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("下载FTP文件")
        self.setFixedSize(400, 150)
 
        self.label_ftp_url = QLabel("FTP链接:", self)
        self.label_ftp_url.setGeometry(20, 20, 80, 30)
 
        self.textbox_ftp_url = QLineEdit(self)
        self.textbox_ftp_url.setGeometry(100, 20, 280, 30)
 
        self.button_download = QPushButton("下载", self)
        self.button_download.setGeometry(160, 65, 80, 30)
        self.button_download.clicked.connect(self.download_ftp_file)
 
        self.label_ftp_info = QLabel("输入包含文件名的链接下载指定文件;\n输入不包含文件名只到端口号的链接下载整个目录。", self)
        self.label_ftp_info.setGeometry(20, 110, 380, 40)
 
    def download_ftp_file(self):
        ftp_url = self.textbox_ftp_url.text()
        if ftp_url == "":
            return
        self.button_download.setEnabled(False)
        self.thread = DownloadThread(ftp_url)
        self.thread.finished.connect(self.show_message_box)
        self.thread.start()
 
    def show_message_box(self, success):
        if success:
            QMessageBox.information(self, "提示", "文件下载成功!", QMessageBox.Ok)
        else:
            QMessageBox.critical(self, "错误", "文件下载失败!", QMessageBox.Ok)
        self.button_download.setEnabled(True)
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("文件分享")
        self.setWindowIcon(QIcon('icon.ico'))
        self.setMaximumSize(self.size())
        self.setFixedSize(400, 300)
 
        self.tcp_label = QLabel('选择传输方式:', self)
        self.tcp_label.move(20, 10)
        self.http_button = QRadioButton('HTTP', self)
        self.http_button.setChecked(True)
        self.http_button.move(150, 10)
        self.http_button.toggled.connect(self.onProtocolChanged)
        self.ftp_button = QRadioButton('FTP', self)
        self.ftp_button.move(250, 10)
        self.ftp_button.toggled.connect(self.onProtocolChanged)
 
        self.ip_label = QLabel('当前IP地址:', self)
        self.ip_label.move(20, 40)
        self.ip_text = QLabel(self.get_wired_ip(), self)
        self.ip_text.setGeometry(120, 43, 250, 25)
 
        self.port_label = QLabel('端 口 :', self)
        self.port_label.move(20, 75)
 
        self.textbox = QLineEdit(self)
        self.textbox.setGeometry(80, 75, 300, 30)
        self.textbox.setValidator(QtGui.QIntValidator(10000, 65500))
        self.textbox.setText(str(random.randint(10000, 65500)))  
 
        self.folder_label = QLabel('文件夹:', self)
        self.folder_label.setGeometry(20, 115, 50, 30)
 
        self.textfol = QLineEdit(self)
        self.textfol.setGeometry(80, 115, 220, 30)
        self.textfol.setText(QDir.currentPath())
        self.textfol.setReadOnly(True)
 
        self.folder_button = QPushButton('选择文件夹', self)
        self.folder_button.setGeometry(300, 115, 80, 30)
        self.folder_button.clicked.connect(self.onFolderClicked)
 
        self.file_label = QLabel('文 件 :', self)
        self.file_label.setGeometry(20, 155, 50, 30)
 
        self.combobox = QComboBox(self)
        self.combobox.setGeometry(80, 155, 300, 30)
 
        self.updateFileList()
 
        self.button = QPushButton(self)
        self.button.setText("启  动")
        self.button.setGeometry(20, 195, 360, 30)
        self.button.clicked.connect(self.show_selection)
 
        self.textboxa = QLineEdit(self)
        self.textboxa.setGeometry(20, 235, 360, 30)
        self.textboxa.mousePressEvent = self.select_text
 
        self.hidden_button = QPushButton("接收\nFTP\n文件", self)
        self.hidden_button.setVisible(False)  
        self.hidden_button.setGeometry(320, 10, 60, 55)
        self.hidden_button.clicked.connect(self.show_ftp_window)
 
        self.labela = QLabel(self)
        self.labela.setText("52pojie")
        self.labela.setGeometry(320, 280, 360, 20)
 
        self.labelb = QPushButton(self)
        self.labelb.setText("?")
        self.labelb.setToolTip("帮助")  
        self.labelb.setGeometry(10, self.height() - 30, 30, 30) 
        self.labelb.clicked.connect(self.open_help_window) 
 
        self.help_window = HelpWindow()
 
        self.thread_pool = ThreadPoolExecutor(max_workers=5)
 
    def open_help_window(self):
        self.help_window.show()
 
    def select_text(self, event):
        self.textboxa.selectAll()
        self.textboxa.setFocus()
 
    def show_ftp_window(self):
        ftp_window = FTPWindow(self)
        ftp_window.exec_()
 
    def onProtocolChanged(self):
        if self.ftp_button.isChecked():
            self.hidden_button.setVisible(True)
        else:
            self.hidden_button.setVisible(False)
 
    def onFolderClicked(self):
        m = QFileDialog.getExistingDirectory(self, "选取文件夹", QDir.currentPath()) 
        self.textfol.setText(m)
        self.updateFileList()
 
    def updateFileList(self):
        folder_path = self.textfol.text()
        file_list = QDir(folder_path).entryList(QDir.Files)
        self.combobox.clear()
        self.combobox.addItems(file_list)
 
    def get_wired_ip(self):
        try:
            default_gateway = netifaces.gateways()['default']
            if default_gateway and netifaces.AF_INET in default_gateway:
                routingNicName = default_gateway[netifaces.AF_INET][1]
                for interface in netifaces.interfaces():
                    if interface == routingNicName:
                        routingIPAddr = netifaces.ifaddresses(interface).get(netifaces.AF_INET)
                        if routingIPAddr:
                            return routingIPAddr[0]['addr']
        except (KeyError, IndexError):
            pass
        return "未找到物理网卡IP"
 
    def start_flask_server(self):
        app = Flask(__name__)
 
        @app.route('/download/<filename>', methods=['GET'])
        def download_file(filename):
            file_path = os.path.join(self.textfol.text(), filename) 
            return send_file(file_path, as_attachment=True)
 
        port = int(self.textbox.text())
        self.textbox.setText(str(random.randint(10000, 65500)))
        app.run(host='0.0.0.0', port=port, threaded=True)
         
 
    def start_ftp_server(self):
        authorizer = DummyAuthorizer()
        authorizer.add_user('a', 'a', self.textfol.text(), perm='elradfmwM')
 
        class CustomFTPHandler(FTPHandler):
            def on_file_received(self, file):
                pass
            def on_file_deleted(self, file):
                pass
            def on_rename(self, old_file, new_file):
                pass
 
        handler = CustomFTPHandler
        handler.authorizer = authorizer
 
        address = ('0.0.0.0', int(self.textbox.text()))
 
        server = ThreadedFTPServer(address, handler)
        self.thread_pool.submit(server.serve_forever)
        self.textbox.setText(str(random.randint(10000, 65500)))
 
    def show_selection(self):
        ip_address = self.get_wired_ip()
        port_text = self.textbox.text()
        if int(port_text)<10000:
            QMessageBox.critical(self, "警告", "请勿使用10000以下端口号!", QMessageBox.Ok)
            self.textbox.setText(str(random.randint(10000, 65500)))
            return
        selected_file = self.combobox.currentText()
        if self.http_button.isChecked():
            self.thread_pool.submit(self.start_flask_server)
            self.textboxa.setText("http://" + ip_address + ":" + port_text + "/download/" + selected_file)
        elif self.ftp_button.isChecked():
            self.thread_pool.submit(self.start_ftp_server)
            self.textboxa.setText("ftp://a:a@" + ip_address + ":" + port_text + "/" + selected_file)
        else:
            QMessageBox.warning(self, "警告", "请选择传输方式!", QMessageBox.Ok)
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = MyWindow()
    win.show()
    sys.exit(app.exec_())

通过串口收发文件

uupython阅读(481)

这段代码实现了使用串口进行文件传输,采用 XMODEM 协议进行文件发送和接收。具体来说,代码包括以下主要功能:

  1. Send_File(filepath, port='COM8', baudrate=115200): 该函数用于将指定路径的文件通过串口发送。它首先构建一个包含文件大小和文件名的指令,并发送该指令。然后通过 XMODEM 协议将文件内容发送到目标设备。
  2. Recv_File(port='COM2', baudrate=115200): 该函数用于在接收端监听指令并执行相应操作。它会不断地等待接收到指令,根据不同指令执行不同操作。对于 AFF 指令,表示开始接收文件,并使用 XMODEM 协议接收文件内容。

主要使用的模块是 serial 用于串口通信,以及 xmodem 用于 XMODEM 协议的实现。

注意事项:

  • 串口的端口号(port)和波特率(baudrate)需要根据实际情况进行配置。
  • 代码中默认使用 XMODEM 协议,可以根据需要选择其他协议。
  • 这段代码实现了基本的文件传输功能,你可以根据实际需求进行扩展和优化,例如添加错误处理、进度显示等。
  • 在实际应用中,需要确保发送端和接收端的协议、参数、文件路径等信息一致。
import serial
from xmodem import XMODEM
import os
 
def Send_File(filepath, port='COM8', baudrate=115200):
    bn = os.path.basename(filepath)
    filesize = os.stat(filepath).st_size
    strSendFileCMD = "AFF_" + str(filesize) + "_" + bn + "\n"
    # 打开串口
    ser = serial.Serial(port, baudrate)
     
    # 定义YMODEM发送函数
    def send_ymodem(filename):
        def getc(size, timeout=1):
            return ser.read(size)
        def putc(data, timeout=1):
            return ser.write(data)
        modem = XMODEM(getc, putc)
        with open(filename, "rb") as f:
            status = modem.send(f)
        return status
     
    # 发送指令
    ser.write(strSendFileCMD.encode())
    # 发送文件
    status = send_ymodem(filepath)
    if status:
        print(f"文件发送成功:{filepath}")
    else:
        print(f"文件发送失败:{filepath}")
    # 关闭串口
    ser.close()
 
 
def Recv_File(port='COM2', baudrate=115200):
    # 打开串口
    ser = serial.Serial(port, baudrate)
     
    # 定义YMODEM接收函数
    def recv_ymodem(filename):
        def getc(size, timeout=1):
            return ser.read(size) or None
        def putc(data, timeout=1):
            return ser.write(data)
        modem = XMODEM(getc, putc)
        with open(filename, "wb") as f:
            status = modem.recv(f)
        return status
     
    # 循环监听指令
    while True:
        # 接收指令
        print("等待接收指令")
        strCMD = ser.read_until().strip().decode()
        print(strCMD)
        cmdlist = strCMD.split("_")
        cmd = cmdlist[0]
        filesize = cmdlist[1]
        filename = cmdlist[2]
        if cmd == "AFF":
            # 收到指令后开始接收文件
            print("开始接收文件")
            # 接收文件并保存
            status = recv_ymodem(filename)
            if status:
                print(f"文件接收成功:{filename}")
            else:
                print(f"文件接收失败:{filename}")
            # 继续监听指令
            continue
        # 其他指令
        print(f"收到指令:{cmd}")
    # 关闭串口
    ser.close()
 
if __name__=="__main__":
    Send_File("D:/users.7z") #发送文件
    #Recv_File() #接收文件

PDF加密文件解密

uupython阅读(561)

这段代码使用了 PyPDF2 库来实现 PDF 文件的解密操作。用户可以输入要解密的 PDF 文件或包含 PDF 文件的文件夹路径,提供对应的密码,然后将解密后的 PDF 文件保存到指定的解密文件夹中。以下是代码的主要功能和步骤:

  1. 导入了必要的模块:os 用于文件操作,PyPDF2 用于 PDF 文件操作。
  2. 定义了 get_reader() 函数,用于获取 PDF 读取实例。如果文件是加密的,会要求提供密码进行解密。
  3. 定义了 decrypt_pdf() 函数,用于将加密的 PDF 文件解密,并生成无需密码的新 PDF 文件。
  4. 定义了 get_pdf_paths() 函数,用于获取文件夹中的所有 PDF 文件路径。
  5. 主程序部分:
  • 用户输入要解密的 PDF 文件或文件夹路径、密码以及解密后的文件夹路径。
  • 如果输入的是文件夹路径,则获取文件夹中的所有 PDF 文件路径,并逐个进行解密。
  • 如果输入的是单个 PDF 文件路径,则直接对该文件进行解密。
  • 解密后的 PDF 文件将保存到指定的解密文件夹中。

注意事项:

  • 在使用该代码之前,需要安装 PyPDF2 库,可以使用 pip install PyPDF2 命令进行安装。
  • 代码中使用了命令行交互来获取用户输入,可以根据需要修改为图形界面输入。

这段代码实现了基本的 PDF 文件解密功能,你可以根据需要进行扩展和优化,例如添加更多的错误处理、界面美化等。同时,请注意确保使用合法的方式使用该代码,遵循相关法律法规。

import os
from PyPDF2 import PdfWriter, PdfReader
 
def get_reader(filename, password):
    try:
        old_file = open(filename, 'rb')
    except Exception as err:
        print('文件打开失败!' + str(err))
        return None
 
    # 创建读实例
    pdf_reader = PdfReader(old_file, strict=False)
 
    # 解密操作
    if pdf_reader.is_encrypted:
        if password is None:
            print('%s文件被加密,需要密码!' % filename)
            return None
        else:
            if pdf_reader.decrypt(password) != 1:
                print('%s密码不正确!' % filename)
                return None
 
    if old_file in locals():
        old_file.close()
 
    return pdf_reader
 
def decrypt_pdf(filename, password, decrypted_folder):
    """
    将加密的文件进行解密,并生成一个无需密码的pdf文件
    :param filename: 原先加密的pdf文件
    :param password: 对应的密码
    :param decrypted_folder: 解密后放置文件的文件夹路径
    :return:
    """
    # 生成一个Reader和Writer
    pdf_reader = get_reader(filename, password)
    if pdf_reader is None:
        return
 
    pdf_writer = PdfWriter()
    for page in pdf_reader.pages:
        pdf_writer.add_page(page)
 
    # 获取解密后的文件名
    file_name, _ = os.path.splitext(filename)
 
    if decrypted_folder is not None:
        decrypted_filepath = os.path.join(decrypted_folder, os.path.basename(file_name) + ".pdf")
    else:
        print('生成文件失败,请给出解密后放置文件夹')
        return
 
    # 写入新文件
    with open(decrypted_filepath, 'wb') as decrypted_file:
        pdf_writer.write(decrypted_file)
        print('解密成功' + decrypted_filepath)
 
# 获取文件夹中的pdf文件路径
def get_pdf_paths(folder_path):
    pdf_paths = []
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.endswith(".pdf"):
                pdf_path = os.path.join(root, file)
                pdf_paths.append(pdf_path)
    return pdf_paths
 
 
pdf_path = input('请输入要解密的pdf或含pdf文件夹路径:')
# pdf_path = '文件夹或文件路径'
password = input('请输入密码:')
# password = '密码'
decrypted_folder = input('请输入解密文件夹:')
# decrypted_folder = '解密文件夹'
 
if os.path.isdir(pdf_path):
    pdf_paths = get_pdf_paths(pdf_path)
    for path in pdf_paths:
        decrypt_pdf(path, password, decrypted_folder)  # 修改此处,传递解密后文件存放的文件夹路径
else:
    _, extension = os.path.splitext(pdf_path)
    if extension.lower() == '.pdf':
        decrypt_pdf(pdf_path, password, decrypted_folder)
    else:
        print('文件非pdf')

[GUI]时间戳互转

uupython阅读(458)

这段代码实现了一个简单的时间戳和时间转换工具的图形界面应用程序。用户可以在界面中输入时间戳或时间,然后点击按钮执行相应的转换操作,将时间戳转换为时间,或将时间转换为时间戳。这个应用程序使用了 tkinter 进行图形界面的创建和操作,并通过线程实现了异步操作,避免界面的卡顿。

主要步骤如下:

  1. 导入必要的模块,包括 timeostkinterthreading
  2. 定义了三个函数 sjc()zsj()zsjc(),分别用于获取当前时间和时间戳、将时间戳转换为时间,以及将时间转换为时间戳。
  3. 定义了一个名为 thread_it 的函数,用于将函数打包成线程,以实现异步操作。
  4. 在主程序中,创建了一个 tkinter 窗口,并添加了三个按钮和两个输入框,用于用户输入。用户可以输入时间戳、时间,然后点击按钮执行转换操作。
  5. 使用 lambda 表达式将按钮与相应的函数关联,同时将函数包装成线程进行异步操作。
  6. 使用 window.mainloop() 启动 tkinter 窗口的主事件循环,使应用程序进入运行状态。

请注意,这段代码只是一个简单的时间戳和时间转换工具的示例,你可以根据需要进行扩展和优化,例如添加更多的错误处理、界面美化、功能增强等。

import time
import os
import tkinter as tk
import threading
import datetime
 
 
def sjc():
    t.delete(1.0, tk.END)
    i = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    print(i)
    t.insert('end', '当前时间:')
    t.insert(tk.INSERT, '\n')
    t.insert('end', i)
    t.insert(tk.INSERT, '\n')
    # 转换成时间数组
    timeArray = time.strptime(i, "%Y-%m-%d %H:%M:%S")
    # 转换成时间戳
    timestamp = int(time.mktime(timeArray))
    print(timestamp)
    t.insert('end', '当前时间戳:')
    t.insert('end', timestamp)
    t.insert(tk.INSERT, '\n')
 
    # utc5_beginTime = (int(timestamp/86400)+1)*86400+18000
    # utc5_endTime = utc5_beginTime+86400*(int(hdts)-1)+79200
    # print("----------utc-5")
    # print(utc5_beginTime)
    # print(utc5_endTime)
    # t.insert('end',"----------utc-5")
    # t.insert(tk.INSERT, '\n')
    # t.insert('end', utc5_beginTime)
    # t.insert(tk.INSERT, '\n')
    # t.insert('end', utc5_endTime)
    # t.insert(tk.INSERT, '\n')
    # utc2_beginTime = (int(timestamp/86400)+1)*86400-7200
    # utc2_endTime = utc2_beginTime+86400*(int(hdts)-1)+79200
    # print("----------utc+2")
    # print(utc2_beginTime)
    # print(utc2_endTime)
    # t.insert('end',"----------utc+2")
    # t.insert(tk.INSERT, '\n')
    # t.insert('end', utc2_beginTime)
    # t.insert(tk.INSERT, '\n')
    # t.insert('end', utc2_endTime)
    # t.insert(tk.INSERT, '\n')
    # utc8_beginTime = (int(timestamp/86400)+1)*86400-28800
    # utc8_endTime = utc2_beginTime+86400*(int(hdts)-1)+79200
    # print("----------utc+8")
    # print(utc8_beginTime)
    # print(utc8_endTime)
    # t.insert('end',"----------utc+8")
    # t.insert(tk.INSERT, '\n')
    # t.insert('end', utc8_beginTime)
    # t.insert(tk.INSERT, '\n')
    # t.insert('end', utc8_endTime)
    # t.insert(tk.INSERT, '\n')
def zsj():
    hdts = txt.get()
    if hdts == "输入时间戳":
        t.insert('end', '输入时间戳')
        t.insert(tk.INSERT, '\n')
        return
    t.insert('end', '时间戳:')
    t.insert('end', hdts)
    t.insert(tk.INSERT, '\n')
    # 将时间戳转换为datetime对象
    dt_object = datetime.datetime.fromtimestamp(int(hdts))
 
    # 格式化输出时间
    formatted_time = dt_object.strftime("%Y-%m-%d %H:%M:%S")
 
    print("转换后的时间:", formatted_time)
    t.insert('end', '时间:')
    t.insert('end', formatted_time)
    t.insert(tk.INSERT, '\n')
def zsjc():
    sj = txt1.get()
    if sj == None:
        t.insert('end', '输入时间')
        t.insert(tk.INSERT, '\n')
        print('输入时间')
        return
    t.insert('end', '时间:')
    t.insert('end', sj)
    t.insert(tk.INSERT, '\n')
    # 转换成时间数组
    timeArray = time.strptime(sj, "%Y-%m-%d %H:%M:%S")
    print(timeArray)
    # 转换成时间戳
    timestamp = int(time.mktime(timeArray))
    print(timestamp)
    t.insert('end', '时间戳:')
    t.insert('end', timestamp)
    t.insert(tk.INSERT, '\n')
 
def thread_it(func, *args):
    '''将函数打包进线程'''
    k = threading.Thread(target=func, args=args)
    k.setDaemon(True)
    k.start()
 
 
def get_servertime(self):
    """
    获取服务器当前时间
    :return:当前服务器时间戳
    """
    req_body = self.get_request_body('gmGetServerTime')
    rsp = self.send_http_request(req_body).json()
    print(rsp['data']['serverTime'])
    return rsp['data']['serverTime']
 
 
if __name__ == '__main__':
    window = tk.Tk()
    window.title('sjc')
    window.geometry("200x300")
    t = tk.Text(window, width='250', height=15, bg='LightBlue')
    t.pack(side='bottom')
    btn1 = tk.Button(window, text="查询时间", bg="orange", command=lambda: thread_it(sjc))
    btn1.place(x=110, y=5)
    btn2 = tk.Button(window, text="时间戳转时间", bg="orange", command=lambda: thread_it(zsj))
    btn2.place(x=110, y=35)
    btn3 = tk.Button(window, text="时间转时间戳", bg="orange", command=lambda: thread_it(zsjc))
    btn3.place(x=110, y=65)
    txt = tk.Entry(window, width=13)
    txt.place(x=10, y=35)
    txt.insert('end', 2)
    txt1 = tk.Entry(window, width=13)
    txt1.place(x=10, y=65)
    txt1.insert('end','')
    # wz1 = tk.Label(window, text='输入开服天数:')
    # wz1.place(x=0, y=0)
    window.mainloop()

[GUI]简易的浏览器

uupython阅读(422)

这段代码实现了一个基于 PyQt5 的简单浏览器应用程序。该浏览器具有基本的浏览器功能,包括地址栏、后退、前进、刷新、添加新标签页、关闭标签页、导航到主页等。此外,它还具有一些设置选项,如语言选择。

主要步骤如下:

  1. 创建一个 Browser 类,继承自 QMainWindow
  2. Browser 类的构造函数中,初始化界面元素,包括地址栏、标签页、导航栏、工具栏等。
  3. 创建 add_new_tab 方法,用于在标签页中打开新页面。该方法接受一个 QUrl 参数,用于指定页面的 URL。
  4. 创建 navigate_to_url 方法,用于从地址栏获取 URL 并在当前标签页中加载该 URL。
  5. 创建 go_backgo_forward 方法,分别用于后退和前进页面。
  6. 创建 reload_page 方法,用于刷新当前页面。
  7. 创建 update_bookmarks 方法,用于更新书签列表,以便在书签菜单中显示当前打开的标签页。
  8. 创建 navigate_to_bookmark 方法,用于导航到书签所指定的页面。
  9. 创建 show_settings 方法,用于显示设置对话框,其中可以选择浏览器的语言。
  10. 创建 set_language 方法,用于设置浏览器界面的语言。
  11. __main__ 部分,创建一个 QApplication 实例,设置应用程序名称。
  12. 创建 Browser 实例,显示主窗口,并启动应用程序的主循环。

请注意,这个代码示例是一个简单的浏览器应用程序的骨架,你可以在此基础上添加更多功能和细节,如历史记录、下载管理、密码管理、广告拦截等。

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import QKeySequence
 
class Browser(QMainWindow):
    def __init__(self):
        super().__init__()
 
        self.url_bar = QLineEdit()
        self.url_bar.returnPressed.connect(self.navigate_to_url)
 
        self.tabs = QTabWidget()
        self.tabs.setDocumentMode(True)
        self.tabs.currentChanged.connect(self.current_tab_changed)
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.close_current_tab)
 
        self.nav_bar = QToolBar()
        self.addToolBar(self.nav_bar)
 
         
 
        self.tabs.tabBarDoubleClicked.connect(self.tab_open_doubleclick)
        self.tabs.currentChanged.connect(self.current_tab_changed)
 
        self.add_new_tab(QUrl("https://www.baidu.com"), "首页")
 
        self.setCentralWidget(self.tabs)
 
        nav_bar = QToolBar()
        self.addToolBar(nav_bar)
 
        back_btn = QAction("后退", self)
        back_btn.triggered.connect(self.go_back)
        nav_bar.addAction(back_btn)
 
        forward_btn = QAction("前进", self)
        forward_btn.triggered.connect(self.go_forward)
        nav_bar.addAction(forward_btn)
 
        reload_btn = QAction("刷新", self)
        reload_btn.triggered.connect(self.reload_page)
        nav_bar.addAction(reload_btn)
 
        home_btn = QAction("首页", self)
        home_btn.triggered.connect(self.navigate_home)
        nav_bar.addAction(home_btn)
 
        self.url_bar = QLineEdit()
        self.url_bar.returnPressed.connect(self.navigate_to_url)
        self.nav_bar.addWidget(self.url_bar)
        nav_bar.addWidget(self.url_bar)
 
        nav_bar.addSeparator()
         
        self.status = QStatusBar()
        self.setStatusBar(self.status)
 
        # 添加书签管理
        self.bookmarks = []
        self.bookmarks_menu = self.menuBar().addMenu("书签")
        self.update_bookmarks()
 
        # 添加历史记录
        self.history = []
 
        # 添加下载管理
        self.downloads = []
 
        # 添加密码管理
        self.passwords = {}
 
        # 添加阅读模式
        self.reading_mode = False
 
        # 添加多语言支持
        self.language = "中文"
 
        # 添加设置选项
        settings_action = QAction("设置", self)
        settings_action.triggered.connect(self.show_settings)
        self.menuBar().addAction(settings_action)
 
        # 添加隐私模式
        self.private_mode = False
 
        # 添加广告拦截
        self.ad_block = False
 
        # 添加搜索引擎切换
        self.search_engine = "Google"
 
        # 添加快捷键
        new_tab_shortcut = QShortcut(QKeySequence("Ctrl+T"), self)
        new_tab_shortcut.activated.connect(self.add_new_tab)
 
        # 美化界面
        self.setStyleSheet("""
            QMainWindow {
                background-color: #F0F0F0;
            }
            QTabWidget::pane {
                border: 1px solid #C0C0C0;
                background: white;
            }
            QTabBar::tab {
                padding: 8px;
                background-color: #E0E0E0;
                border: 1px solid #C0C0C0;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
            }
            QTabBar::tab:selected {
                background-color: #FFD700;
            }
            QToolBar {
                background-color: #E0E0E0;
            }
            QLineEdit {
                background-color: #FFFFFF;
            }
            QStatusBar {
                background-color: #E0E0E0;
            }
        """)
    def tab_open_doubleclick(self, i):
 
        if i == -1:
            self.add_new_tab()
 
    def update_url(self, q, browser=None):
        if browser is None:
            browser = self.tabs.currentWidget()
        if browser != self.tabs.currentWidget():
            return
 
        self.url_bar.setText(q.toString())
 
        self.url_bar.setText(q.toString())
 
    def current_tab_changed(self, i):
        qurl = self.tabs.currentWidget().url()
        self.update_url(qurl, self.tabs.currentWidget())
 
    def add_new_tab(self, qurl=None, label="新标签页"):
        if qurl is None:
            qurl = QUrl("https://www.baidu.com")
 
        browser = QWebEngineView()
        browser.setUrl(qurl)
        i = self.tabs.addTab(browser, label)
 
        self.tabs.setCurrentIndex(i)
 
        browser.urlChanged.connect(lambda qurl, browser=browser: self.update_url(qurl, browser))
        browser.loadFinished.connect(lambda _, i=i, browser=browser: self.tabs.setTabText(i, browser.page().title()))
 
    def update_url(self, q, browser=None):
        if browser != self.tabs.currentWidget():
            return
 
        self.url_bar.setText(q.toString())
 
    def tab_open_doubleclick(self, i):
        if i == -1:
            self.add_new_tab()
 
    def current_tab_changed(self, i):
        qurl = self.tabs.currentWidget().url()
        self.update_url(qurl, self.tabs.currentWidget())
 
    def close_current_tab(self, i):
        if self.tabs.count() < 2:
            return
 
        self.tabs.removeTab(i)
     
    def navigate_home(self):
        self.tabs.currentWidget().setUrl(QUrl("https://www.baidu.com"))
 
    def navigate_to_url(self):
        q = QUrl(self.url_bar.text())
 
        if q.scheme() == "":
            q.setScheme("http")
 
        self.tabs.currentWidget().setUrl(q)
 
    def go_back(self):
        self.tabs.currentWidget().back()
 
    def go_forward(self):
        self.tabs.currentWidget().forward()
 
    def reload_page(self):
        self.tabs.currentWidget().reload()
 
    def update_bookmarks(self):
        for bookmark in self.bookmarks:
            self.bookmarks_menu.removeAction(bookmark)
 
        self.bookmarks.clear()
 
        for i in range(self.tabs.count()):
            qurl = self.tabs.widget(i).url()
            title = self.tabs.tabText(i)
            action = QAction(title, self)
            action.setData(qurl)
            action.triggered.connect(self.navigate_to_bookmark)
            self.bookmarks.append(action)
            self.bookmarks_menu.addAction(action)
 
    def navigate_to_bookmark(self):
        action = self.sender()
        if action:
            qurl = action.data()
            self.tabs.currentWidget().setUrl(qurl)
 
    def show_settings(self):
        settings_dialog = QDialog(self)
        settings_dialog.setWindowTitle("Settings")
        settings_dialog.setMinimumSize(300, 200)
 
        layout = QVBoxLayout()
 
        # 添加设置选项
        language_label = QLabel("Language:")
        language_combo = QComboBox()
        language_combo.addItem("English")
        language_combo.addItem("Chinese")
        language_combo.currentIndexChanged.connect(self.set_language)
        language_combo.setCurrentText(self.language)
 
        layout.addWidget(language_label)
        layout.addWidget(language_combo)
 
        settings_dialog.setLayout(layout)
        settings_dialog.exec_()
 
    def set_language(self, index):
        language = "English" if index == 0 else "Chinese"
        if language != self.language:
            self.language = language
            # TODO: 设置浏览器界面语言
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setApplicationName("强大浏览器")
 
    window = Browser()
    window.show()
 
    sys.exit(app.exec_())