Qt调用python脚本识别验证码

时间:2022-09-24 08:54:39

Qt调用python脚本识别验证码

一些懒人经常会想让很多工作都由机器处理,实现自动化。而一些涉及验证码的就要费些力气了。

python在许多领域都是让人省时省力的神兵利器,任何程序员都可以加以利用获取裨益,因为python的库实在太好太全太方便了。

在验证码识别上,python下也有一个pytesseract。只所以说也,是因为tesseract库有很多版本,比如java下的,比如c++下的(但是c++直接用起来来就没那么简单直接了,可以搜相关文章,一大段一大段的,安装库也有些费事)。此处不讨论pytesseract的识别正确率和训练改进,因为本人尚未研究到那一地步。此处只记录如何在Qt中调用python脚本。

代码

python脚本

python识别验证码的代码,输入为验证码图片的路径:

identCode.py

# coding:utf-8
import sys
print(sys.path)
import pytesseract

from PIL import Image

def getCode(path):
    image = Image.open(path)
    vcode = pytesseract.image_to_string(image)
    return vcode


if __name__=="__main__":
    if len(sys.argv) < 2:
        print("give pic path please\n")
        sys.exit(0)

    print(getCode(sys.argv[1]))

在命令行中执行 ”identCode.py recg.png“即可显示出所识别的验证码结果。


c++调用脚本获取结果代码

identifycode.h

#ifndef IDENTIFYCODE_H
#define IDENTIFYCODE_H

#include<string>
#include<iostream>
#include<QProcess>

using std::string;

class IdentifyCodeSh 
{
public:
    IdentifyCodeSh(){};
    virtual ~IdentifyCodeSh(){};

    virtual string recg(std::string path);
};


#endif // IDENTIFYCODE_H

identifycode.cpp

#include "identifycode.h"

using namespace std;


std::string IdentifyCodeSh::recg(std::string path)
{
    string result;
    QProcess proc;
    string trim="\r\n\t\v\d";
    //启动进程
    proc.start(QString("python identCode.py %1 ")
        .arg(QString::fromStdString(path)).toLocal8Bit());
    //等待进程可读
    proc.waitForReadyRead();
    if(proc.isReadable()){
        result=proc.readAll().data();
        std::cout<<"str read is:"<<result.c_str()<<std::endl;
        //去掉头尾多余的空格、换行符 
        result=result.substr(result.find_first_not_of(trim),
            result.find_last_not_of(trim)+1);
        cout<<" str trimmed is "<<result.c_str()<<endl;
    }else{
        cout<<"not readable"<<endl;
    }
    //等待进程执行完毕
    proc.waitForFinished();
    return result;
}

这里用到了QProcess类,可以执行一个系统命令,有点类似于c语言中的system(char* cmd)命令。但是QProcess封装得更好,可以读取到命令的输出,也可以向其中输入指令。如果将功能再封装,完全就可以实现一个自动交互式的expect(或者pexpect)模块功能了。当然,性能可能不是那么好:),具体的expect的底层源码有空也可以研究研究。

这样,基本上python中的所有功能都可以在c++项目中借用到了。其他python模块的功能借用如法炮制即可。:)。

问题

上面这种解决方案如果人品好的话,是可行的。人品好指的是你的Python脚本版本正好就是子进程执行时所对应的Python版本。
Qt中自带了一个版本的Python,比如我的Qt5.3带了个Python2.7。而我的脚本中用的是Python3.5。可想而知,会出现一些什么问题。报错如下:

Traceback (most recent call last): File "F:/SoftwareDevelopByMyself/Qt/StockAi/python/identCode.py", line 10, in <module> import pytesseract File "C:\Python35\Lib\site-packages/pytesseract/__init__.py", line 4, in <module> from pytesseract.pytesseract import image_to_string File "C:\Python35\Lib\site-packages/pytesseract/pytesseract.py", line 65, in <module> from PIL import Image File "C:\Python35\Lib\site-packages/PIL/Image.py", line 67, in <module> from PIL import _imaging as core ImportError: cannot import name _imaging

上面这个错误发生的原因,是QProcess中调用时默认就用了Qt中的Python2.7。而直接指定python3.5的可执行文件解析脚本又报一个Py_initialize错误。未能了解这个问题的深层原因。留待以后进步了再回头寻找答案。:)
Qt调用python脚本识别验证码
“Fatal Python error:Py_Initialize: unable to load the file system codec”
知道问题原因的大神麻烦指点下。:)

于是本人尝试了另一种方法,不用qt,用纯c++来实现python脚本的调用。而纯C++是可行的。代码如下:

#include<iostream>
#include<cstdlib>
#include<windows.h>

using namespace std;

int main(void){

    FILE* fp=popen("C:/Python35/python.exe F:/SoftwareDevelopByMyself/Qt/StockAi/python/identCode.py d:/recg.bmp","r");
    char buf[256];
    while (fgets(buf, sizeof(buf), fp) != 0) {
        cout<<buf<<endl;
    }
    pclose(fp);

    printf("done");
    getchar();
    return 1;
}

Qt调用python脚本识别验证码