今天玩點新鮮的,使用一個新庫 aiohttp
,利用它提升咱爬蟲的爬取速度。html
安裝模塊常規套路python
pip install aiohttp
運行以後等待,安裝完畢,想要深造,那麼官方文檔必備 :https://aiohttp.readthedocs.io/en/stable/正則表達式
接下來就能夠開始寫代碼了。json
咱們要爬取的頁面,這一次選取的是服務器
http://bbs.fengniao.com/forum/forum_101_1_lastpost.html
打開頁面,咱們很容易就獲取到了頁碼session
很久沒有這麼方便的看到頁碼了。併發
嘗試用 aiohttp
訪問這個頁面吧,模塊的引入,沒有什麼特殊的,採用 import
便可
若是咱們須要 使用Asyncio + Aiohttp
異步IO 編寫爬蟲,那麼須要注意,你須要異步的方法前面加上async
less
接下來,先嚐試去獲取一下上面那個地址的網頁源碼。異步
代碼中,先聲明一個fetch_img_url的函數,同時攜帶一個參數,這個參數也能夠直接寫死。async
with
上下文不在提示,自行搜索相關資料便可 (`・ω・´)
aiohttp.ClientSession() as session:
建立一個session
對象,而後用該session
對象去打開網頁。session
能夠進行多項操做,好比post
, get
, put
等
代碼中 await response.text()
等待網頁數據返回
asyncio.get_event_loop
建立線程,run_until_complete
方法負責安排執行 tasks
中的任務。tasks
能夠爲單獨的函數,也能夠是列表。
import aiohttp import asyncio async def fetch_img_url(num): url = f'http://bbs.fengniao.com/forum/forum_101_{num}_lastpost.html' # 字符串拼接 # 或者直接寫成 url = 'http://bbs.fengniao.com/forum/forum_101_1_lastpost.html' print(url) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400', } async with aiohttp.ClientSession() as session: # 獲取輪播圖地址 async with session.get(url,headers=headers) as response: try: html = await response.text() # 獲取到網頁源碼 print(html) except Exception as e: print("基本錯誤") print(e) # 這部分你能夠直接臨摹 loop = asyncio.get_event_loop() tasks = asyncio.ensure_future(fetch_img_url(1)) results = loop.run_until_complete(tasks)
上面代碼最後一部分也能夠寫成
loop = asyncio.get_event_loop() tasks = [fetch_img_url(1)] results = loop.run_until_complete(asyncio.wait(tasks))
好了,若是你已經成果的獲取到了源碼,那麼距離最終的目的就差那麼一丟丟了。
修改代碼爲批量獲取10頁。
只須要修改tasks
便可,在此運行,看到以下結果
tasks = [fetch_img_url(num) for num in range(1, 10)]
下面的一系列操做和上一篇博客很是相似,找規律。
隨便打開一個頁面
http://bbs.fengniao.com/forum/forum_101_4_lastpost.html
點擊一張圖片,進入內頁,在點擊內頁的一張圖片,進入到一個輪播頁面
再次點擊進入圖片播放頁面
最後咱們在圖片播放頁面,找到源碼中發現了全部的圖片連接,那麼問題出來了,如何從上面的第一個連接,轉變成輪播圖的連接???
下面的源碼是在 http://bbs.fengniao.com/forum/pic/slide_101_10408464_89383854.html
右鍵查看源碼。
繼續分析吧~~~~ ヾ(=・ω・=)o
http://bbs.fengniao.com/forum/forum_101_4_lastpost.html 轉變成下面的連接? http://bbs.fengniao.com/forum/pic/slide_101_10408464_89383854.html
繼續看第一個連接,咱們使用F12開發者工具,去抓取一個圖片看看。
圖片中標黃色框的位置,發現了咱們想要的數字,那麼好了,咱們只須要經過正則表達式把他們匹配出來就行了。
代碼在下面####
的位置,須要注意的是,我採用的原始的正則匹配,在編寫正則表達式的過程當中,我發現一步居然沒有完整匹配,只能分紅兩個步驟了,你能夠看一下具體的細節o(╥﹏╥)o
<div class="picList">
async def fetch_img_url(num): url = f'http://bbs.fengniao.com/forum/forum_101_{num}_lastpost.html' print(url) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400', } async with aiohttp.ClientSession() as session: # 獲取輪播圖地址 async with session.get(url,headers=headers) as response: try: ############################################### url_format = "http://bbs.fengniao.com/forum/pic/slide_101_{0}_{1}.html" html = await response.text() # 獲取到網頁源碼 pattern = re.compile('<div class="picList">([\s\S.]*?)</div>') first_match = pattern.findall(html) href_pattern = re.compile('href="/forum/(\d+?)_p(\d+?)\.html') urls = [url_format.format(href_pattern.search(url).group(1), href_pattern.search(url).group(2)) for url in first_match] ############################################## except Exception as e: print("基本錯誤") print(e)
代碼完成,咱們已經獲取到,咱們想要的URL了,下面繼續讀取URL內部信息,而後匹配咱們想要的圖片連接
async def fetch_img_url(num): # 去抄上面的代碼 async with aiohttp.ClientSession() as session: # 獲取輪播圖地址 async with session.get(url,headers=headers) as response: try: #去抄上面的代碼去吧 ################################################################ for img_slider in urls: try: async with session.get(img_slider, headers=headers) as slider: slider_html = await slider.text() # 獲取到網頁源碼 try: pic_list_pattern = re.compile('var picList = \[(.*)?\];') pic_list = "[{}]".format(pic_list_pattern.search(slider_html).group(1)) pic_json = json.loads(pic_list) # 圖片列表已經拿到 print(pic_json) except Exception as e: print("代碼調試錯誤") print(pic_list) print("*"*100) print(e) except Exception as e: print("獲取圖片列表錯誤") print(img_slider) print(e) continue ################################################################ print("{}已經操做完畢".format(url)) except Exception as e: print("基本錯誤") print(e)
圖片最終的JSON已經拿到,最後一步,下載圖片,噹噹噹~~~~,一頓迅猛的操做以後,圖片就拿下來了
async def fetch_img_url(num): # 代碼去上面找 async with aiohttp.ClientSession() as session: # 獲取輪播圖地址 async with session.get(url,headers=headers) as response: try: # 代碼去上面找 for img_slider in urls: try: async with session.get(img_slider, headers=headers) as slider: # 代碼去上面找 ########################################################## for img in pic_json: try: img = img["downloadPic"] async with session.get(img, headers=headers) as img_res: imgcode = await img_res.read() # 圖片讀取 with open("images/{}".format(img.split('/')[-1]), 'wb') as f: f.write(imgcode) f.close() except Exception as e: print("圖片下載錯誤") print(e) continue ############################################################### except Exception as e: print("獲取圖片列表錯誤") print(img_slider) print(e) continue print("{}已經操做完畢".format(url)) except Exception as e: print("基本錯誤") print(e)
圖片會在你提早寫好的images
文件夾裏面快速的生成
tasks
最多能夠開1024協程,可是建議你開100個就OK了,太多併發,人家服務器吃不消。
以上操做執行完畢,在添加一些細節,好比保存到指定文件夾,就OK了。