Python協程爬取妹子圖(內有福利,你懂得~)

項目說明:css

  一、項目介紹html

     本項目使用Python提供的協程+scrapy中的選擇器的使用(至關好用)實現爬取妹子圖的(福利圖)圖片,這個學會了,某榴什麼的、pow(2, 10)是吧!shell

  二、用到的知識點編程

     本項目中會用到如下知識點windows

    ① Python的編程(本人使用版本3.6.2)瀏覽器

    ② 使用scrapy中的css選擇器app

    ③ 使用async協程異步

    ④ 使用aiohttp異步訪問urlscrapy

    ⑤ 使用aiofiles異步保存文件async

  三、 項目效果圖

            

 

項目實現:

  咱們最終的目的是把圖片的標題替換成須要保存的目錄,下面的圖片呢,就按着網頁上圖片的名稱保存~,有了這個需求之後,ok,社會我demon哥,人很話很少,開幹!

   咱們須要網站的入口,入口以下~就爬取萌妹子吧!

   妹子圖中萌妹分類的網站入口:http://www.meizitu.com/a/cute.html  

   打開萌妹子的入口連接之後,咱們須要分析下網頁中結構,而後經過分析頁面,獲取咱們有用的內容:

   經過入口咱們得知,url地址中,有兩個咱們須要關係的點,一個是妹子圖的妹子類型,一個是要獲取頁面的頁碼,若是獲取多頁的話,也就是替換成不一樣的頁碼便可(圖以下)

     

   分析完上面的頁面之後,咱們在來分析當前頁中須要提取的信息 ,使用Chrome瀏覽器打開開發者模式(windows是F12,MacOS是command+option+i)

    

    點擊剛剛選中妹子的url的地址,咱們在來分析這裏面的有用信息

      

   信息提取就到這裏,咱們下面須要使用css選擇器,提取url而後開始寫方法,來下載這些圖片

 

   沒有安裝的scrapy的趕忙去pip3 install scrapy一下,要麼您老就右上角的小叉叉退出吧~ 否則沒辦法進行了!

    Scrapy提供一個Shell的參數命令了,在這個參數後面加上你要提取頁面中的url地址,就能夠進入到scrapy shell中,在裏面能夠經過css xpath選擇器調試提取信息,用法以下:

    在終端輸入: scrapy shell http://www.meizitu.com/a/cute.html

    

   出現上面的便可,這裏面有個response,咱們能夠經過response.css或者reponses.xpath獲取url的數據,ok..我這裏使用css來提取,爲嘛?! 簡單唄~

    css的具體語法嘛~~你們不會的話,能夠自行百度,或者去菜鳥站補一補知識,我這人比較懶,我就不講了!直接告訴大家怎麼提取吧~能夠經過Chrome給我提供的開發者工具來獲取css選擇器的表達式,請看下圖

     

   上面圖的圖很眼熟對吧,嗯,這是哪一個主頁圖,咱們須要在當前頁面中獲取全部妹子的url地址,而後在進入到每一個妹子的url地址中獲取這個妹子的全部圖片!首先先來獲取當前頁面的全部妹子的url地址,切換到scrap shell中,經過response.css來提取信息

   提取妹子的url地址:  response.css('#maincontent a::attr(href)').extract()

  

   嘿~,當前頁面的中的全部妹子的url都有了,那就好辦了呀,在進入這些地址中逐個獲取妹子獨立頁面中的url地址,而後下載就好咯!可是,你們有木有發現,這些頁面中的url有重複的,怎麼辦呢,用set能夠去重哦,先來寫個獲取當前頁面的簡單的方法,一會咱們在修改這個方法。

 1 import requests
 2 from scrapy import Selector
 3 
 4 
 5 def get_page_items(*, start_page_num: int=1, end_page_num: int=2, step: int=1):
 6     items = []
 7     for page_num in range(start_page_num, end_page_num, step):
 8         base_url = 'http://www.meizitu.com/a/{genre}_{page_num}.html'
 9         req = requests.get(base_url.format(genre='cute', page_num=1))
10         content = req.content.decode('gbk')
11         selector = Selector(text=content)
12         item_urls = list(set(selector.css('#maincontent a::attr(href)').extract()))
13         items.extend(url for url in item_urls if url.startswith('http://www.meizitu.com/a/'))
14     return items
15 
16 
17 print(get_page_items())

上面的代碼能夠供咱們拿下指定頁面中的全部漂亮小姐姐的url地址哦~,有了這些漂亮小姐姐的url,進入這個url之後,在提取小姐姐頁面的全部url就能夠下載啦~!

 1 import requests
 2 from scrapy import Selector
 3 
 4 
 5 def get_page_items(*, start_page_num: int=1, end_page_num: int=2, step: int=1):
 6     items = []
 7     for page_num in range(start_page_num, end_page_num, step):
 8         base_url = 'http://www.meizitu.com/a/{genre}_{page_num}.html'
 9         req = requests.get(base_url.format(genre='cute', page_num=1))
10         content = req.content.decode('gbk')
11         selector = Selector(text=content)
12         item_urls = list(set(selector.css('#maincontent a::attr(href)').extract()))
13         items.extend(url for url in item_urls if url.startswith('http://www.meizitu.com/a/'))
14     return items
15 
16 
17 def get_images(item):
18     req = requests.get(item)
19     content = req.content.decode('gbk')
20     selector = Selector(text=content)
21     image_urls = list(set(selector.css('#maincontent p img::attr(src)').extract()))
22     print(image_urls)
23 
24 
25 for item in get_page_items():
26     get_images(item)

上面代碼執行的結果爲:

能夠看到的效果,全部小姐姐的下載圖片的地址都已經拿到了,可是上面的代碼有兩個問題,聰明的小夥伴,可能已經發現了,上面代碼的重合性過高,那些獲取url的咚咚,均可以整合,在下面的一版,咱們來改寫這個函數,有了這些圖片的地址,咱們只須要調取某個函數或者方法,來下載這些圖片保存到本地便可,怎麼玩?! 往下看.....

 1 # _*_coding: utf-8_*_
 2 import os
 3 from time import perf_counter
 4 from functools import wraps
 5 
 6 import requests
 7 from scrapy import Selector
 8 """
 9 -------------------------------------------------
10    File Name:     妹子圖_串行
11    Description :
12    Author :        demon
13    date:          06/10/2017
14 -------------------------------------------------
15    Change Activity:
16                    06/10/2017:
17 -------------------------------------------------
18 """
19 __author__ = 'demon'
20 
21 
22 def timer(func):
23     """
24     :param func: 裝飾器的函數,記錄方法所消耗的時間
25     :return:
26     """
27     @wraps(func)
28     def wrapper(*args, **kwargs):
29         start_time = perf_counter()
30         result = func(*args, **kwargs)
31         end_time = perf_counter()
32         cls_name = func.__name__
33         fmt = '{cls_name} {args} spend time: {time:.5f}'
34         print(fmt.format(cls_name=cls_name, args=args, time=end_time - start_time))
35         return result
36     return wrapper
37 
38 
39 def get_content_css(url):
40     req = requests.get(url)
41     content = req.content.decode('gbk')
42     selector = Selector(text=content)
43     return selector
44 
45 
46 def get_page_items(*, start_page_num: int=1, end_page_num: int=2, step: int=1):
47     items = []
48     for page_num in range(start_page_num, end_page_num, step):
49         base_url = 'http://www.meizitu.com/a/{genre}_{page_num}.html'
50         selector = get_content_css(base_url.format(genre='cute', page_num=page_num))
51         item_urls = list(set(selector.css('#maincontent a::attr(href)').extract()))
52         items.extend(url for url in item_urls if url.startswith('http://www.meizitu.com/a/'))
53     return items
54 
55 
56 def get_images(item):
57     selector = get_content_css(item)
58     image_urls = list(set(selector.css('#maincontent p img::attr(src)').extract()))
59     dir_name = selector.css('#maincontent div.metaRight h2 a::text').extract_first()
60     'ok' if os.path.exists(dir_name) else os.mkdir(dir_name)
61     for url in image_urls:
62         download_image(dir_name, url)
63 
64 
65 @timer
66 def download_image(dir_name, image_url):
67     headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) '
68                              'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}
69     req = requests.get(image_url, headers=headers)
70     image = req.content
71     filename = image_url.rsplit('/', 1)[-1]
72     save_path = os.path.join(dir_name, filename)
73     with open(save_path, 'wb') as f:
74         f.write(image)
75 
76 
77 if __name__ == "__main__":
78     start = perf_counter()
79     for item in get_page_items():
80         get_images(item)
81     end = perf_counter()
82     print(format('end', '*^100'))
83     print('download all images cost time:{:.3f}'.format(end - start))

上面的代碼能夠保證圖片保存到本地,那麼基本的代碼邏輯沒有問題了,保存文件(download_image)也實現了~, 可是  可是這不是咱們想要的效果,這玩意很慢的,一個一個並行下來的,要TMD天荒地老呀!

臥槽,不能忍受呀,一個頁面就要用121秒的時間,這尼瑪的要是10頁20頁的不得瘋了呀!必定要改,改代碼,改爲協程~,如下是三頁的數據才用時190秒呀,提高了不是一點半點呀!

 

說幹就幹,改爲協程,直接上所有代碼吧!由於...我懶得...寫了,這篇博客...寫了將近五個小時了...臥槽!要瘋了~

  1 # _*_coding: utf-8_*_
  2 import os
  3 import asyncio
  4 from functools import wraps
  5 from time import perf_counter
  6 
  7 import aiohttp
  8 import aiofiles
  9 from scrapy import Selector
 10 """
 11 -------------------------------------------------
 12    File Name:     妹子圖
 13    Description :
 14    Author :        demon
 15    date:          06/10/2017
 16 -------------------------------------------------
 17    Change Activity:
 18                    06/10/2017:
 19 -------------------------------------------------
 20 """
 21 __author__ = 'demon'
 22 
 23 
 24 def timer(func):
 25     """
 26     :param func: 裝飾器的函數,記錄方法所消耗的時間
 27     :return:
 28     """
 29     @wraps(func)
 30     def wrapper(*args, **kwargs):
 31         start_time = perf_counter()
 32         result = func(*args, **kwargs)
 33         end_time = perf_counter()
 34         cls_name = func.__name__
 35         print('{cls_name} spend time: {time:.5f}'.format(cls_name=cls_name, time=end_time - start_time))
 36         return result
 37     return wrapper
 38 
 39 
 40 class MeiZiTuDownload:
 41     def __init__(self, *, genre: str='cute', start_page_num: int=1, end_page_num: int=5, step: int=1):
 42         self.base_url = 'http://www.meizitu.com/a/{genre}_{page_num}.html'
 43         self.start_num = start_page_num
 44         self.end_num = end_page_num
 45         self.step = step
 46         self.genre = genre
 47         self.headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) '
 48                                       'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}
 49 
 50     async def get_html_content(self, url: str):
 51         """
 52         :param url: 網頁的url地址
 53         :return:    網頁的html源碼
 54         """
 55         req = await aiohttp.request('GET', url, headers=self.headers)
 56         content = await req.read()
 57         content = content.decode('gbk')
 58         return content
 59 
 60     async def get_page_item(self, page_num: int):
 61         """
 62         :param page_num: 獲取網頁中的每一頁中的具體的url地址
 63         :return:
 64         """
 65         item_url = self.base_url.format(genre=self.genre, page_num=page_num)
 66         content = await self.get_html_content(item_url)
 67         selector = Selector(text=content)
 68         urls = list(set(selector.css('#maincontent a::attr(href)').extract()))
 69         page_items = (url for url in urls if url.startswith('http://www.meizitu.com/a/'))
 70         for item in page_items:
 71             await self.get_item(item)
 72 
 73     async def get_item(self, item: str):
 74         """
 75         :param item: 單獨的下載頁面
 76         :return:
 77         """
 78         item_content = await self.get_html_content(item)
 79         selector = Selector(text=item_content)
 80         dir_name = selector.css('#maincontent div.metaRight h2 a::text').extract_first()
 81         image_urls = selector.css('#picture p img::attr(src)').extract()
 82         'ok' if os.path.exists(dir_name) else os.mkdir(dir_name)
 83         for image_url in image_urls:
 84             image_name = image_url.rsplit('/', 1)[-1]
 85             save_path = os.path.join(dir_name, image_name)
 86             await self.download_images(save_path, image_url)
 87 
 88     async def download_images(self, save_path: str, image_url: str):
 89         """
 90         :param save_path: 保存圖片的路徑
 91         :param image_url: 圖片的下載的url地址
 92         :return:
 93         """
 94         req = await aiohttp.request('GET', image_url, headers=self.headers)
 95         image = await req.read()
 96         fp = await aiofiles.open(save_path, 'wb')
 97         await fp.write(image)
 98 
 99     async def __call__(self, page_num: int):
100         await self.get_page_item(page_num)
101 
102     def __repr__(self):
103         cls_name = type(self).__name__
104         return '{cls_name}{args}'.format(cls_name=cls_name, args=(self.genre, self.start_num, self.end_num, self.step))
105 
106 
107 if __name__ == "__main__":
108     start = perf_counter()
109     download = MeiZiTuDownload(genre='cute')
110     loop = asyncio.get_event_loop()
111     to_do = [download(num) for num in range(1, 4)]
112     wait_future = asyncio.wait(to_do)
113     resp, _ = loop.run_until_complete(wait_future)
114     loop.close()
115     end = perf_counter()
116     func_name = download.__class__.__name__
117     spend_time = end - start
118     print(format('end', '*^100'))
119     print('{func_name} spend time: {time:.5f}'.format(func_name=func_name, time=spend_time))

協程的使用,你們移步到廖大神的哪裏學習下吧~~~,我就不講了...否則我要瘋了...我要看會電影,緩一會。

廖大神博客地址:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432090171191d05dae6e129940518d1d6cf6eeaaa969000

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息