偷個懶,公號摳腚早報80%自動化——1.批量生成微信封面圖

簡述

2018年的三月份寫過一篇:《小豬的Python學習之旅 —— 18.Python微信轉發小宇宙早報》,從一開始 手動轉發別人發的新聞早報,到編寫腳本到自動轉發。而後畢竟這個是別人整理的,並不能保證準時,不少 時候早報變成了午報,而後還有存檔問題,每次想看以前的早報就,都要微信搜聊天記錄。嘖嘖,我琢磨着, 要不本身來整理早報。因而偷偷開始手動去整理早報,而後發在公號「摳腚男孩」上:python

畢竟本身整理的,不肯定看官能不能接受,得找幾個小火汁來驗證下下~數據庫

而後,把我整理的日報,套模板,改下日期,丟到童鞋羣裏看看他們有木有發現,測試兩天後~bash

行吧,我就發吧,接下來簡單說下天天發早報的流程。微信

  • 1.製做當天早報的封面圖
  • 2.瀏覽新華社,i黑馬,第一財經週刊等新聞站點,採集有趣的新聞,複製下標題
  • 3.把複製的標題粘貼到文本編輯器中,湊夠15條新聞
  • 4.登陸微信公衆平臺,打開昨天的文章,複製樣式,粘貼,而後把今天的內容填進去

接着檢查下,沒什麼問題,就發佈了。一開始,天天要耗費將近一個小時的時間來完成這件 事情,天天的摸魚時間這麼寶貴,花一個小時來作這件事情,顯得有些得不償失。做爲一個 **勤(lan)奮(duo)**的開發仔,確定要想辦法來優化下,最好的結果就是:發早報徹底自動化~app

固然,也是想一想而已,有讀者可能好奇,爲啥標題是80%,那麼剩下的20%是什麼?微信公衆平臺

答:15%是新聞的過濾,篩選有意思的新聞標題,這一步其實能夠優化,最簡單的就是,直接爬熱搜 或者評論多的新聞標題,可是這樣莫得靈魂,我更傾向於訓練一個機器人,讓他自動去篩選標題。 可是目前還不會這些東西,因此先擱一擱咯,還得手動去篩選~ 剩下的5%是把早報發表到公號上,其實也能夠自動化,經過selenium寫個腳本自動點點點 就行了,不過我的感受意義不大,並且我習慣發出去以前還須要預覽下,確認內容 無誤後才發送,畢竟文章發佈後只能改標題,不能修改內容。編輯器

說下我目前想達到的一個形態吧ide

  • 1.編寫腳本批量生成微信的封面圖。
  • 2.編寫爬蟲定時去爬取新聞,保存到本地數據庫中。
  • 3.編寫接口,包括獲取當天採集到的新聞,加入到新聞篩選池等。
  • 4.編寫用來篩選新聞的APP,利用上班坐地鐵的時間快速篩選當天的新聞標題。
  • 5.編寫一鍵生成統同樣式的早報文章的腳本。
  • 6.編寫一鍵生成當天新聞詳情頁面的腳本。
  • 7.複製粘貼生成的樣式文章,填寫標題,發佈者,在閱讀原文中添加當天新聞詳情頁面的url,完成發佈。
  • 8.編寫微信機器人,定時(暫定10點),拉取早報進行文本處理後,自動轉發到相關的羣。

好吧,大概的路線就這樣,本節先從製做早報封面圖開始**優(偷)化(懶)**吧。函數


1.封面圖的製做過程

先來看看我天天的早報封面圖吧,是介樣的:post

組成部分:背景圖(900*383)+ 大標題(52px) + 二級標題(44px) 接着縮下我是製做這種封面圖的流程:

  • 1.平時閒着沒事逛下一些壁紙的APP或者站點,以爲好看的就保存下來。
  • 2.打開Pixelmator Pro新建一個900*383的模板,把圖片拖進去,調節圖片大小直到圖片的寬度和模板的寬度相等。
  • 3.接着移動調整縮放後的圖片,直到本身喜歡位置。
  • 4.依次添加大小標題,調整居中。
  • 5.合併圖層,裁剪。
  • 6.導出成jpg文件。

爲了讓大家感覺這個流程,我大概錄了個Gif演示下,實際操做耗時遠比這個久(7,8分鐘的樣子)。

天天如機器搬重複着這樣的操做,多呆哦~而後,我居然堅持了60+天(┬_┬); 着實須要一個腳本,把我從這種繁冗的工做中解脫出來。

讀者可能對圖源感興趣,我通常喜歡直接保存壁紙APP裏精選的靚圖, 固然也能夠自行爬取一些壁紙站點。另外,我發現,有些長圖,其實 能夠裁剪成幾份來做爲多期的封面,好比這樣的圖:

分割成兩個,挺好看的。

感受像像集卡同樣,有點意思。


2.提取圖片處理的流程

先來提取下圖片處理的流程:

  • 圖片縮放:保持長寬比例不變進行縮放,直到寬爲900px爲止。
  • 圖片裁剪:先計算圖片能夠裁剪成多少份,以圖片中間爲基準裁剪,計算Y軸偏移,每一個圖片的座標。
  • 圖片加字:對裁剪後的圖片依次添加大小標題。

3.材料準備

行吧,處理流程說了,說下用到的Python庫,直接經過pip命令安裝便可: (主要使用opencv來進行圖片處理,pillow即PIL庫)

pip install numpy
pip install opencv-python
pip install pillow
複製代碼

4.圖片縮放

保持長寬比,設置爲900px,咱們經過opencv提供的imread()方法來獲取一個圖片對象,而後進行 相關操做。先獲取一波高和寬度

import cv2

img = cv2.imread('1.jpg')
(h, w) = img.shape[:2]
print(h, w)

# 輸出結果:956 1080
複製代碼

若是你想把圖片顯示出來,能夠直接調用imshow()方法:

cv2.imshow('image', img)   # 參數依次爲:窗口名稱(窗口不能重名),讀入的圖片。
複製代碼

上述的代碼,運行後會發現窗口一閃而過,能夠調用waitKey()讓窗口不關閉

cv2.waitkey()   # 想窗口一直不關閉,能夠不填參數或填0;也能夠指定一個等待時間(單位毫秒)
                # 在一個時間段內,等待用戶按鍵觸發關閉,若是一直不按鍵,到了時間會自動關閉。
複製代碼

可是,這裏其實隱藏着一個小坑:若是你的圖片是中文文件名或文件路徑包含中文,調用imread會報錯,好比:

img = cv2.imread('測試.jpg')
cv2.imshow('img', img)
cv2.waitKey()
複製代碼

運行結果

一樣調用imwrite()方法也是沒法生成帶有中文路徑的圖片的,能夠自行編寫兩個函數來解決:

def cv_imread(file_path):
    cv_img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), -1)
    return cv_img
    
def cv_imwrite(img, file_path):
    cv2.imencode('.jpg', img)[1].tofile(file_path)
複製代碼

行吧,能獲取到寬高了,接着調用opencv提供的resize()方法調整圖片的尺寸,參數依次爲: 圖片寬高元組,還有一個可選參數:interpolation插值方法,默認使用INTER_LINEAR 雙線性插值,其餘的還有:INTER_NEARESTINTER_AREAINTER_CUBICINTER_LANCZOS4

import cv2
import numpy as np


def cv_imread(file_path):
    cv_img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), -1)
    return cv_img


def cv_imwrite(img, file_path):
    cv2.imencode('.jpg', img)[1].tofile(file_path)


img = cv_imread('測試.jpg')
(h, w) = img.shape[:2]
print("縮放前的尺寸:", img.shape[:2])
res = cv2.resize(img, (900, round(h * (900 / w))))
print("裁剪後的尺寸:", res.shape[:2])
複製代碼

運行結果

縮放前的尺寸: (956, 1080)
縮放後的尺寸: (797, 900)
複製代碼

5.圖片裁剪

縮放完,接着就到裁剪了,先是計算圖片能裁剪成幾張:

crop_pic_count = int(ch / 383)
print("圖片能夠裁剪爲:%d張" % crop_pic_count)

# 輸出結果:圖片能夠裁剪爲:2張
複製代碼

接着是裁剪圖片,能夠經過:圖片對象[y軸起始座標:y軸終點座標, x軸起始座標:x軸終點座標],來裁剪。 因此,咱們要計算每一個裁剪區域的對應的座標方位。另外,這裏還要考慮一個偏移,以中間位置爲基準進行 裁剪,這樣感受會好一點。給個加偏移和不加偏移裁剪後的對比圖吧:

so,我仍是傾向於加偏移,計算偏移也很簡單,直接拿高對383進行求餘,而後除以2。

start_y = int(ch % 383 / 2)
複製代碼

接着根據能切成的圖片張數,計算怎麼裁剪

for i in range(0, crop_pic_count):
    crop_img = res[383 * i + start_y: 383 * (i + 1) + start_y, 0:900]
    cv_imwrite(crop_img, '剪切圖%d.jpg' % (i+1))
複製代碼

裁剪後的圖片

6.圖片加字

行吧,圖片也裁剪好了,接着就是圖片加字了,能夠經過opencv提供的putText()添加文字, 參數依次爲: 圖像文字內容座標字體大小顏色字體厚度

img = cv_imread('剪切圖0.jpg')
cv2.putText(img, 'Test', (50, 300), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 255, 255), 2)
cv2.imshow('image', img)
cv2.waitKey()
複製代碼

