项目说明:

  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  
<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))
<>

协程的使用,大家移步到廖大神的哪里学习下吧~~~,我就不讲了...不然我要疯了...我要看会电影,缓一会。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信