网站地址
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()