運行結果以下

加字成功,挺簡單的,是吧?可是,若是你添加的文字不是字母或數字,而是中文的話,那麼恭喜,黑人問號~

緣由是:opencv自帶的putText函數沒法輸出utf8類型的字符,所以沒法將中文打印到圖片上。 兩個解決方法:

  • 方法一:利用另外一個freetype庫,將字符解碼轉碼,不過有點繁瑣。
  • 方法二:利用pillow庫裏ImageDraw類的text函數繪製中文,先從成cv2轉PIL格式,加完中文再轉回cv2格式輸出。

這裏採用的是方法二,text函數的參數:起始座標元組,文字內容,字體,顏色。 問題來了,怎麼肯定繪製文字的起始座標?

答:若是你要程序算,挺麻煩的,文字寬度怎麼獲取,既然圖片尺寸固定,文字長度不變,爲什麼不取巧一下呢?

直接在Pixelmator Pro上把文字拖好,而後複製下座標,不就行了~

另外,這裏筆者用的字體是 蘋果-簡,常規體,能夠自行下載,記得把字體文件名改爲英文文件名, 否則,會讀取不到字體。好的,擼代碼試試:

img = cv_imread('剪切圖0.jpg')
# 將圖片從OpenCv格式轉爲PIL格式
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 加載字體(字體文件名,字體大小)
title_font = ImageFont.truetype('apple-simple.ttf', 52)
date_font = ImageFont.truetype('apple-simple.ttf', 44)
# 繪製文字的位置
title_pos = (236, 110)
date_pos = (338, 192)
# 繪製內容
title_content = u"『摳腚早報速讀』"
date_content = u"第190111期"
# 繪製
draw = ImageDraw.Draw(img_pil)
draw.text(title_pos, title_content, font=title_font, fill=(255, 255, 255))
draw.text(date_pos, date_content, font=date_font, fill=(255, 255, 255))
img_open_cv = cv2.cvtColor(np.asarray(img_pil), cv2.COLOR_RGB2BGR)
cv_imwrite(img_open_cv, "加字後.jpg")
複製代碼

接着看下輸出的圖片

嘖嘖嘖,完美,到此自動裁剪生成一個早報封面的腳本就完成啦,接下來咱們來補全和完善下咱們的程序。

7.代碼補全完善

就是加了循環,一些小邏輯,比較簡單,註釋也比較清晰,就不叨逼叨了,直接上完整代吧:

# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
import time
from PIL import Image, ImageDraw, ImageFont
from datetime import datetime, timedelta
import shutil

pic_source_dir = os.path.join(os.getcwd(), "news_pic_source\\")  # 原圖路徑
pic_crop_dir = os.path.join(os.getcwd(), "news_pic_crop\\")  # 裁剪後的圖片路徑
pic_font_dir = os.path.join(os.getcwd(), "news_pic_font\\")  # 加字後的圖片路徑
start_date = "20190112"  # 繪製圖片的其起始日期


# 判斷文件夾是否存在,不存在則新建
def is_dir_existed(path, mkdir=True):
    if mkdir:
        if not os.path.exists(path):
            os.makedirs(path)
    else:
        return os.path.exists(path)


# opencv讀取中文路徑名會亂碼
def cv_imread(file_path):
    cv_img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), -1)
    return cv_img


# opencv寫入中文路徑名會亂碼
def cv_imwrite(img, file_path):
    cv2.imencode('.jpg', img)[1].tofile(file_path)


# 把原圖裁剪爲多個小圖(900*383)
def crop_little_pic(pic_path):
    img = cv_imread(pic_path)
    (sh, sw) = img.shape[:2]
    # 將圖片的寬設置爲900,高則按比例縮放
    res = cv2.resize(img, (900, round(sh * (900 / sw))))
    # 獲取縮放後的高和寬,判斷圖片可裁剪的張數
    (ch, cw) = res.shape[:2]
    crop_pic_count = int(ch / 383)
    # 計算Y軸偏移
    start_y = int(ch % 383 / 2)
    # 根據圖片的張數來決定怎麼裁剪
    for i in range(0, crop_pic_count):
        crop_img = res[383 * i + start_y: 383 * (i + 1) + start_y, 0:900]
        cv_imwrite(crop_img, os.path.join(pic_crop_dir, str(int(round(time.time() * 1000))) + '.jpg'))


# 繪製文字
def draw_text(pic_path, date):
    img = cv_imread(pic_path)
    # 將圖片從OpenCv格式轉爲PIL格式
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    # 加載字體(字體文件名,字體大小)
    title_font = ImageFont.truetype('apple-simple.ttf', 52)
    date_font = ImageFont.truetype('apple-simple.ttf', 44)
    # 繪製文字的位置
    title_pos = (236, 110)
    date_pos = (316, 192)
    # 繪製內容
    title_content = u"『摳腚早報速讀』"
    date_content = u"第%s期" % date[2:]
    # 繪製
    draw = ImageDraw.Draw(img_pil)
    draw.text(title_pos, title_content, font=title_font, fill=(255, 255, 255))
    draw.text(date_pos, date_content, font=date_font, fill=(255, 255, 255))
    img_open_cv = cv2.cvtColor(np.asarray(img_pil), cv2.COLOR_RGB2BGR)
    cv_imwrite(img_open_cv, os.path.join(pic_font_dir, date[2:] + '.jpg'))


# 遍歷得到某類文件路徑列表
def fetch_file_path(path, file_type):
    file_list = []
    f = os.listdir(path)
    for i in f:
        if i.endswith(file_type):
            file_list.append(os.path.join(path, i))
    return file_list


# 構造生成日期列表
def init_date_list(begin_date, count):
    d_list = []
    begin_date = datetime.strptime(begin_date, "%Y%m%d")
    end_date = datetime.strptime((datetime.now() + timedelta(days=count)).strftime("%Y%m%d"), "%Y%m%d")
    while begin_date <= end_date:
        date_str = begin_date.strftime("%Y%m%d")
        d_list.append(date_str)
        begin_date += timedelta(days=1)
    return d_list


if __name__ == '__main__':
    is_dir_existed(pic_source_dir)
    while True:
        choice = input(
            "%s\n請輸入你想進行的操做\n1.進行圖片裁剪\n2.圖片加字\n3.清空裁剪文件夾\n4.清空加字文件夾\n5.退出程序\n%s\n" % ('=' * 32, '=' * 32))
        if choice == '1':
            is_dir_existed(pic_crop_dir)
            pic_path_list = fetch_file_path(pic_source_dir, ".jpg")
            if len(pic_path_list) == 0:
                print("原圖文件夾中無圖片,請先添加圖片!")
            else:
                print("開始批量裁剪...")
                begin = datetime.now()
                for pic in pic_path_list:
                    crop_little_pic(pic)
                end = datetime.now()
                print("批量裁剪完畢,生成圖片:%d張,耗時:%s秒" % (len(fetch_file_path(pic_crop_dir, ".jpg")), (end - begin).seconds))
        elif choice == '2':
            is_dir_existed(pic_font_dir)
            crop_path_list = fetch_file_path(pic_crop_dir, ".jpg")
            date_list = init_date_list(start_date, len(crop_path_list) + 1)
            if len(crop_path_list) == 0:
                print("裁剪文件夾中無圖片,請先生成裁剪圖片!")
            else:
                print("開始批量加字...")
                begin = datetime.now()
                print(len(crop_path_list), len(date_list))
                for i in range(len(crop_path_list)):
                    draw_text(crop_path_list[i], date_list[i])
                end = datetime.now()
                print("批量加字完畢,處理圖片:%d張,耗時:%s秒" % (len(fetch_file_path(pic_font_dir, ".jpg")), (end - begin).seconds))
        elif choice == '3':
            if is_dir_existed(pic_crop_dir, False):
                shutil.rmtree(pic_crop_dir)
                print("文件夾刪除成功!")
            else:
                print("文件夾不存在,刪除失敗~")
        elif choice == '4':
            if is_dir_existed(pic_font_dir, False):
                shutil.rmtree(pic_font_dir)
                print("文件夾刪除成功!")
            else:
                print("文件夾不存在,刪除失敗~")
        elif choice == '5':
            exit("退出程序~")
        else:
            print("錯誤序號,請確認後從新輸入!!!")
複製代碼

執行前,先準備一波圖片原圖,這裏準備了:

接着運行一波代碼,運行後依次鍵入1,2進行裁剪和加字:

嘖嘖,94張原圖生成了243張封面圖,並且,只花了十幾秒,打開生成的文件夾看一波:

都生成到9月份了,2333,真人生苦短,我用Python,此處應該有掌聲~

另外前幾天寫了個腳本是採集一堆視頻第一幀而後進行處理的,趕腳有同窗會須要,把核心代碼也貼下把~

# 截取視頻的第一幀
def fetch_video_first_frame(path_list):
    for mp4 in path_list:
        cap = cv2.VideoCapture(mp4)
        if cap.isOpened():
            ret, im = cap.read()
            cv2.imencode('.jpg', im)[1].tofile(
                os.path.join(pic_source_output_dir, mp4.split("\\")[-1]).replace("mp4", "jpg"))
        cap.release()
複製代碼

行吧,本節內容就這麼多,有疑問的歡迎在評論區留言~


Tips:公號目前只是堅持發早報,在慢慢完善,有點心虛,只敢貼個小圖,想看早報的能夠關注下~

相關文章
相關標籤/搜索