【編程】自定義下載簡書文章和圖片

歡迎關(guān)注我的專欄( つ??ω??)つ【人工智能通識】
【專題】簡書下載器:Python-Tkinter項(xiàng)目編程入門


繼續(xù)本專題前面的文章。

梳理Tkinter界面

梳理后運(yùn)行結(jié)果如下:


對應(yīng)的main.py代碼如下:

from tkinter import *
from tkinter import ttk
import time
import random
import modules.reqs as reqs  # 導(dǎo)入reqs請求函數(shù)
import modules.options as opts
from tkinter.scrolledtext import ScrolledText

# 創(chuàng)建窗體
root = Tk()
root.title('簡書文章下載器')
root.resizable(width=False, height=False)
root.config(background='#EEE')


def run():  # 啟動(dòng)獲取動(dòng)作
    reqs.getAll(opts, [0], [4])


def refreshInfo():  # 信息的自刷新函數(shù)
    text = reqs.genInfoStr()
    text += '\nState:'+reqs.state
    text += '\nCurVol:'+reqs.curVol
    text += '\nCurArt:'+reqs.curArt
    text += '\nCurImg:'+reqs.curImg
    info.config(text=text)
    info.after(500, refreshInfo)


# 創(chuàng)建界面
rown = 0  # 占位符
ttk.Frame(root, height=10).grid() 

rown += 1  # header輸入框
iptHeader = ScrolledText(root, height=1, width=50)
iptHeader.grid(row=rown, padx=10, pady=0, sticky=W)

rown += 1  # header輸入框說明
label1 = ttk.Label(root, text='粘貼header,不要包含:打頭的部分,去除if-none-match行')
label1.grid(row=rown, pady=0, padx=10, sticky=W)

rown += 1  # 占位符
ttk.Frame(root, height=20).grid(row=rown) 

rown += 1 # 文集序號輸入框
iptVol = Text(root,height=1, width=50)
iptVol.grid(row=rown, padx=10, pady=0, sticky=W)

rown += 1  # vol輸入框說明
label2 = ttk.Label(root, text='文集列表,請用逗號分隔,如0,1,2')
label2.grid(row=rown, pady=0, padx=10, sticky=W)

rown += 1  # 占位符
ttk.Frame(root, height=20).grid(row=rown) 

rown += 1 # 文集序號輸入框
iptArt = Text(root,height=1, width=50)
iptArt.grid(row=rown, padx=10, pady=0, sticky=W)

rown += 1  # vol輸入框說明
label3 = ttk.Label(root, text='文章列表,請用逗號分隔,如0,1,2')
label3.grid(row=rown, pady=0, padx=10, sticky=W)

rown += 1  # 占位符
ttk.Frame(root, height=30).grid(row=rown) 

# 運(yùn)行按鈕
rown += 1
bt = ttk.Button(root, text='開始下載', width=30,command=run)
bt.grid(row=rown, padx=10,ipady=10 ,pady=0, sticky='WE')

# 信息標(biāo)簽
rown += 1
info = ttk.Label(root, text='?/?')
info.grid(row=rown,  padx=10, ipady=10, ipadx=10, sticky=W)
info.after(500, refreshInfo)  # 自動(dòng)循環(huán)更新

root.mainloop()

獲取用戶輸入

在Tkinter中可以使用xxx.get('1.0',END)來獲取輸入內(nèi)容,這里的1.0表示從頭獲取,END表示獲取全部。

我們修改run函數(shù):

def run():  # 啟動(dòng)獲取動(dòng)作
    hdrs = iptHeader.get("1.0", END)
    vols = iptVol.get("1.0", END)
    arts = iptArt.get("1.0", END)

    opts.headers = opts.str2obj(hdrs, '\n', ': ')

    volnarr = []
    vols=vols.replace('\n', '')
    volsarr = vols.split(',')
    volnarr = map(lambda x: int(x), volsarr)
    volnarr = list(set(volnarr))

    artnarr = []
    artsarr = arts.split(',')
    map(lambda x: int(x), artsarr)
    artnarr = list(set(artnarr))

    writeHeaders()  # 保存設(shè)置

    reqs.getAll(opts, volnarr, artnarr)

然后運(yùn)行main.py,從瀏覽器復(fù)制粘貼你的headers,然后設(shè)定文集列表和文章列表,點(diǎn)擊按鈕就能開始下載文章了。

自動(dòng)保存和讀取headers

每次都手工復(fù)制headers比較麻煩,我們?yōu)檐浖黾幼詣?dòng)保存和讀取headers的功能。

每次打開的時(shí)候自動(dòng)嘗試讀取config.txt文件,填充到iptHeaders,然后每次運(yùn)行都自動(dòng)將輸入框的文字保存到config.txt文件。

在最底部添加代碼:

def writeHeaders():  # 將header寫入到臨時(shí)文件
    hdrs = iptHeader.get("1.0", END)
    with open(os.getcwd()+'/config.txt', 'a') as f:
        f.write(hdrs)

def  readHeaders(): #讀取設(shè)置文件并填充到界面
    fpath=os.getcwd()+'/config.txt'
    if os.path.exists(fpath):
        f=open(fpath,'r')
        hdrs=f.read()
        iptHeader.insert(INSERT,hdrs)

readHeaders()

root.mainloop()

注意root.mainloop()要放在最后。

最后匯總

如果要打包成獨(dú)立運(yùn)行的軟件(不安裝python也可以運(yùn)行),那么請參考這個(gè)文章:

【編程】用Py2app打包Python-Tkinter項(xiàng)目

整個(gè)項(xiàng)目代碼已經(jīng)同步到我的github中,你也可以從dist文件夾下找到mac版本的打包軟件直接下載使用。地址是:

zhyuzh的簡書下載器JianshuDownloader

整個(gè)項(xiàng)目暫時(shí)告一段落,全部文章已整理在:

【專題】Python-Tkinter項(xiàng)目編程入門

最終代碼

最新修訂請參照 Github項(xiàng)目:JianshuDownloader

main.py

from tkinter import *
from tkinter import ttk
import time
import random
import modules.reqs as reqs  # 導(dǎo)入reqs請求函數(shù)
import modules.options as opts
from tkinter.scrolledtext import ScrolledText
import os

# 創(chuàng)建窗體
root = Tk()
root.title('簡書文章下載器')
root.resizable(width=False, height=False)
root.config(background='#EEE')


def run():  # 啟動(dòng)獲取動(dòng)作
    hdrs = iptHeader.get("1.0", END)
    vols = iptVol.get("1.0", END)
    arts = iptArt.get("1.0", END)

    opts.headers = opts.str2obj(hdrs, '\n', ': ')

    volnarr = []
    vols=vols.replace('\n', '')
    volsarr = vols.split(',')
    volnarr = map(lambda x: int(x), volsarr)
    volnarr = list(set(volnarr))

    artnarr = []
    artsarr = arts.split(',')
    map(lambda x: int(x), artsarr)
    artnarr = list(set(artnarr))

    writeHeaders()  # 保存設(shè)置

    reqs.getAll(opts, volnarr, artnarr)


def refreshInfo():  # 信息的自刷新函數(shù)
    text = reqs.genInfoStr()
    text += '\nState:'+reqs.state
    text += '\nCurVol:'+reqs.curVol
    text += '\nCurArt:'+reqs.curArt
    text += '\nCurImg:'+reqs.curImg
    info.config(text=text)
    info.after(500, refreshInfo)


# 創(chuàng)建界面
rown = 0  # 占位符
ttk.Frame(root, height=10).grid()

rown += 1  # header輸入框
iptHeader = ScrolledText(root, height=1, width=50)
iptHeader.grid(row=rown, padx=10, pady=0, sticky=W)

rown += 1  # header輸入框說明
text1 = '''
請從瀏覽器右擊檢查打開控制臺
切換到Network部分
從XHR類型中找到notebooks請求
復(fù)制它的Request Headers部分
注意不要包含:打頭的部分,并去除if-none-match行'
'''
label1 = ttk.Label(root, text=text1)
label1.grid(row=rown, pady=0, padx=10, sticky=W)

rown += 1  # 占位符
ttk.Frame(root, height=20).grid(row=rown)

rown += 1  # 文集序號輸入框
iptVol = Text(root, height=1, width=50)
iptVol.grid(row=rown, padx=10, pady=0, sticky=W)

rown += 1  # vol輸入框說明
label2 = ttk.Label(root, text='文集列表,請用英文逗號分隔,如0,1,2')
label2.grid(row=rown, pady=0, padx=10, sticky=W)

rown += 1  # 占位符
ttk.Frame(root, height=20).grid(row=rown)

rown += 1  # 文集序號輸入框
iptArt = Text(root, height=1, width=50)
iptArt.grid(row=rown, padx=10, pady=0, sticky=W)

rown += 1  # vol輸入框說明
label3 = ttk.Label(root, text='文章列表,請用英文逗號分隔,如0,1,2')
label3.grid(row=rown, pady=0, padx=10, sticky=W)

rown += 1  # 占位符
ttk.Frame(root, height=30).grid(row=rown)

# 運(yùn)行按鈕
rown += 1
bt = ttk.Button(root, text='開始下載', width=30, command=run)
bt.grid(row=rown, padx=10, ipady=10, pady=0, sticky='WE')

# 信息標(biāo)簽
rown += 1
info = ttk.Label(root, text='?/?')
info.grid(row=rown,  padx=10, ipady=10, ipadx=10, sticky=W)
info.after(500, refreshInfo)  # 自動(dòng)循環(huán)更新


def writeHeaders():  # 將header寫入到臨時(shí)文件
    hdrs = iptHeader.get("1.0", END)
    with open(os.getcwd()+'/config.txt', 'a') as f:
        f.write(hdrs)


def readHeaders():  # 讀取設(shè)置文件并填充到界面
    fpath = os.getcwd()+'/config.txt'
    if os.path.exists(fpath):
        f = open(fpath, 'r')
        hdrs = f.read()
        iptHeader.insert(INSERT, hdrs)


readHeaders()

root.mainloop()

modules/reqs.py


import time
from threading import Thread
import requests
import json
import os
import re
import math


atotal = 1  # 總文章數(shù)量
afini = 0  # 已讀取的文章數(shù)量
cookiestr = ''  # cookie全局變量
curVol = '...'  # 正在獲取的文集名
curArt = '...'  # 正在獲取的文章名
curImg = '...'  # 正在獲取的圖片名
state = '等待中'  # 當(dāng)前狀態(tài),等待中,獲取中,已完成。


def genInfoStr():  # 拼接信息字符串
    global atotal
    global afini
    infoStr = '正在獲取('+str(afini)+'/'+str(atotal)+'):'
    per = atotal/15
    fi = math.ceil(afini/per)
    for _ in range(fi):
        infoStr += '■'
    for _ in range(15-fi):
        infoStr += '□'
    return infoStr

# volnarr文集列表,artnarr文章列表,例如volnarr=[0,3,1]表示只獲取三個(gè)文集,默認(rèn)為全部


def getAll(opt, volnarr, artnarr):  # 啟動(dòng)線程
    t = Thread(target=getVolums, args=(opt, volnarr, artnarr))  # 多線程,避免鎖死界面
    t.start()
    global state
    state = '獲取中'
    print(state)
    return 'RUNNING!'


def getArticle(art, vol, opt):  # 獲取文章內(nèi)容
    artId = art['id']
    urlArt = 'http://www.itdecent.cn/author/notes/'+str(artId)+'/content'
    res = requests.get(urlArt, headers=opt.headers)
    resdata = json.loads(res.text)
    cont = resdata['content']

   # 文件路徑
    dir = os.getcwd()+'/data/' + vol['name'] + '/'
    fname = dir+art['title']
    if not os.path.exists(dir):
        os.makedirs(dir)
        os.makedirs(dir+'/imgs/')

    # 獲取圖片地址
    imglist = re.findall(r"[^`]\!\[[^\]]*\]\((.+?)\)", cont, re.S)
    for iu in imglist:
        imgUrl = iu.split('?')[0]
        global curImg
        curImg = os.path.basename(imgUrl)
        print('GETTING IMAGE:', curImg)
        if not os.path.exists(imgUrl):
            res = requests.get(imgUrl)
            img = res.content
            with open(dir+'imgs/'+os.path.basename(imgUrl), 'wb') as f:
                f.write(img)
            time.sleep(1)

    # 保存md文件
    if art['note_type'] == 2:
        fname += '.md'
    else:
        fname += '.html'
    if os.path.exists(fname):
        os.remove(fname)

    with open(fname, 'a') as f:
        newcont = cont.replace(
            'https://upload-images.jianshu.io/upload_images', 'imgs')
        f.write(newcont)
        f.close()

    return 'getArticles OK!'


def getArticlesList(vol, opt, artnarr):  # 獲取文章列表
    volId = vol['id']
    urlVol = 'http://www.itdecent.cn/author/notebooks/'+str(volId)+'/notes'
    res = requests.get(urlVol, headers=opt.headers)
    resdata = json.loads(res.text)
    getlist = []  # 指定獲取列表
    if len(artnarr) == 0:
        getlist = resdata
    else:
        for i in artnarr:
            if i < len(resdata):
                getlist.append(resdata[i])

    # 每個(gè)文集計(jì)算文章數(shù)量
    global atotal
    global afini
    atotal = len(getlist)
    afini = 0
    print("ATOTAL:", atotal, afini)

    n = 0
    for d in getlist:
        global curArt
        curArt = str(n)+':' + d['title']
        n += 1
        print('GETTING ARTICLE:', curArt)
        time.sleep(1)
        getArticle(d, vol, opt)
        afini += 1  # 計(jì)數(shù)加1
    return 'getArticlesList OK!'


def getVolums(opt, volnarr, artnarr):  # 獲取文集列表
    res = requests.get(opt.urlVolumns, headers=opt.headers)
    resdata = json.loads(res.text)
    getlist = []  # 指定獲取列表
    if len(volnarr) == 0:
        getlist = resdata
    else:
        for i in volnarr:
            if i < len(resdata):
                getlist.append(resdata[i])
    n = 0  # 序號
    for d in getlist:
        global curVol
        curVol = str(n)+':'+d['name']
        n += 1
        print('GETTING VOLUMN:', curVol)
        time.sleep(1)
        getArticlesList(d, opt, artnarr)

    print("DOWNLOAD FINI!", atotal, afini)
    global state
    state = '已完成'
    print(state)
    return 'getVolums OK!'

modules/options.py

savePath='../data'
urlVolumns = 'http://www.itdecent.cn/author/notebooks'
params = {'order_by': 'shared_at', 'page': '1'}
headers = '''
請復(fù)制粘貼您瀏覽器的headers信息
'''
def str2obj(s, s1=';', s2='='):
    li = s.split(s1)
    res = {}
    for kv in li:
        li2 = kv.split(s2)
        if len(li2) > 1:
            res[li2[0]] = li2[1]
    return res

headers = str2obj(headers, '\n', ': ')

歡迎關(guān)注我的專欄( つ??ω??)つ【人工智能通識】


每個(gè)人的智能新時(shí)代

如果您發(fā)現(xiàn)文章錯(cuò)誤,請不吝留言指正;
如果您覺得有用,請點(diǎn)喜歡;
如果您覺得很有用,歡迎轉(zhuǎn)載~


END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容