美文网首页大数据 爬虫Python AI SqlPython之佳
史上最详细的Python爬妹子图教程,包看包会!

史上最详细的Python爬妹子图教程,包看包会!

作者: 山禾家的猫 | 来源:发表于2019-01-09 12:03 被阅读2次

    项目说明:

      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就可以下载啦~!

    1import requests

    2fromscrapyimport Selector

    3

    4

    5defget_page_items(*, start_page_num: int=1, end_page_num: int=2, step: int=1): 6items = []

    7forpage_numin range(start_page_num, end_page_num, step):

    8base_url ='http://www.meizitu.com/a/{genre}_{page_num}.html'

    9req = requests.get(base_url.format(genre='cute', page_num=1))

    10content = req.content.decode('gbk')

    11selector = Selector(text=content)

    12item_urls = list(set(selector.css('#maincontent a::attr(href)').extract()))

    13items.extend(urlforurlinitem_urlsifurl.startswith('http://www.meizitu.com/a/'))

    14return items

    15

    16

    17def get_images(item):

    18req = requests.get(item)

    19content = req.content.decode('gbk')

    20selector = Selector(text=content)

    21image_urls = list(set(selector.css('#maincontent p img::attr(src)').extract()))

    22print(image_urls)

    23

    24

    25foritemin get_page_items():

    26get_images(item)

    上面代码执行的结果为:

    可以看到的效果,所有小姐姐的下载图片的地址都已经拿到了,但是上面的代码有两个问题,聪明的小伙伴,可能已经发现了,上面代码的重合性太高,那些获取url的咚咚,都可以整合,在下面的一版,我们来改写这个函数,有了这些图片的地址,我们只需要调取某个函数或者方法,来下载这些图片保存到本地即可,怎么玩?! 往下看.....

    1# _*_coding: utf-8_*_

    2import os

    3fromtimeimport perf_counter

    4fromfunctoolsimport wraps

    5

    6import requests

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

    22def timer(func):

    23"""

    24:param func: 装饰器的函数,记录方法所消耗的时间

    25 :return:

    26"""

    27@wraps(func)

    28defwrapper(*args, **kwargs):

    29start_time = perf_counter()

    30result = func(*args, **kwargs)

    31end_time = perf_counter()

    32cls_name = func.__name__

    33fmt ='{cls_name} {args} spend time: {time:.5f}'

    34print(fmt.format(cls_name=cls_name, args=args, time=end_time - start_time))

    35return result

    36return wrapper

    37

    38

    39def get_content_css(url):

    40req = requests.get(url)

    41content = req.content.decode('gbk')

    42selector = Selector(text=content)

    43return selector

    44

    45

    46defget_page_items(*, start_page_num: int=1, end_page_num: int=2, step: int=1):

    47items = []

    48forpage_numin range(start_page_num, end_page_num, step):

    49base_url ='http://www.meizitu.com/a/{genre}_{page_num}.html'

    50selector = get_content_css(base_url.format(genre='cute', page_num=page_num))

    51item_urls = list(set(selector.css('#maincontent a::attr(href)').extract()))

    52items.extend(urlforurlinitem_urlsifurl.startswith('http://www.meizitu.com/a/'))

    53return items

    54

    55

    56def get_images(item):

    57selector = get_content_css(item)

    58image_urls = list(set(selector.css('#maincontent p img::attr(src)').extract()))

    59dir_name = selector.css('#maincontent div.metaRight h2 a::text').extract_first()

    60'ok'ifos.path.exists(dir_name)else os.mkdir(dir_name)

    61forurlin image_urls:

    62download_image(dir_name, url)

    63

    64

    65@timer

    66def download_image(dir_name, image_url):

    67headers = {'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'}

    69req = requests.get(image_url, headers=headers)

    70image = req.content

    71filename = image_url.rsplit('/', 1)[-1]

    72save_path = os.path.join(dir_name, filename)

    73with open(save_path,'wb') as f:

    74f.write(image)

    75

    76

    77if__name__=="__main__":

    78start = perf_counter()

    79foritemin get_page_items():

    80get_images(item)

    81end = perf_counter()

    82print(format('end','*^100'))

    83print('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)) 

    看完文章的你心动了么?

    如果有需要源码的小伙伴欢迎大家加裙【六 五 三,四 六 六,六 六 八】

    提供爬妹子图的源码哦~ 还有想要Python学习资料的同学也可以加入我们哦~ 

    相关文章

      网友评论

        本文标题:史上最详细的Python爬妹子图教程,包看包会!

        本文链接:https://www.haomeiwen.com/subject/lsmwrqtx.html