慣例,美女鎮(zhèn)樓!!

最近發(fā)現(xiàn)一個
python異步IO支持庫挺不錯的,就是號稱可以支持百萬級別并發(fā)的asyncio(真實性沒驗證過)。asyncio是Python3.4版本引入的標準庫,直接內(nèi)置了對異步IO的支持。asyncio的編程模型就是一個消息循環(huán)。我們從asyncio模塊中直接獲取一個EventLoop的引用,然后把需要執(zhí)行的協(xié)程扔到EventLoop中執(zhí)行,就實現(xiàn)了異步IO。異步編程相對于同步編程來說要復雜很多,主要是因為異步編程一般很難監(jiān)測程序的實時執(zhí)行情況,所以需要付出更多的努力在使用回調(diào)函數(shù)上。
下面簡單的demo個栗子來說明下asyncio庫使用的關鍵字及作用,作為比較我們先給出一個同步編程的栗子作為對比。
# 同步模式
import requests
def demo():
return requests.get('https://www.baidu.com')
print(demo())
接下來我們再來看看asyncio模式:
import asyncio,aiohttp
async def demo():
async with aiohttp.ClientSession() as session:
response = await session.get('https://www.baidu.com')
content = await response.read()
return content
loop = asyncio.get_event_loop()
loop.run_until_complete(demo())
好了,一對比發(fā)現(xiàn),臥槽,同樣是簡單的顯示網(wǎng)頁內(nèi)容,asyncio模式的代碼比普通的同步模式要復雜多了,而且還出現(xiàn)了一堆什么async、asyncio、aiohttp、await等關鍵字,搞得頭都大?。e急,慢慢來分析,使用async以及await關鍵字將函數(shù)異步化。在demo()中實際上有兩個異步操作:首先異步獲取網(wǎng)站響應,然后再通過異步讀取響應的內(nèi)容,當然使用read()還可以讀取圖像等內(nèi)容。
上面的代碼中,我們創(chuàng)建了一個ClientSession 對象命名為session,然后通過session的get方法得到一個 ClientResponse對象,命名為session,get方法中傳入了一個必須的參數(shù)url,就是要獲得源碼的http url。至此便通過協(xié)程完成了一個異步IO的get請求。
aiohttp推薦使用ClientSession作為主要的接口發(fā)起請求,ClientSession允許在多個請求之間保存cookie以及相關對象信息。Session(會話)在使用完畢之后需要關閉,關閉Session是另一個異步操作,所以每次你都需要使用async with關鍵字。一旦你建立了客戶端session,你可以用它發(fā)起請求。這里是又一個異步操作的開始。上下文管理器的with語句可以保證在處理session的時候,總是能正確的關閉它。要讓你的程序正常的跑起來,你需要將他們加入事件循環(huán)中。所以你需要創(chuàng)建一個asyncio loop的實例, 然后將任務加入其中。
假如我們將demo中的content改為content=response.read()那么結果會怎樣?結果可能是你期待獲得響應對象,但是你得到的是一組生成器。這是因為response.read()是一個異步操作,這意味著它不會立即返回結果,僅僅返回生成器。這些生成器需要被調(diào)用跟運行,但是這并不是默認行為。在Python34中加入的yield from以及Python35中加入的await便是為此而生。它們將迭代這些生成器。以上代碼只需要在response.read()前加上await關鍵字即可修復。
訪問多個鏈接
上面介紹的都是簡單的一個url,如果我們要順序的訪問多個url該如何操作呢?先來看看同步模式是如何操作的:
for url in urls:
print(requests.get(url).text)
看起來是不是很簡單,so easy!!但是,如果采用asyncio異步模式就沒這么簡單了。來看看asyncio模式下是如何實現(xiàn):
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(url) for url in urls]
loop.run_until_complete(asyncio.wait(tasks))
看起來代碼也很短,但是卻出現(xiàn)了一堆函數(shù),都不知道這些函數(shù)是干嘛的?,F(xiàn)在我們來分析慢慢分析下。
首先,創(chuàng)建一個事件循環(huán)實例對象,接著我們要創(chuàng)建個tasks任務列表,這里我們使用的是列表表達式來構建一個tasks列表。不過我們需要將tasks中的每個元素包裝在asyncio的Future對象中,然后將Future對象列表作為任務傳遞給事件循環(huán)。這樣就可以順序訪問多個url了。
胡亂分析了一波,算了,還是來個實例吧!
爬蟲實例
這次我們選取的網(wǎng)站是unsplash,這個網(wǎng)站上的圖片質(zhì)量還是挺高的,而我們這次采取的爬取內(nèi)容方式也不是直接分析網(wǎng)頁內(nèi)容然后通過xpath定位元素獲取想要的內(nèi)容,而是通過在請求加載過程中分析Network信息獲取api信息,直接通過api查詢關鍵字keyword獲取內(nèi)容。
- 分析
Network獲取api信息
在chrome瀏覽器中輸入https://www.unsplash.com點擊進入unsplash網(wǎng)站,再點擊電腦上的F12按鍵進入調(diào)試模式,在調(diào)試模式界面選中上面的Network一欄
- 在
unsplash網(wǎng)站界面的search框中輸入查詢關鍵字,比如美女,按電腦上的F5鍵刷新網(wǎng)頁,注意Network下的變化,通過分析找到api請求信息為https://unsplash.com/napi/search/photos?query=%E7%BE%8E%E5%A5%B3&xp=&per_page=20&page=1
- 通過對
api請求信息的分析我們可以發(fā)現(xiàn)請求的時候還需要附帶四個參數(shù)項
params={
'xp':'',
'per_page':20,
'page':1,
'query':str
}
點擊分析得到的網(wǎng)址會發(fā)現(xiàn)返回的是一個json格式的數(shù)據(jù)。接下來我們就可以通過之前的分析得到的信息進行code了。

是不是看著這一大串信息頭都大?。縿e急,告訴你一個好的在線json格式查看工具
json editor online,看看格式化后信息如何:
這下
json信息是不是看起來清爽很多了??!我們關心的是 results信息,再點擊results看看,
擼碼
本著面向對象編程的思想,我們將這個爬蟲程序封裝成一個類Spider,代碼如下:
class Spider(object):
def __init__(self, query):#query為搜索的關鍵字
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) '
'Chrome/22.0.1216.0 Safari/537.2',
}
self.num = 1
self.query = query
if self.query not in os.listdir('.'):
os.mkdir(self.query)
self.path = os.path.join(os.path.abspath('.'), self.query)
os.chdir(self.path) #切換到目標目錄
def get_page_info(self, page):
'''獲取json內(nèi)容'''
url = 'https://unsplash.com/napi/search/photos'
data = {
'page': page,
'query': self.query,
'per_page': 20,
'xp':'',
}
response = requests.get(url, params=data)
if response.status_code == 200:#成功返回信息
return response.json()
else:
print('請求失敗,狀態(tài)碼為{}'.format(response.status_code))
async def get_content(self, link):
'''獲取link相應的內(nèi)容'''
async with aiohttp.ClientSession() as session:
response = await session.get(link)
content = await response.read()
return content
async def download_img(self, img):
'''通過圖片下載地址下載圖片'''
content = await self.get_content(img[1])
with open(img[0] + '.jpg', 'wb') as f:
f.write(content)
print('下載第{}張圖片成功!'.format(self.num))
self.num += 1
def run(self):
start = time.time()#記錄起始時間戳
for i in range(1, 11):
results = self.get_page_info(i)['results']
print('hahha ....',results)
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(self.download_img((link['id'], link['links']['download']))) for link in
results]
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()#獲取結束時間戳
print('共運行了{}秒'.format(end - start))#程序耗時
至此,一個爬蟲類所需的功能就封裝完成了,下面就可以通過使用這個爬蟲類在unsplash網(wǎng)站上爬取圖片了。
def main():
query = input('Please input you want to search keywords: ')
spider = Spider(query)
spider.run()
if __name__ == '__main__':
main()
爬蟲運行后Terminal顯示的信息如下:

自動設置電腦壁紙
接下來我們換個玩法,我們將爬蟲爬取的圖片設置為電腦桌面,每隔5分鐘自動更換一次電腦桌面圖片。
用python設置電腦桌面壁紙很簡單,這些工作主要借助為win32api和win32gui這兩個內(nèi)置模塊,我們一起來看具體代碼:
def set_wallpaper(img_path):
# 打開指定注冊表路徑
reg_key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, win32con.KEY_SET_VALUE)
# 最后的參數(shù):2拉伸,0居中,6適應,10填充,0平鋪
win32api.RegSetValueEx(reg_key, "WallpaperStyle", 0, win32con.REG_SZ, "2")
# 最后的參數(shù):1表示平鋪,拉伸居中等都是0
win32api.RegSetValueEx(reg_key, "TileWallpaper", 0, win32con.REG_SZ, "0")
# 刷新桌面 win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, img_path, win32con.SPIF_SENDWININICHANGE)
print('now change desktop wallpaper')
上面的方法傳入的是壁紙的絕對路徑,如何獲取這個絕對路徑img_path呢?很簡單,python中的os模塊有個walk方法,這個方法傳入的是一個目錄路徑,參考代碼如下(附注釋):
def show_walk(dir_path):
for root,dirs,files in os.walk(dir_path, topdown=False):
for name in files:
print(os.path.join(root, name))
for name in dirs:
print(os.path.join(root, name))
# 返回的是一個三元tupple(root, dirs, files),其中第一個為起始路徑,第二個為起始路徑下的文件夾,第三個是起始路徑下的文件。
# root是一個string,代表目錄的路徑,
# dirs是一個list,包含了dirpath下所有子目錄的名字,
# files是一個list,包含了非目錄文件的名字,這些名字不包含路徑信息。如果需要得到全路徑,需要使用 os.path.join(root, name)。
接下來我們就利用os.walk來獲取上面通過爬蟲爬取的圖片的絕對路徑并返回一個包含該目錄下的所有圖片的路徑的列表,參考代碼如下:
def get_img_path():
print(os.getcwd()) #獲取當前路徑
path = os.path.join(os.getcwd(),'desktop-background')# desktop-background是爬蟲程序里輸入的query查詢參數(shù)
print(path)
img_path = []
for root, dirs, files in os.walk(path):
for file in files:
img_path.append(os.path.join(root,file))
return img_path
最后來實現(xiàn)每隔5分鐘自動更新電腦桌面壁紙功能,主要是通過random.choice()方法傳入圖片地址列表隨機獲取上面返回的圖片路徑中的任意一張圖片的路徑來設置電腦桌面壁紙。具體的代碼參考如下:
def auto_change_wallpaper():
img_path = get_img_path()
time_intvl = 5*60
start_time = int(time.time())
print(start_time)
while True:
end_time = int(time.time())
cost_time = end_time-start_time
if cost_time == time_intvl:
set_wallpaper(random.choice(img_path))
start_time = end_time
else:
pass
附上這個自動更換桌面壁紙的完整code:
#!/usr/bin/env python
#-*-coding:utf-8-*-
import random
import win32api,win32gui,win32con
import time,os
def set_wallpaper(img_path):
# 打開指定注冊表路徑
reg_key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, "Control Panel\\Desktop", 0, win32con.KEY_SET_VALUE)
# 最后的參數(shù):2拉伸,0居中,6適應,10填充,0平鋪
win32api.RegSetValueEx(reg_key, "WallpaperStyle", 0, win32con.REG_SZ, "2")
# 最后的參數(shù):1表示平鋪,拉伸居中等都是0
win32api.RegSetValueEx(reg_key, "TileWallpaper", 0, win32con.REG_SZ, "0")
# 刷新桌面
win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, img_path, win32con.SPIF_SENDWININICHANGE)
print('now change desktop wallpaper')
def get_img_path():
print(os.getcwd())
path = os.path.join(os.getcwd(),'desktop-background')
print(path)
img_path=[]
for root, dirs, files in os.walk(path):
for file in files:
img_path.append(os.path.join(root,file))
return img_path
def auto_change_wallpaper():
img_path=get_img_path()
time_intvl=5*60
start_time = int(time.time())
print(start_time)
while True:
end_time = int(time.time())
cost_time = end_time-start_time
if cost_time == time_intvl:
set_wallpaper(random.choice(img_path))
start_time = end_time
else:
pass
def main():
auto_change_wallpaper()
if __name__ == '__main__':
main()
好,瞎扯了這么多,就寫到這里吧!


