二、项目介绍
1、本项目使用Vs2019+Qt库+Python库来开发一个百度图片播放下载器(支持Gif)。
Qt播放Gif图片参考文章:
本文主要描述Qt调用Python库来获取百度图片。
2、本项目完成的功能如下:
(1)、支持通过搜索关键词及图片张数来获取百度图片进行循环播放及保存原文件。
(2)、支持设置播放图片窗口大小。
(3)、支持Gif图片。
3、效果展示:
三、项目开始
1、准备工作
搭建一份Qt播放Gif图片的工程环境。然后获取python对应平台的头文件及库:
注意:安装的python的Lib目录下有很多文件,整个目录比较大,不便于我们程序发布。我们只需要保留程序所需要的文件即可,找到本程序的安装路径即可看到基本所需的Lib文件。
2、写python程序来爬取百度图片
这里参考文章:
然后进行简单修改,提供一个getImgUrls(key,num)接口给C++调用获取当前关键词多少张urls,提供一个getImg()接口给C++调用循环获取爬取到的图片,该接口返回给C++的是图片的base64编码。
修改后的内容如下:
baidu_photo_spider.py:
"""根据搜索词下载百度图片"""
import os
import re
import base64
import time
from typing import List, Tuple
from urllib.parse import quote
import requests
from conf import *
# 关键词, 改为你想输入的词即可, 相当于在百度图片里搜索一样
keyword = '治愈系柴犬'
# 最大下载数量
max_download_images = 100
all_pic_urls = []
count = 0
def get_page_urls(page_url: str, headers: dict) -> Tuple[List[str], str]:
"""获取当前翻页的所有图片的链接
Args:
page_url: 当前翻页的链接
headers: 请求表头
Returns:
当前翻页下的所有图片的链接, 当前翻页的下一翻页的链接
"""
if not page_url:
return [], ''
try:
html = requests.get(page_url, headers=headers)
html.encoding = 'utf-8'
html = html.text
except IOError as e:
print(e)
return [], ''
pic_urls = re.findall('"objURL":"(.*?)",', html, re.S)
next_page_url = re.findall(re.compile(r'<a href="(.*)" class="n">下一页</a>'), html, flags=0)
next_page_url = 'http://image.baidu.com' + next_page_url[0] if next_page_url else ''
return pic_urls, next_page_url
def getImgUrls(key,num):
global keyword
global max_download_images
global all_pic_urls
keyword = key
max_download_images = num
url_init = url_init_first + quote(keyword, safe='/')
page_urls, next_page_url = get_page_urls(url_init, headers)
all_pic_urls.extend(page_urls)
page_count = 0
while 1:
page_urls, next_page_url = get_page_urls(next_page_url, headers)
page_count += 1
print('正在获取第%s个翻页的所有图片链接' % str(page_count))
if next_page_url == '' and page_urls == []:
print('已到最后一页,共计%s个翻页' % page_count)
return
all_pic_urls.extend(page_urls)
if len(all_pic_urls) >= max_download_images:
print('已达到设置的最大下载数量%s' % max_download_images)
return
def down_pic(i,pic_url):
try:
pic = requests.get(pic_url, timeout=15)
image_output_path = './images/' + str(i + 1) + '.jpg'
with open(image_output_path, 'wb') as f:
f.write(pic.content)
print('成功下载第%s张图片: %s' % (str(i + 1), str(pic_url)))
except IOError as e:
print('下载第%s张图片时失败: %s' % (str(i + 1), str(pic_url)))
print(e)
return
def getImg():
global all_pic_urls
global count
global max_download_images
none='none'
try:
pic = requests.get(list(set(all_pic_urls))[count], timeout=15)
imgbase64 = base64.b64encode(pic.content).decode()
#print(count)
count += 1
if count == max_download_images:
count = 0
return imgbase64,none
except IOError as e:
print(e)
return none,none
if __name__ == '__main__':
if not os.path.exists('./images'):
os.mkdir('./images')
getImgUrls('猫', 2)
#下载图片
for i, pic_url in enumerate(list(set(all_pic_urls))[:max_download_images]):
down_pic(i, pic_url)
#循环读取列表中的图片
while 1:
print(getImg())
time.sleep(5)
3、封装C++调用python库的接口
直接上代码:
#include "PythonDevApi.h"
#include "Python.h"
void initPython()
{
Py_Initialize();//加载Python解释器
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('Dlls/')");
}
void unInitPython()
{
Py_Finalize();//卸载Python解释器
}
static PyObject* pBaiduModule = NULL;
int pyGetBaiduImgInit(const char* key, int num)
{
pBaiduModule = PyImport_ImportModule("baidu_photo_spider");//Python py文件名
PyImport_ReloadModule(pBaiduModule);
if (pBaiduModule == nullptr)
return -1;
PyObject* pFunc = NULL;
PyObject* pArgs = NULL;
PyObject* pResult = NULL;
pFunc = PyObject_GetAttrString(pBaiduModule, "getImgUrls");//py文件内函数名
//执行函数
pArgs = Py_BuildValue("si", key, num);
pResult = PyObject_CallObject(pFunc, pArgs);
return 0;
}
int pyGetBaiduImgWork(char* img, int imgSize)
{
//执行函数
PyObject* pBaiduFunc = PyObject_GetAttrString(pBaiduModule, "getImg");//py文件内函数名
if (pBaiduFunc == NULL)
return -1;
PyObject* pArgs = NULL;
PyObject* pResult = PyObject_CallObject(pBaiduFunc, pArgs);
if (pResult == NULL)
return -1;
char* imgbase64, * none;
int ret = PyArg_ParseTuple(pResult, "s|s", &imgbase64, &none);
if (ret == 1)
{
if (memcmp(imgbase64, none, strlen(none)) != 0 && img != NULL && strlen(imgbase64) < imgSize)
{
memcpy(img, imgbase64, strlen(imgbase64));
return 0;
}
}
else
{
printf("ret:%d\n", ret);
}
return -1;
}
int pyGetBaiduImgUninit()
{
return 0;
}
4、调用封装好的python接口来进行图片获取与保存文件:
void GuiQtDevThread::run()
{
int count = 0;
pyGetBaiduImgInit(m_key.toStdString().c_str(), m_num);
while (1)
{
//程序退出时终止线程
if (m_gui->getGetImgThreadState() == THREAD_STATE_E_PrepareStop)
{
m_gui->setGetImgThreadState(THREAD_STATE_E_Stop);
pyGetBaiduImgUninit();
return ;
}
count++;
if (count == 50)
{
count = 0;
//只能使用emit方式操作控件,直接操作控件会死机,这里会等待槽函数结束才继续运行(Qt::BlockingQueuedConnection)
memset(m_img, 0, m_img_size);
int ret = pyGetBaiduImgWork(m_img, m_img_size);
if (ret != 0)
{
QThread::msleep(100);
continue;
}
QByteArray decodeimg = QByteArray::fromBase64(m_img);
CQVString filenamet;
filenamet.Append("%s%d", "imgtmp", _getpid());
QFile file(filenamet.GetBuffer());
file.open(QIODevice::WriteOnly);
file.write(decodeimg);
file.close();
QThread::msleep(50);
QString saveFileName = m_gui->getSaveDir();
if (saveFileName.length() > 1)
{
bool dirExist = true;
QDir dir;
if (!dir.exists(saveFileName))
{
dirExist = dir.mkdir(saveFileName);
}
if (dirExist)
{
CQVString filenamet;
filenamet.Append("%s%d", "imgtmp", _getpid());
QImageReader reader(filenamet.GetBuffer());
reader.setDecideFormatFromContent(true);
int nCount = reader.imageCount();
if (nCount > 0 && reader.canRead())
{
QString fileFomate(QImageReader::imageFormat(filenamet.GetBuffer()));
saveFileName += "/";
QDateTime current_date_time = QDateTime::currentDateTime();
QString current_date = current_date_time.toString("yyyy-MM-dd-hh-mm-ss");
saveFileName += current_date;
saveFileName += ".";
saveFileName += fileFomate;
QFile saveFile(saveFileName);
saveFile.open(QIODevice::WriteOnly);
saveFile.write(decodeimg);
saveFile.close();
QThread::msleep(50);
}
}
}
m_gui->setFileChange();
}
QThread::msleep(100);
}
}
5、项目生成后事件需要把python的dll拷贝到exe目录下,并且把python的DLLs和Lib目录以及我们写的py程序拷贝到主工程和exe目录下。这样才能保证vs2019和双击exe正常运行程序,如图: