前言
在剛開始學習python的時候,有看到過迭代器和生成器的相關內容,不過當時并未深入了解,更談不上使用了,其實是可以用生成器來改造一下的,所以本次就使用生成器來優化一下爬蟲代碼
- 生成器函式與普通函式的區別是,生成器用關鍵字 yield 來回傳值,而普通函式用 return 一次性回傳值;
- 當你呼叫生成器函式的時候,函式內部的代碼并不立馬執行 ,這個函式只是回傳一個生成器物件;
- 一般使用for回圈迭代生成器物件來獲取具體的回傳值
什么時候可以使用生成器呢?
一般爬蟲經常會通過for回圈來迭代處理資料,例如我之前爬取20頁資料時,會先把獲得的資料存盤到一個串列或字典中,然后再把整個串列或字典 return 出去,然后保存資料至本地又會再呼叫這個串列獲取資料(其實做了2步:先把頁面的資料提取出來存到串列,后面用的時候再迭代串列);
類似這種直接使用串列或字典來存盤資料,其實是先存盤到了記憶體中,如果資料量過大的話,則會占用大量記憶體,這樣顯然是不合適的;
此時就可以使用生成器,我們每提取一條資料,就把該條資料通過 yield 回傳出去,好處是不需要提前把所有資料加載到一個串列中,而是有需要的時候才給它生成值回傳,沒呼叫這個生成器的時候,它就處于休眠狀態等待下一次呼叫
優化爬蟲代碼
首先看一下未使用生成器的代碼
# -*- coding:utf-8 -*-
import requests
from requests.exceptions import RequestException
import os, time
from lxml import etree
def get_html(url):
"""獲取頁面內容"""
response = requests.get(url, timeout=15)
# print(response.status_code)
try:
if response.status_code == 200:
# print(response.text)
return response.text
else:
return None
except RequestException:
print("請求失敗")
# return None
def parse_html(html_text):
"""決議一個結果頁的內容,提取圖片url"""
html = etree.HTML(html_text)
if len(html) > 0:
img_src = https://www.cnblogs.com/djdjdj123/p/html.xpath("//img[@class='photothumb lazy']/@data-original") # 提取圖片url,通過xpath提取會生成一個串列
# print(img_src)
return img_src # 將提取出來的圖片url串列回傳出去
else:
print("決議頁面元素失敗")
def get_all_image_url(depth):
"""
提取所有頁面的所有圖片url
:param depth: 爬取頁碼
:return:
"""
base_url = 'https://imgbin.com/free-png/naruto/' # 定義初始url
image_urls = []
for i in range(1, depth):
url = base_url + str(i) # 根據頁碼遍歷請求url
html = get_html(url) # 決議每個頁面的內容
# print(html)
if html:
list_data = parse_html(html) # 提取頁面中的圖片url
for img in list_data:
image_urls.append(img)
return image_urls
def get_image_content(url):
"""請求圖片url,回傳二進制內容"""
try:
r = requests.get(url, timeout=15)
if r.status_code == 200:
return r.content
return None
except RequestException:
return None
def main(depth=None):
"""
主函式,下載圖片
:param depth: 爬取頁碼
:return:
"""
j = 1
img_urls = get_all_image_url(depth) # 提取頁面中的圖片url
root_dir = os.path.dirname(os.path.abspath('.'))
save_path = root_dir + '/pics/' # 定義保存路徑
# print(img_urls)
# print(next(img_urls))
# print(next(img_urls))
for img_url in img_urls: # 遍歷每個圖片url
try:
file_path = '{0}{1}.{2}'.format(save_path, str(j), 'jpg')
if not os.path.exists(file_path): # 判斷是否存在檔案,不存在則爬取
with open(file_path, 'wb') as f:
f.write(get_image_content(img_url))
f.close()
print('第{}個檔案保存成功'.format(j))
else:
print("第{}個檔案已存在".format(j))
j = j + 1
except FileNotFoundError as e:
print("遇到錯誤:", e)
continue
except TypeError as f:
print("遇到錯誤:", f)
continue
if __name__ == '__main__':
start = time.time()
main(2)
end = time.time()
print(end-start)
-
parse_html()函式:它的作用決議一個結果頁的內容,提取一頁的所有圖片url(通過xpath提取,所以資料時存盤在一個串列中),可以把它改造為生成器;
-
get_all_image_url()函式:呼叫parse_html()函式,通過控制爬取頁碼,提取所有頁面的所有圖片url,然后存到一個串列中回傳出去,可以改造為生成器;
-
main()函式:呼叫get_all_image_url()函式得到所有圖片url的串列,然后迭代這個串列,來得到每一個圖片url來下載圖片
接下來要做的就是改造 parse_html()函式 和 get_all_image_url()函式
這個其實也比較簡單,只需要把原本要追加到串列中的東西通過 yield 關鍵字回傳出去就行了
parse_html()函式:
'''
遇到問題沒人解答?小編創建了一個Python學習交流群:778463939
尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書!
'''
def parse_html(html_text):
"""決議一個結果頁的內容,提取圖片url"""
html = etree.HTML(html_text)
if len(html) > 0:
img_src = https://www.cnblogs.com/djdjdj123/p/html.xpath("//img[@class='photothumb lazy']/@data-original")
# print(img_src)
for item in img_src:
yield item
get_all_image_url()函式
def get_all_image_url(depth):
"""
提取所有頁面的所有圖片url
:param depth: 爬取頁碼
:return:
"""
base_url = 'https://imgbin.com/free-png/naruto/' # 定義初始url
for i in range(1, depth):
url = base_url + str(i) # 根據頁碼遍歷請求url
html = get_html(url) # 決議每個頁面的內容
# print(html)
if html:
list_data = parse_html(html) # 提取頁面中的圖片url
for img in list_data:
yield img # 通過yield關鍵字回傳每個圖片的url地址
然后上面代碼中有個地方需要注意
-
for i in range(1, depth): 這個for回圈,是迭代爬取頁碼
-
list_data = https://www.cnblogs.com/djdjdj123/p/parse_html(html):呼叫parse_html()函式,獲取每一頁內容的生成器物件
-
for img in list_data: 迭代 list_data,然后通過yield img 把值回傳出去
get_all_image_url()函式 還可以用以下方式回傳結果
def get_all_image_url(depth):
"""
提取所有頁面的所有圖片url
:param depth: 爬取頁碼
:return:
"""
base_url = 'https://imgbin.com/free-png/naruto/' # 定義初始url
for i in range(1, depth):
url = base_url + str(i) # 根據頁碼遍歷請求url
html = get_html(url) # 決議每個頁面的內容
# print(html)
if html:
list_data = https://www.cnblogs.com/djdjdj123/p/parse_html(html) # 提取頁面中的圖片url
yield from list_data
使用關鍵字 yield from 替代了之前的內層for回圈,可以達到相同的效果(具體含義可以查看 Python yield from 用法詳解、yield from)
main()函式 不需要作改動,因為我們在呼叫生成器物件時,也是通過for回圈來提取里面的值的,所以這部分代碼和之前一樣
OK,本次代碼優化到此結束,python有太多東西要學啦,感覺自己懂得還是太少,要保持學習的心態,加油~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/101176.html
標籤:Python
