Featured image of post 『Notes』Python Programming diary

『Notes』Python Programming diary

记录 Python 项目开发中遇到的罕见 BUG

PackagesNotFoundError: The following packages are missing from the target environment

错误现象: 使用 Pycharm 手动添加新环境,或使用 conda create -n [env-name] [python=3.9]指令遇到如下报错:

1
PackagesNotFoundError: The following packages are missing from the target environment - python=3.9

事件回溯: Pycharm 手动添加源,本质上相当于执行了 conda 脚手架指令,简单的可视化交互其本质上也只是使用了 -p 参数指定 env 存储路径以及 Python 版本,所以根因出在脚手架身上。

在报错时,我设置了 conda config:channels 也就是所谓的 「源」为清华源。若我开启本机代理,则环境创建会时抛出另一个访问清华园的 404 错误;若我不开启本机代理,则会抛出本 Tag 异常。我在开关本机代理的时候,也排列组合设置了 Pycharm 中的代理设置(无代理/手动代理)。最终依靠移除 conda 关于源的环境变量解决问题。

参考方案: 执行如下代码移除 conda 源(使用默认源):

1
conda config --remove-key channels

SSLError(SSLEOFError(8, ‘EOF occurred in violation of protocol (_ssl.c:1129)’))

错误现象: 使用 pip 脚手架下载任意包抛出。先经过 N 论超时警告,最后抛出 SSLErrorHTTPSConnectionPool 异常。错误日志如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> pip install requests
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLEOFError(8, '
EOF occurred in violation of protocol (_ssl.c:1129)'))': /simple/requests/
WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLEOFError(8, '
EOF occurred in violation of protocol (_ssl.c:1129)'))': /simple/requests/
WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLEOFError(8, '
EOF occurred in violation of protocol (_ssl.c:1129)'))': /simple/requests/
WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLEOFError(8, '
EOF occurred in violation of protocol (_ssl.c:1129)'))': /simple/requests/
WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLEOFError(8, '
EOF occurred in violation of protocol (_ssl.c:1129)'))': /simple/requests/
Could not fetch URL https://pypi.tuna.tsinghua.edu.cn/simple/requests/: There was a problem confirming the ssl certificate: HTTPSConnectionPo
ol(host='pypi.tuna.tsinghua.edu.cn', port=443): Max retries exceeded with url: /simple/requests/ (Caused by SSLError(SSLEOFError(8, 'EOF occu
rred in violation of protocol (_ssl.c:1129)'))) - skipping
ERROR: Could not find a version that satisfies the requirement requests (from versions: none)
ERROR: No matching distribution found for requests

事件回溯:pip 配置了全局 清华源(如下所示),运行指令时使用由 v2rayN 发起的系统代理,这是一个「墙内人」几乎不会遇到的问题……

1
2
>>> pip config list
global.index-url='https://pypi.tuna.tsinghua.edu.cn/simple'

参考方案: 根因在于我们访问的镜像接口识别并屏蔽了代理流量(不可信的源),也即核心解决思路为「不使用代理」访问镜像源。

  1. 推荐」修改 代理客户端 路由规则(以 v2rayN 为例其他代理软件同理)

    这是个一劳永逸的做法,代理软件将不再接管访问此域名的流量。

    启动代理客户端,依次打开 设置参数设置系统代理设置,在「例外」中填入镜像域名,以清华源 pip 镜像域名为例,填入 pypi.tuna.tsinghua.edu.cn,如下图所示:

  1. 「推荐」 开启 TUN 模式代理(以 Clash for Windows v0.19.19 为例)

    在面板中启动 TUN Mode,此时 Clash 可以通过端点间隧道(虚拟网卡)代理当前局域网的全局流量,此时无论是浏览器流量还是各种电脑软件的流量都会通过 Clash 虚拟网卡访问互联网。

  2. 在执行 pip 指令时清除系统代理,也即关闭代理软件的系统代理功能

    比较繁琐,需要执行前关闭代理,(如果你当前的环境依赖代理)执行后再重新开启代理。

文件编码

错误现象: 此问题难以使用某一类异常描述,因为运行时不会报错,但运行结果和预想的不太一样。例如,网络上抓取语料,需要获取 user-nickname,不免会遇上使用特殊符号命名的玩家。这些符号直接在网页上渲染,或是直接在控制台输出,都是「所见即所得」,但在「写入文件」这一步骤时,出现了不可控的「乱码」问题。

事件回溯: 以下方 Python 代码为例,我们定义一个包含「特殊符号」集合的变量,并以 UTF-8 编码形式将其写入到 CSV 文件当中。

1
2
3
4
5
6
7
import csv

a = ["🌪", "嬡芣释掱"]

with open("test.csv", "w", encoding="utf-8", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(a)

打开 test.csv,查看写入的数据:

image-20211025162216767

当时的第一反应是 encoding 参数的选择不恰当,而事实证明的确如此。但我却在编码类型的选择上陷入大坑。最后试出了一种能够正常显示写入数据的编码格式 utf-16

修改参数 encoding="utf-16" 后再次打开文件,查看写入数据:

image-20211025162348681

可以看到「特殊符号」的**字形(glyph)**已被正常渲染,但似乎分隔符的识别出现了问题,同一行数据被识别为同一个单元的内容。

于是我在查了有关资料后,找到了另一种编码格式 utf-8-sig

修改参数 encoding="utf-8-sig" 后再次打开文件,查看写入数据:

image-20211025162553256

蚌埠住了,这次分隔符正常识别,火星文正常显示,但表情包符号却乱码了。

参考方案: 使用 utf-8-sigutf-16 编码尝试应对字形渲染问题。

但值得一提的是,无论是 utf-8utf-8-sig 还是 utf-16 ,只要不是默认的 gbk 都不会引发读写错误。尽管在 GUI 软件上出现了字形渲染的问题,但将同样的数据读回 Python 程序后,其仍然是一个正常显示的可以操作的 str 对象。

字符编码

最后更新时间:2022-01-28

错误现象:「看着一摸一样」的两句字符串,在 == 条件判别下返回 Flase

事件回溯: 这里要记录一个有趣的背景故事。这是作者在玩 hCaptcha challenge 时遇上的趣事。

如果你接触过爬虫,你一定了解这玩意(如下图所示),给出一个需求标签,点选九宫格中符合要求的图片。当下有很多流行的人机挑战, hCaptcha challenge 算是其中 T0 级别的模块,能够阻挡近乎九成的爬虫流量,新手遇上这种视觉验证类的 CAPTCHA ,基本上不会有太多想法。

image-20220128230029379

但我们环顾这个技术行业,hCaptcha challenge 显然算不上最“变态”的或者说最困难的人机挑战。最相关的好比 Google 的 reCaptcha v2 ,除了简单的图像分类外,还创新性的使用了图像切割的形式抽象地表述实体,让简单的「分类问题」变成了难度更高的「目标识别」问题(如下图所示),从编码者角度来看,无论是自动化模块的编写还是视觉模块的处理都是不小的挑战。

Snipaste_2022-01-29_00-00-03.png

当然,如果你同时熟悉 Undetected Automation 以及 Darknet Image ClassificationObject Detection,我相信这对你来说不是什么难事。但你要相信,当下的人机挑战的病态程度已经远超你的想象,篇幅有限此处不多展开,后买咱再开个坑详细说说「人机挑战的那些事」。

回到正题,此处的 字符编码 问题出自于 hCaptcha challenge 。我们通过网页元素截取 label 也即我们需要 「点击」的图像标签名,送入模型比对后得出相应的结果。此处截取的 label 是具有 locale 特性的,如果你以 zh-CN 请求形式打开,标签名会显示中文。坑就在这了,作者获取到的某些 label不是标准的中文格式编码,什么意思呢,你可以用你喜欢的编程语言去比对如下两个字符串,看它们是否“相等”。

1
2
# Python 
print("ー条船" == "一条船")

结果显然是不相等的,在该篇博客中,由于页面字体兼容此种字符编码的渲染,所以你很明显能看到两个“一”有所不同,但如果在你的 IDE 中,设置了等宽编程字体,或者 Source code pro 之类的流行字体,这对字符串从肉眼看是完全一样的。

不知道这是不是 hCaptcha 开发团队的有意为之,至少我觉得这是从“反爬虫”角度来说非常高明的一招。想撩到来犯爬虫,先潦倒它的开发者。同样地,其他语种也会有这样的问题,如英文 bіcycle(自行车):

1
2
#Python
print("bіcycle" == "bicycle")

好的,这下连本博客的默认字体引擎也无法渲染了,肉眼可见的“完全一致”,但如果你将它们复制进你的 IDE ,执行结果显然是 False

如果你也感叹这一招十分高明,大可在网上检索一下其中的“玄机”。

如果你也在处理 hCaptcha challenge,和我使用同样的思路获取 label,并且没有发现这个字符编码问题,你会陷入窘况。显然,我们训练的模型的标注 label 都是标准的国际统一的字符编码,在这种特殊的情况下,一个单词,因为某个字母的编码不同,经过解释器后,在模型看来,这是两个完全不一样的“实体目标”。

参考方案: 手动编织 dict mapping ,将异常编码做一层映射再导向模型。

这是个有效的,但也是最笨的方法。当然,根据本人的实验结论来看,使用 locale=zh-CN 时,这项阻碍是最小的(大部分异常都能看出差别)。如果你使用 en-US 本地化,任何一个英文字母都有可能出现异常编码(且肉眼难以察觉),工程难度非常大。

此时不应有 &

错误现象: 命令提示符抛出提示「此时不应有 &」后秒退。在 Pycharm 中启动的 CMD 终端秒退。

参考方案: Win + R ,键入 regedit 打开注册表,在如下路径找到 Autorun,将其数据数值删去。

1
HKEY_CURRENT_USER\Software\Microsoft\Command Processor
image-20220524043332047
You will to enjoy grander sight / By climing to a greater height.