爬取美图录网站图片

网站地址

https://www.meitulu.com/

分析该网站

1. 打开网站地址后,查看网站右侧导航菜单,目标是提取出爬取网站图片所需的链接地址,这些链接地址最好是包含整个网站的链接地址。

2. 随便点开一个图集分类下的类别,比如"女神",,进入到的链接地址是:https://www.meitulu.com/t/nvshen/. 

3. 从中随便选一个图集点开,进入到该图集的详情页面。上面显示的该图集的相关信息,下面显示的是该图集的每张图片。
   右上角显示的是该图集的当前位置。注意这个当前位置。

4. 再从图集类别中选一个进入到该图集的详情页面,发现右上角也有当前位置。

5. 点击导航菜单中的精选美女,进入到某一个图集详情页面,发现右上角也有当前位置。

6. 点击导航菜单中的日韩美女,进入到某一个图集详情页面,发现右上角也有当前位置。

7. 经过以上分析可知,该网站的图集分为如下三大类:日韩美女,港台美女和国产美女。图集分类中是每一个图集的标签汇总。

8. 提取出图集三大类的地址如下:
日韩美女:https://www.meitulu.com/rihan/, 
港台美女:https://www.meitulu.com/gangtai/, 
国产美女:https://www.meitulu.com/guochan/, 
经查看这三个网址,只有最后的不一样,前面的网址等都是一样的,可以构造列表来遍历循环使用,['rihan','gangtai','guochan']

9. 假如进入国产美女里。通过分析页面信息,每一个图集信息都是在一个li标签里,可以使用正则表达式提取出这些li标签。

10.点击其中一个图集,其链接是:https://www.meitulu.com/item/16889.html, 进入到该图集的详情页面.
最上面显示该图集的图片共有96张,每页显示4张图片,拉到最后的第24页,24*4=96张。

10.查看该图集下的每一个图片链接,发现是在一个img标签里,第一个的4张图片地址依次是:
https://mtl.ttsqgs.com/images/img/16889/1.jpg, 
https://mtl.ttsqgs.com/images/img/16889/2.jpg, 
https://mtl.ttsqgs.com/images/img/16889/3.jpg,
https://mtl.ttsqgs.com/images/img/16889/4.jpg,

最后24页的4张图片地址链接是:https://www.meitulu.com/item/16889_24.html, 每一个图片的地址依次是:
    https://mtl.ttsqgs.com/images/img/16889/93.jpg,
    https://mtl.ttsqgs.com/images/img/16889/94.jpg,
    https://mtl.ttsqgs.com/images/img/16889/95.jpg,
    https://mtl.ttsqgs.com/images/img/16889/96.jpg

同时页面显示的有"美图录提示:点击图片,查看原尺寸高清大图",js代码是:
    function() { 
        window.open("/img.html?img=" + this.src + "")
    }
点击图片进入原尺寸高清大图,复制出网址如下:https://www.meitulu.com/img.html?img=https://mtl.ttsqgs.com/images/img/16889/1.jpg。

11. 分析第10步的图片链接,可以发现:
(1)图集链接地址(https://www.meitulu.com/item/16889.html)中的数字16889跟该图集中的每一个张图片的链接地址(https://mtl.ttsqgs.com/images/img/16889/1.jpg)相关.
(2)每一个图片的链接地址最后的数字是从1开始的,一直到该图集的总数第96
(2)图集详情页中的图片总数跟图集分页数有关,图片总数除以4,若有余数再加1,得到的数字就是该图集的分页数。
(3)该图集的链接地址也有规律,比如图集的第一页地址是:https://www.meitulu.com/item/16889.html, 
第二页的是:https://www.meitulu.com/item/16889_2.html, 最后第24页的是:https://www.meitulu.com/item/16889_24.html。
(4)经过以上分析,优先采用(1)和(2)中得到的规律,提取出每一个图集的名称,图集的链接和图片总数,然后构造该图集下的每一个图片的链接。
(5)图集的链接也有规律,比如国产美女分类,第一页的地址是:https://www.meitulu.com/guochan/, 
第二页的地址是:https://www.meitulu.com/guochan/2.html, 第164页的地址是:https://www.meitulu.com/guochan/164.html。 可以采用遍历的方式。

实际操作中的坑

1. 使用requests的get方式请求每一个图片的链接,得到的图片是损坏的,向群里其他人请教得知,请求是需要加上headers头部信息,必须有Referer,且Referer参数值还有要求,比如说某张图片的图片地址是:https://mtl.ttsqgs.com/images/img/16889/13.jpg, 则Referer的值是https://www.meitulu.com/img.html?img=https://mtl.ttsqgs.com/images/img/16889/13.jpg, 注意查看这俩网址之间的关系。
2. 请求次数过多会报403 Forbidden,通过使用模块fake_useragent生成随机的User-Agent信息。可以解决一小部分情况,时间一长还是会再次报403 Forbidden,只能再次随机生成不同的User-Agent值。
3. 时间长的话会出现这样一个情况,往后的每个图集只能下载保存前9张图片,以后的图片访问请求均报403 Forbidden,这个估计是封IP了,所以还需要使用代理才行。
4. 在网上找到的一个能用的代理软件,地址是:https://github.com/chenjiandongx/async-proxy-pool, 若使用的redis版本低于3.0,则代理池代码可以直接运行使用,若高于3.0版本,则需要修改其中一个文件,具体如下:async_proxy_pool/database.py,修改其中的第45行,原先是self.redis.zadd(REDIS_KEY, proxy, score),修改成:self.redis.zadd(REDIS_KEY, {proxy: score})。
5. 使用代理
(1)运行客户端,启动收集器和校验器:python3 client.py
(2)运行服务器,启动 web 服务:python3 server_flask.py
(3)获取代理地址信息
import requests
proxy = requests.get('http://192.168.0.200:3289/pop')
proxies = proxy.json()
print(proxies)
(4)爬虫代码中使用代理
跟(3)合二为一
import requests
proxy = requests.get('http://192.168.0.200:3289/pop')
requests.get("http://example.org", proxies=proxies)

实际代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import random
import re
import requests
from fake_useragent import UserAgent
from requests.exceptions import RequestException

# 获取三大分类页面详细数据
def get_one_page(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            response.encoding = 'utf8'
            return response.text
        else:
            return None
    except RequestException:
        print('请求失败')
        return None

# 解析三大分类页面数据,提取需要的数据
def parse_one_page(html):
    pattern = re.compile('<li>.*?<p>数量: (.*?) 张</p>.*?<p class=p_title><a href="(.*?)".*?>(.*?)</a></p>.*?</li>',
                         re.S | re.M)
    items = re.findall(pattern, html)
    for i in range(len(items)):
        yield {
            'num': int(items[i][0]),  # 获取图集的图片总数
            'name': items[i][2],  # 获取图集名称
            'url': items[i][1].split('/')[4][:-5],  # 获取图集id
        }

# 获取随机请求头
def GetUserAgent():
    ua = UserAgent()
    return random.choice([ua.safari, ua.firefox, ua.chrome, ua.opera, ua.ie, ua.random])

# 获取随机代理地址,因爬取速度慢,暂不使用
def GetProxy():
    proxy = requests.get('http://192.168.0.200:3289/pop')
    proxies = proxy.json()
    return proxies

# 请求图片链接地址
def download_image(item):
    try:
        file_path = item['name']  # 获取图集名称
        id = item['url']  # 获取图集id
        num = item['num']  # 获取图集的图片总数
        for i in range(1, num + 1):
            url = f'https://mtl.ttsqgs.com/images/img/{id}/{i}.jpg'  # 使用f-string的方式拼接字符串
            headers = {  # 构造请求头
                'User-Agent': GetUserAgent(),
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                'Accept-Encoding': 'gzip, deflate, br',
                'Accept-Language': 'zh-CN,en-US;q=0.8,zh;q=0.5,en;q=0.3',
                'Referer': 'https://www.meitulu.com/img.html?img=%s' % url,
            }
            yield {
                'name': file_path,
                'headers': headers,
                'i': i,
                'url': url,
                'num': num
            }
    except RequestException:
        print('请求图片出错')
        return None

# 保存图片,使用第三方错误重试模块,该模块需要导入
# @retry(stop_max_attempt_number=3)
def save_image(path, x):
    name = x['name']
    i = x['i']
    headers = x['headers']
    url = x['url']
    num = x['num']
    base_path = f'{os.getcwd()}{os.sep}{path}{os.sep}{name}'
    if not os.path.exists(base_path):
        os.makedirs(base_path)
    save_path = f'{base_path}{os.sep}{i}.jpg'
    if not os.path.exists(save_path):
        try:
            requests.packages.urllib3.disable_warnings()
            response = requests.get(url, headers=headers, verify=False)
        except:
            print(f'请求{name}图集的第{i}张图片链接地址失败,共{num}张,图片链接是{url}')
        else:
            if response.status_code == 200:
                try:
                    with open(save_path, 'wb') as f:
                        f.write(response.content)
                        print(f'保存{name}图集的第{i}张图片成功,共{num}张')
                except:
                    print(f'保存{name}图集的第{i}张图片失败,共{num}张,图片链接是{url}')
            else:
                print(f'请求{name}图集的第{i}张图片链接地址状态不是200,共{num}张,图片链接是{url}')
    else:
        print(f'{name}图集文件夹已存在,请求一下图集')

# 遍历三大分类
def img_url():
    img_ict = {'rihan': 88, 'gangtai': 36, 'guochan': 165}
    for k, v in img_ict.items():
        yield {
            'name': k,
            'num': v
        }

# 新传分类名称
def main():
    for img_u in img_url():
        for i in range(1, img_u['num']):
            if i == 1:
                url = f'https://www.meitulu.com/{img_u["name"]}/'
            else:
                url = f'https://www.meitulu.com/{img_u["name"]}/{i}.html'
            html = get_one_page(url)
            for item in parse_one_page(html):
                for x in download_image(item):
                    save_image(img_u['name'], x)


if name == ‘main‘:
main()

效果展示

-------------本文结束 感谢您的阅读-------------
如果觉得我的文章对您有用,请随意打赏.您的支持将鼓励我继续创作!

  • 本文标题: 爬取美图录网站图片
  • 文章作者: 永夜初晗凝碧天
  • 发布时间: 2019年12月31日 - 12:12:28
  • 更新时间: 2019年12月31日 - 12:12:28
  • 本文链接: https://yongnights.github.io/2019/12/31/爬取美图录网站图片/
  • 版权声明: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。