python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

本文介紹一個python爬蟲小項目,通過tkinter繪製交互界面,根據界面提供的信息爬取網站、篩選數據,最後將獲得的數據寫入excel表格並用圖表顯示,達到不打開網站就能獲得成果表格的目的。具體分享如下:

程序界面 運行效果:

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

點擊“使用說明”按鈕,彈出說明文件:

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

代碼分4塊:

1、tkinter設置界面,通過交互得到查詢的城市(對應招標網址)、項目類型(分施工、設計、監理三種)、查詢起止日期、投資額範圍及文件保存地址。將獲得的以上7個信息作為參數傳入第2、3步使用。

2、requests + .json獲得網站每條項目信息網址,再通過 requests + BeautifulSoup訪問解析,篩選獲取有用信息形成列表。

3、xlsxwriterg根據第1步傳遞的文件保存地址,創建excel文件,將第2步的信息列表寫入文件,生成圖表。

4、輔助功能:為便於查看結果文件,在程序界面上設置“打開文件”和“打開文件夾”按鈕,點擊直接打開,省去逐步打開文件夾的過程。

一、tkinter設置界面

將界面分為標題及1至8行,每行逐步建立,避免混亂。

<code>root = Tk()  # 創建窗口 

root.title('招標信息查詢')
root.resizable(0, 0) # 0,0表示框體x,y方向均不可調;
root.geometry('700x450+200+80')
dirpath = biaot
#
ft0 = tkFont.Font(family='楷體', size=16, weight=tkFont.BOLD)
titlelabel = Label(root, text='招標信息查詢小助手', font=ft0, anchor='w')
titlelabel.grid(row=0, column=1, columnspan=8, pady=10)

# 第1行
ft = tkFont.Font(family='Fixdsys', size=10, weight=tkFont.BOLD, slant=tkFont.ITALIC)
label1 = Label(root, text='選擇區域:', width=10, anchor='w') # padx=30, sticky=W
label1.grid(row=1, column=1, columnspan=2, pady = 10)
label11 = Label(root, text=city['杭州'][3], width=60, anchor='w', font=ft)
label11.grid(row=1, column=4, columnspan=6, sticky=W)
cl1=ttk.Combobox(root,textvariable=StringVar(), width=10) #初始化
cl1["values"]=("杭州","北京","上海","廣州")
cl1.current(0)
cl1.grid(row=1,column=3, sticky=W)
cl1.bind("<<comboboxselected>>",cit)
data['city'] = city[cl1.get()]

# 第2行
label2 = Label(root, text='查詢類型:', width=10, anchor='w')
label2.grid(row=2, column=1, columnspan=2, pady = 10)
h1 = BooleanVar()
cb1=Checkbutton(root, text='施工', width=10, anchor='w', variable=h1, command=type)
cb1.grid(row=2, column=3, sticky=W)
h2 = BooleanVar()
cb2 = Checkbutton(root, text='設計', width=10, anchor = 'w', variable=h2, command=type)
cb2.grid(row=2, column=4, sticky=W)
h3 = BooleanVar()
cb3 = Checkbutton(root, text='監理', width=10, anchor = 'w', variable=h3, command=type)
cb3.grid(row=2, column=5, sticky=W)
label21 = Label(root, text='當前查詢:', width=10, anchor='w')
label21.grid(row=2, column=6, columnspan=1, sticky=W)
label22 = Label(root, text='', width=20, anchor='w', font=ft)
label22.grid(row=2, column=7, columnspan=2, sticky=W)

# 第3行
label3 = Label(root, text='起始日期:', width=10, anchor='w')
label3.grid(row=3, column=1, columnspan=2, pady = 10)
cl31 = ttk.Combobox(root, textvariable=StringVar(), width=5)

cl31["values"] = ("2019", "2020", "2021")
cl31.current(1)
cl31.grid(row=3, column=3, sticky=W)
cl31.bind("<<comboboxselected>>", begintime)
cl32 = ttk.Combobox(root,textvariable=StringVar(), width=5)
cl32["values"] = (1,2,3,4,5,6,7,8,9,10,11,12)
cl32.current(2)
cl32.grid(row=3,column=4, sticky=W)
cl32.bind("<<comboboxselected>>", begintime)
day = []
for i in range(1,32):
day.append(i)
cl33 = ttk.Combobox(root,textvariable=StringVar(), width=5)
cl33["values"] = day
cl33.current(4)
cl33.grid(row=3, column=5, sticky=W)
cl33.bind("<<comboboxselected>>", begintime)
label31 = Label(root, text='開始日期:', width=10, anchor='w')
label31.grid(row=3, column=6, columnspan=1, sticky=W)
label32 = Label(root, text=cl31.get()+'-'+cl32.get()+'-'+cl33.get(), width=10, anchor='w', font=ft)
label32.grid(row=3, column=7, columnspan=1, sticky=W) # , pady=20
data['begintime'] = label32['text']

# 第4行
label4 = Label(root, text='終止日期:', width=10, anchor='w')
label4.grid(row=4, column=1, columnspan=2)
cl41 = ttk.Combobox(root, textvariable=StringVar(), width=5)
cl41["values"]=("2019","2020")
cl41.current(1)
cl41.grid(row=4, column=3, sticky=W, pady=10)
cl41.bind("<<comboboxselected>>", endtime)
cl42 = ttk.Combobox(root, textvariable=StringVar(), width=5)
cl42["values"]=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
cl42.current(2)
cl42.grid(row=4, column=4, sticky=W)
cl42.bind("<<comboboxselected>>", endtime)
day = []
for i in range(1,32):
day.append(i)
cl43 = ttk.Combobox(root, textvariable=StringVar(), width=5)
cl43["values"] = day
cl43.current(10)
cl43.grid(row=4, column=5, sticky=W)
cl43.bind("<<comboboxselected>>", endtime)
label41 = Label(root, text='終止日期:', width=10, anchor='w')
label41.grid(row=4, column=6, columnspan=1, sticky=W)
label42 = Label(root, text=cl41.get()+'-'+cl42.get()+'-'+cl43.get(), width=10, anchor='w', font=ft)
label42.grid(row=4, column=7, columnspan=2, sticky=W)
data['endtime'] = label42['text']


# 第5行
label5 = Label(root, text='造價範圍:', width=10, anchor='w')
label5.grid(row=5, column=1, columnspan=2, pady=12, sticky=N)
label51 = Label(root, text='————————————', width=10, anchor='w')
label51.grid(row=5, column=4, columnspan=1, pady=12, sticky=NW)
var51 = StringVar()
var51.trace("w", lambda name, index, mode, sv=var51: minpri(var51))
edit51 = Entry(root, textvariable=var51, width=8)
edit51.grid(row=5, column=3, pady=12, sticky=NW)
var52 = StringVar()
var52.trace("w", lambda name, index, mode, sv=var52: maxpri(var52))
edit52 = Entry(root, textvariable=var52, width=8)
edit52.grid(row=5, column=5, pady=12, sticky=NW)
bt51 = Button(root, text='保存地址', width=8, height=1, command=setpath)
bt51.grid(row=5, column=6, pady=10, columnspan=1, sticky=NW)
label53 = Label(root, text='E:\\python\\爬招標網\\試驗.xlsx', width=23, height=3, wraplength=170, justify='left', anchor="nw")# takefocus='True', wraplength=90, justify='left',
label53.grid(row=5, column=7, columnspan=2, pady=12, sticky=NW)
data['dir'] = label53.cget('text')

# 第6行至第8行,結果顯示框,佔三行三列
result = Label(root, width=36, wraplength = 280, height=10, font=("楷體", 10), fg="blue", borderwidth=2, relief='sunken', anchor='n', justify = 'left')
result.grid(row=6, column=3, columnspan=3, rowspan=3, sticky=NW)

# 第6行
pic_0 = PhotoImage(file='pic/休息.png')
button60 = Button(root, width=50, height=62, image=pic_0, command=shuoming)#, anchor='w'
button60.grid(row=6, column=1, columnspan=1, sticky=W, padx=20)#, sticky=NW, padx=30
pic_1 = PhotoImage(file='pic/1.png')
button6 = Button(root, width=50, text='開始查詢', height=62, state='disabled', image=pic_1, command=search) # 'normal' btn1['state'] = 'disabled'
button6.grid(row=6, column=6, sticky=W, padx=20)
pic_2 = PhotoImage(file='pic/91.png')
button61 = Button(root, width=50, text='打開文件', height=62, state='disabled', image=pic_2, command=openfile)
button61.grid(row=6, column=7, sticky=W, padx=20)
pic_3 = PhotoImage(file='pic/8.png')
button62 = Button(root, width=50, text='打開路徑', height=62, state='disabled', image=pic_3, command=opendir)
button62.grid(row=6, column=8, sticky=W, padx=20)

# 第7行
ft1 = tkFont.Font(family='Fixdsys', size=10)
label7= Label(root, text='使用\\n說明', width=8, font=ft1)#, anchor='w'
label7.grid(row=7, column=1, columnspan=2, sticky=N)
label71 = Label(root, text='開始\\n查詢', width=8, font=ft1)
label71.grid(row=7, column=6, sticky=N)

label72 = Label(root, text='打開\\n文件', width=8, font=ft1)
label72.grid(row=7, column=7, sticky=N)
label73 = Label(root, text='打開\\n文件夾', width=8, font=ft1)
label73.grid(row=7, column=8, sticky=N)#, sticky=S

# 第8行
ft2 = tkFont.Font(family='Fixdsys', size=8)
label8 = Label(root, text='------版本: bmy-001-----', width=20, font=ft2)
label8.grid(row=8, column=7, columnspan=2)#, sticky=S/<comboboxselected>/<comboboxselected>/<comboboxselected>/<comboboxselected>/<comboboxselected>/<comboboxselected>/<comboboxselected>/<code>

通過上面的代碼,界面就繪製完成了。我們需要對在界面上進行的數據操作結果保存下來,作為後續爬蟲及文件保存操作的依據。共需傳遞7個數據,首先定義一個空字典準備用來裝數據。

<code>data = {
'city': '',
'type': '',
'begintime': '',
'endtime': '',
'minprice': '',
'maxprice': '',
'dir': ''}/<code>

二、通過界面逐個賦值:

1、city,下拉框選擇要查詢的城市,

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

<code>def cit(event):
global data
text = city[cl1.get()]
label11.config(text=text[3])
data['city'] = city[cl1.get()]
set_state()/<code>

第一步繪製界面時,繪製城市選擇下拉框時,給其設置了當選擇數據後,觸發cit函數塊。

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

data['city'] = city[cl1.get()]:將下拉框中選定的城市名寫入到data字典裡的city項下。此時,若我們選擇了杭州,則data的結果就變成:

<code>data = {
'city': '杭州',
'type': '',
'begintime': '',
'endtime': '',
'minprice': '',
'maxprice': '',
'dir': ''}/<code>

2、'type',多選框選擇要查詢的是施工、還是設計、還是監理

<code>def type():
msg = []
if h1.get() == True:
msg.append("施工")
if h2.get() == True:
msg.append("設計")
if h3.get() == True:
msg.append("監理")
label22.config(text = msg)
data['type'] = msg
set_state()/<code>

繪製界面時,也設置了選中複選框後觸發的函數

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

將當前選擇結果及時在後面顯示出來的代碼:

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

<code>label22.config(text = msg)  # abe/<code>

3、起止日期設置方法同1

4、'minprice': '', 'maxprice': '',通過輸入框entry設置最小金額和最大金額,此時,我們需要控制輸入框只能輸入數字:

<code>def minpri(var51):
try:
edit51.get() == '' or float(edit51.get()) # 獲取輸入框的值,轉為浮點數,如果不能轉,責捕獲異常

data['minprice'] = edit51.get() # 如果輸入的是數值,則寫入data字典minprice鍵下
set_state()
except:
edit51.delete(len(edit51.get())-1)
messagebox.showwarning('警告', '請輸入數字')
def maxpri(var52):
try:
edit52.get() == '' or float(edit52.get()) # 同上
data['maxprice'] = edit52.get()
set_state()
except:
edit52.delete(len(edit52.get())-1)
messagebox.showwarning('警告', '請輸入數字')/<code>

5、dir:通過按鈕點擊觸發函數,打開選擇文件對話框,設置文件保存路徑。

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

<code>def setpath():
path = './'
fname = filedialog.asksaveasfilename(title=u'選擇文件', filetypes=[("excel07", ".xlsx")],
initialdir=(os.path.expanduser(path)))
if fname != '':
if not fname[-5:] == '.xlsx':
fname = fname + '.xlsx'
label53.config(text=fname)# 將對話框設置的路徑更新在後面的label53裡
data['dir'] = label53['text'] # 將標籤label53的內容寫入data字典的dir鍵下
set_state()/<code>

6、按鈕狀態控制

<code>def set_state():
bl = 1
for i in data:
if data[i] == '' or data[i] == []: # 逐個判斷data字典裡鍵下的值,當全部都有值後,按鈕狀態設置為正常
bl = 0
break
if bl == 1:
button6['state'] = 'normal'
else:
button6['state'] = 'disabled'/<code>
python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

7處紅線位置均有數據,開始查詢按鈕變為可點擊狀態

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

計算出結果後,文件和文件夾按鈕變為可點擊狀態

三、開始查詢代碼:

<code>def search():
if datetime.strptime(data['endtime'], '%Y-%m-%d') < datetime.strptime(data['begintime'], '%Y-%m-%d'):
messagebox.showwarning('警告', '起止日期不合理')
else:
info = zbw.getinfo(data) #調用爬蟲代碼,獲得數據
if len(info) == 1:
messagebox.showwarning('對不起, 沒找到信息,請更換範圍查找')
else:
tt = '小助手共為您找到{}條信息:'.format(len(info)-1) + '\\n'
for j in range(1, len(info)):
if j <= 4:
tt += '\\n' + ' ' + str(info[j][0]) + '、' + info[j][1][0:10] + '...: ' + str(info[j][3]) + '\\n'
else:
break
result.config(text=tt, anchor='w')
zbw.save(info) #調用操作excel代碼,將數據保存在excel文件裡
button61['state'] = 'normal' # 設置打開文件按鈕狀態
button62['state'] = 'normal'# 設置打開文件夾按鈕狀態
messagebox.showwarning('查詢', tt)/<code>

用os.startfile打開幫助文件和excel文件

<code>def openfile():
os.startfile(label53['text'])

def opendir():
os.startfile(os.path.dirname(label53['text']))

def shuoming():
os.startfile('使用說明.txt')/<code>

四、爬蟲部分

案例將爬取數據功能和保存為excel功能封裝在zbw.py文件裡。

<code>def getinfo(data):
url = data['city'][0]
params = data['city'][1]
typename = data['type'][0]
minprice = data['minprice']
maxprice = data['maxprice']
begintime = datetime.strptime(data['begintime'], '%Y-%m-%d')
endtime = datetime.strptime(data['endtime'], '%Y-%m-%d')
dir = data['dir']
response = requests.post(url, params=params).json()
n = 1
info = [[dir,typename,data['city'][3]]]
for i in range(len(response)):
url1 = data['city'][2] + response[i]['SEGMENTSHOWID']
response1 = requests.get(url1).content
soup = BeautifulSoup(response1, 'lxml', from_encoding='gbk')
SegmentName = soup.find('span', id="SegmentName").text
BuildUnitName = soup.find('span', id="BuildUnitName").text
ProjectTypeName = soup.find('span', id="ProjectTypeName").text
Tzze = soup.find('span', id="Tzze").text
CreateTime = soup.find('span', id='CreateTime').text
Enterprise = soup.find('span', id='Enterprise').text

info1 = []
ct = datetime.strptime(CreateTime, '%Y-%m-%d')

if typename == ProjectTypeName and float(maxprice) > float(Tzze) >= float(minprice) and endtime > ct > begintime:
info1.append((n))
info1.append(SegmentName)
info1.append(BuildUnitName)
info1.append(float(Tzze))
info1.append(ProjectTypeName)
info1.append(CreateTime)
info1.append(Enterprise)
info1.append(url1)
info.append(info1)
n += 1
return info
/<code>

五、數據保存為excel

<code>def save(info):
if len(info) == 1:
print('noinfo')

return ''
else:
wb = xlsxwriter.Workbook(info[0][0])
sht = wb.add_worksheet(info[0][1])
bold = wb.add_format({'bold': 1, 'font_size':12, 'text_wrap': True, 'align': 'center', 'valign': 'vcenter'})
align = wb.add_format({'align': 'center', 'font_size':10, 'valign': 'vcenter', 'text_wrap': True})
head = ['序號', '項目名稱', '建設單位', '投資總額', '類型', '日期', '資質要求', '詳情', info[0][2]]
sht.write_row('A1' , head, bold)
sht.set_row(0, height=30)
for row in range(2, len(info) + 1):
sht.write_row('A'+ str(row), info[row-1], align)
sht.set_row(row-1, height=40)

sht.set_column(0, 0, width=6)
sht.set_column(1, 2, width=20)
sht.set_column(3, 5, width=10)
sht.set_column(6, 7, width=11.5)


chart = wb.add_chart({'type': 'column'})
chart.add_series({
'name': [info[0][1], 0, 3],
'categories': [info[0][1], 1, 0, len(info)-1, 0],
'values': [info[0][1], 1, 3, len(info)-1, 3],
'line': {'color': 'red'}
})
chart.set_title({'name': '項目投資總額比較'})
chart.set_x_axis({'name': '報建日期'})
chart.set_y_axis({'name': '投資總額'})
chart.set_style(1)
sht.insert_chart('I2', chart, {'x_offset':5, 'y_offset':5})
wb.close()/<code>

保存的excel文件內容為:

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

點擊excel表中詳情列網址,能直接進入下圖的網站詳細頁面

python爬蟲小項目開發實戰,tkinter+爬蟲+xlsxwriter生成圖表

六、後語

本文案例是本人編寫的第一個python程序,敬請留言交流。


分享到:


相關文章: