Qt+Python开发百度图片下载器
一、资源下载地址
//www.aliyundrive.com/s/jBU2wBS8poH
本项目路径:项目->收费->百度图片下载器(可试用5分钟)
安装包直接下载地址://139.9.165.1/media/BaiduPicDown.exe
二、项目介绍
1、本项目使用Vs2019+Qt库+Python库来开发一个百度图片播放下载器(支持Gif)。
Qt播放Gif图片参考文章:
//www.cnblogs.com/liangqin/p/15161809.html
本文主要描述Qt调用Python库来获取百度图片。
2、本项目完成的功能如下:
(1)、支持通过搜索关键词及图片张数来获取百度图片进行循环播放及保存原文件。
(2)、支持设置播放图片窗口大小。
(3)、支持Gif图片。
3、效果展示:
三、项目开始
1、准备工作
搭建一份Qt播放Gif图片的工程环境。然后获取python对应平台的头文件及库:
注意:安装的python的Lib目录下有很多文件,整个目录比较大,不便于我们程序发布。我们只需要保留程序所需要的文件即可,找到本程序的安装路径即可看到基本所需的Lib文件。
2、写python程序来爬取百度图片
这里参考文章:
//blog.csdn.net/xiligey1/article/details/73321152
然后进行简单修改,提供一个getImgUrls(key,num)接口给C++调用获取当前关键词多少张urls,提供一个getImg()接口给C++调用循环获取爬取到的图片,该接口返回给C++的是图片的base64编码。
修改后的内容如下:
baidu_photo_spider.py:
1 """根据搜索词下载百度图片""" 2 import os 3 import re 4 import base64 5 import time 6 from typing import List, Tuple 7 from urllib.parse import quote 8 9 import requests 10 11 from conf import * 12 13 # 关键词, 改为你想输入的词即可, 相当于在百度图片里搜索一样 14 keyword = '治愈系柴犬' 15 16 # 最大下载数量 17 max_download_images = 100 18 19 all_pic_urls = [] 20 count = 0 21 22 def get_page_urls(page_url: str, headers: dict) -> Tuple[List[str], str]: 23 """获取当前翻页的所有图片的链接 24 Args: 25 page_url: 当前翻页的链接 26 headers: 请求表头 27 Returns: 28 当前翻页下的所有图片的链接, 当前翻页的下一翻页的链接 29 """ 30 if not page_url: 31 return [], '' 32 try: 33 html = requests.get(page_url, headers=headers) 34 html.encoding = 'utf-8' 35 html = html.text 36 except IOError as e: 37 print(e) 38 return [], '' 39 pic_urls = re.findall('"objURL":"(.*?)",', html, re.S) 40 next_page_url = re.findall(re.compile(r'<a href="(.*)" class="n">下一页</a>'), html, flags=0) 41 next_page_url = '//image.baidu.com' + next_page_url[0] if next_page_url else '' 42 return pic_urls, next_page_url 43 44 def getImgUrls(key,num): 45 global keyword 46 global max_download_images 47 global all_pic_urls 48 keyword = key 49 max_download_images = num 50 url_init = url_init_first + quote(keyword, safe='/') 51 page_urls, next_page_url = get_page_urls(url_init, headers) 52 all_pic_urls.extend(page_urls) 53 page_count = 0 54 while 1: 55 page_urls, next_page_url = get_page_urls(next_page_url, headers) 56 page_count += 1 57 print('正在获取第%s个翻页的所有图片链接' % str(page_count)) 58 if next_page_url == '' and page_urls == []: 59 print('已到最后一页,共计%s个翻页' % page_count) 60 return 61 all_pic_urls.extend(page_urls) 62 if len(all_pic_urls) >= max_download_images: 63 print('已达到设置的最大下载数量%s' % max_download_images) 64 return 65 66 def down_pic(i,pic_url): 67 try: 68 pic = requests.get(pic_url, timeout=15) 69 image_output_path = './images/' + str(i + 1) + '.jpg' 70 with open(image_output_path, 'wb') as f: 71 f.write(pic.content) 72 print('成功下载第%s张图片: %s' % (str(i + 1), str(pic_url))) 73 except IOError as e: 74 print('下载第%s张图片时失败: %s' % (str(i + 1), str(pic_url))) 75 print(e) 76 return 77 78 def getImg(): 79 global all_pic_urls 80 global count 81 global max_download_images 82 none='none' 83 try: 84 pic = requests.get(list(set(all_pic_urls))[count], timeout=15) 85 imgbase64 = base64.b64encode(pic.content).decode() 86 #print(count) 87 count += 1 88 if count == max_download_images: 89 count = 0 90 return imgbase64,none 91 except IOError as e: 92 print(e) 93 return none,none 94 95 if __name__ == '__main__': 96 if not os.path.exists('./images'): 97 os.mkdir('./images') 98 99 getImgUrls('猫', 2) 100 101 #下载图片 102 for i, pic_url in enumerate(list(set(all_pic_urls))[:max_download_images]): 103 down_pic(i, pic_url) 104 105 #循环读取列表中的图片 106 while 1: 107 print(getImg()) 108 time.sleep(5)
3、封装C++调用python库的接口
直接上代码:
1 #include "PythonDevApi.h" 2 #include "Python.h" 3 4 void initPython() 5 { 6 Py_Initialize();//加载Python解释器 7 PyRun_SimpleString("import sys"); 8 PyRun_SimpleString("sys.path.append('Dlls/')"); 9 } 10 11 void unInitPython() 12 { 13 Py_Finalize();//卸载Python解释器 14 } 15 16 static PyObject* pBaiduModule = NULL; 17 18 int pyGetBaiduImgInit(const char* key, int num) 19 { 20 pBaiduModule = PyImport_ImportModule("baidu_photo_spider");//Python py文件名 21 PyImport_ReloadModule(pBaiduModule); 22 if (pBaiduModule == nullptr) 23 return -1; 24 25 PyObject* pFunc = NULL; 26 PyObject* pArgs = NULL; 27 PyObject* pResult = NULL; 28 pFunc = PyObject_GetAttrString(pBaiduModule, "getImgUrls");//py文件内函数名 29 //执行函数 30 pArgs = Py_BuildValue("si", key, num); 31 pResult = PyObject_CallObject(pFunc, pArgs); 32 33 return 0; 34 } 35 36 int pyGetBaiduImgWork(char* img, int imgSize) 37 { 38 //执行函数 39 PyObject* pBaiduFunc = PyObject_GetAttrString(pBaiduModule, "getImg");//py文件内函数名 40 if (pBaiduFunc == NULL) 41 return -1; 42 43 PyObject* pArgs = NULL; 44 PyObject* pResult = PyObject_CallObject(pBaiduFunc, pArgs); 45 if (pResult == NULL) 46 return -1; 47 48 char* imgbase64, * none; 49 int ret = PyArg_ParseTuple(pResult, "s|s", &imgbase64, &none); 50 if (ret == 1) 51 { 52 if (memcmp(imgbase64, none, strlen(none)) != 0 && img != NULL && strlen(imgbase64) < imgSize) 53 { 54 memcpy(img, imgbase64, strlen(imgbase64)); 55 return 0; 56 } 57 } 58 else 59 { 60 printf("ret:%d\n", ret); 61 } 62 63 return -1; 64 } 65 66 int pyGetBaiduImgUninit() 67 { 68 return 0; 69 }
4、调用封装好的python接口来进行图片获取与保存文件:
1 void GuiQtDevThread::run() 2 { 3 int count = 0; 4 5 pyGetBaiduImgInit(m_key.toStdString().c_str(), m_num); 6 7 while (1) 8 { 9 //程序退出时终止线程 10 if (m_gui->getGetImgThreadState() == THREAD_STATE_E_PrepareStop) 11 { 12 m_gui->setGetImgThreadState(THREAD_STATE_E_Stop); 13 pyGetBaiduImgUninit(); 14 return ; 15 } 16 count++; 17 if (count == 50) 18 { 19 count = 0; 20 //只能使用emit方式操作控件,直接操作控件会死机,这里会等待槽函数结束才继续运行(Qt::BlockingQueuedConnection) 21 memset(m_img, 0, m_img_size); 22 int ret = pyGetBaiduImgWork(m_img, m_img_size); 23 if (ret != 0) 24 { 25 QThread::msleep(100); 26 continue; 27 } 28 29 QByteArray decodeimg = QByteArray::fromBase64(m_img); 30 31 CQVString filenamet; 32 filenamet.Append("%s%d", "imgtmp", _getpid()); 33 QFile file(filenamet.GetBuffer()); 34 file.open(QIODevice::WriteOnly); 35 file.write(decodeimg); 36 file.close(); 37 QThread::msleep(50); 38 39 QString saveFileName = m_gui->getSaveDir(); 40 if (saveFileName.length() > 1) 41 { 42 bool dirExist = true; 43 QDir dir; 44 if (!dir.exists(saveFileName)) 45 { 46 dirExist = dir.mkdir(saveFileName); 47 } 48 49 if (dirExist) 50 { 51 CQVString filenamet; 52 filenamet.Append("%s%d", "imgtmp", _getpid()); 53 QImageReader reader(filenamet.GetBuffer()); 54 reader.setDecideFormatFromContent(true); 55 int nCount = reader.imageCount(); 56 if (nCount > 0 && reader.canRead()) 57 { 58 QString fileFomate(QImageReader::imageFormat(filenamet.GetBuffer())); 59 60 saveFileName += "/"; 61 QDateTime current_date_time = QDateTime::currentDateTime(); 62 QString current_date = current_date_time.toString("yyyy-MM-dd-hh-mm-ss"); 63 saveFileName += current_date; 64 saveFileName += "."; 65 saveFileName += fileFomate; 66 QFile saveFile(saveFileName); 67 saveFile.open(QIODevice::WriteOnly); 68 saveFile.write(decodeimg); 69 saveFile.close(); 70 QThread::msleep(50); 71 72 } 73 } 74 } 75 76 m_gui->setFileChange(); 77 } 78 QThread::msleep(100); 79 } 80 }
5、项目生成后事件需要把python的dll拷贝到exe目录下,并且把python的DLLs和Lib目录以及我们写的py程序拷贝到主工程和exe目录下。这样才能保证vs2019和双击exe正常运行程序,如图: