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

来源:http://www.cnblogs.com/demon89/archive/2017/10/06/7631656.html
-Advertisement-
Play Games

Python 協程爬取妹子圖~~~ async aiohttp scrapy ...


項目說明:

  1、項目介紹

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

  2、用到的知識點

     本項目中會用到以下知識點

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

    ② 使用scrapy中的css選擇器

    ③ 使用async協程

    ④ 使用aiohttp非同步訪問url

    ⑤ 使用aiofiles非同步保存文件

  3、 項目效果圖

            

 

項目實現:

  我們最終的目的是把圖片的標題替換成需要保存的目錄,下麵的圖片呢,就按著網頁上圖片的名稱保存~,有了這個需求以後,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

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JDBC(Java DataBase Connectivity,java資料庫連接)是一種用於執行SQL語句的Java API,可以為多種關係資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成。JDBC提供了一種基準,據此可以構建更高級的工具和介面,使資料庫開發人員能夠編寫資料庫應用程式, ...
  • 首先我們來瞭解下python中的進程,線程以及協程! 從電腦硬體角度: 電腦的核心是CPU,承擔了所有的計算任務。一個CPU,在一個時間切片里只能運行一個程式。 從操作系統的角度: 進程和線程,都是一種CPU的執行單元。 進程:表示一個程式的上下文執行活動(打開、執行、保存...) 線程:進程執 ...
  • matplotlib學習之函數積分圖 ...
  • 關於split()函數的簡單使用,分隔符在字元串中的位置不同,分割的不同情況。 ...
  • 單例模式顧名思義,就是只有一個實例,作為對象的創建模式,單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。 單例模式的三個要點: 1.某個類只能有一個實例。 2.必須自行創建這個實例。 3.必須自行向整個系統提供這個實例。 為什麼要使用PHP單例模式? 1.PHP的應用有一個 ...
  • /* 需求:演示一個Hello World的Java小程式 思路: 1.定義一個類。因為Java程式都是定義在類中,Java程式都是以類的形式存在的,類的形式其實就是位元組碼的最終體現 2.定義一個主函數。可以讓該類可以獨立運行。 3.使用輸出語句。可以讓程式的運行結果顯示在控制臺上。 步驟: 1.定 ...
  • 1、MVC:非Java獨有,所有的B/S的結構都在使用它。 M——Model模式(自己寫代碼) V——View 視圖(jsp) C——Controller 控制器(Servlet) 2、JavaWeb三層框架 Web層-->與Web相關的內容(Servlet,JSP,Servlet相關API:req ...
  • 一、創建自定義標簽基本步驟 1、步驟 標簽處理類(標簽也是一個對象,那麼就需要先有類!) tld文件,它是一個xml 頁面中使用<%@taglib%>來指定tld文件的位置 2、標簽處理類 SimpleTag介面 void doTag():每次執行標簽時都會調用這個方法; JspTag getPar ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...