一、資源下載地址

https://www.aliyundrive.com/s/jBU2wBS8poH

本專案路徑:專案->收費->百度圖片下載器(可試用5分鐘)

安裝包直接下載地址:http://139.9.165.1/media/BaiduPicDown.exe

二、專案介紹

1、本專案使用Vs2019+Qt庫+Python庫來開發一個百度圖片播放下載器(支援Gif)。

Qt播放Gif圖片參考文章:

https://www.cnblogs.com/liangqin/p/15161809.html

本文主要描述Qt呼叫Python庫來獲取百度圖片。

2、本專案完成的功能如下:

(1)、支援通過搜尋關鍵詞及圖片張數來獲取百度圖片進行迴圈播放及儲存原檔案。

(2)、支援設定播放圖片視窗大小。

(3)、支援Gif圖片。

3、效果展示:

三、專案開始

1、準備工作

搭建一份Qt播放Gif圖片的工程環境。然後獲取python對應平臺的標頭檔案及庫:

注意:安裝的python的Lib目錄下有很多檔案,整個目錄比較大,不便於我們程式釋出。我們只需要保留程式所需要的檔案即可,找到本程式的安裝路徑即可看到基本所需的Lib檔案。

2、寫python程式來爬取百度圖片

這裡參考文章:

https://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 = 'http://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正常執行程式,如圖: