社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
当初学Python进一个Python学习群,不久之后群主大大就发了自己写的一个爬虫,就是爬取www.mzitu.com网站上的妹纸图片,看完之后,惊为天人,一脸懵逼,觉得群主大大好厉害,当初自己进群的初衷以及学Python的初衷就是为了做爬虫,然后慢慢的开始。所以说这次,就是为了实践一下,爬取这个网站上的所有的图片,别邪恶哦,虽然是隐藏福利,我只是为了写爬虫实践,我只是为了写爬虫实践,我只是为了写爬虫实践!!!
网络爬虫会给网站服务器造成巨大负荷,所以,本次实践的源码仅供参考交流,不宜用于商业应用,由此造成的任何法律责任本人不予承担。
为了得到这个网站上所有的妹子图片,就得分析网站的架构,通过分析发现http://www.mzitu.com/all这个网址下有2013-2017年网站上所有的相册的链接,所以准备通过以下步骤爬取网站上的所有图片。
第1步:在http://www.mzitu.com/all下获取所有的相册的链接以及相册的名字
第2步:根据每个相册的链接得到每个相册每一个图片页面的链接
第3步:根据每一个图片的链接得到每一张图片的名字以及图片链接
第一步:在http://www.mzitu.com/all下获取所有的相册的链接以及相册的名字
分析网站源码,查看每一个相册链接以及名字的所处标签
由图上可以看出,每一个相册的链接以及名称都位于<a >标签下
第二步:根据每个相册的链接得到每个相册每一个图片页面的链接
通过分析网站源码,分析每一个相册下每一个图片页面的链接
通过分析,发现每一个图片页面链接都处于<div class="pagenavi">的<a herf>下,但是只有几个图片的页面的链接,如上图所示,分别只有2,3,4.......65,但是图片页面的规律是相册链接+"/"+页面编号,在这里我们得到最大的页面编号65,就可以通过循环得到每一个图片页面的链接,所以在这里通过字符选取将编号压入进一个list,然后通过max方法得到最大编号,得到每一个相册图片页面的链接。
第三步:根据每一个图片的链接得到每一张图片的名字以及图片链接
根据以上得到的图片页面链接,在每个页面内爬取图片的下载链接以及图片的名称
通过分析,图片链接以及名称都位于<img>标签内
通过以上的分析,将整个流程写为Python代码,在其中使用了第三方包Beautifulsoup,现附上全部代码,如有错误,请指教
# 得到妹子图网站所有图片,输入网址为http://www.mzitu.com/all
# __author__ = 'Administrat
# coding=utf-8
import io
import os
import sys
import math
import urllib
from urllib.request import urlopen
from urllib.request import urlretrieve
from urllib import request
from bs4 import BeautifulSoup
import re
import requests
import time
import socket
from collections import OrderedDict
socket.setdefaulttimeout(5000) # 设置全局超时函数
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='gb18030')
# 设置不同的headers,伪装为不同的浏览器
headers1 = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0','Referer':'https://www.mzitu.com/all'}
headers2 = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36','Referer':'https://www.mzitu.com/all'}
headers3 = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11','Referer':'https://www.mzitu.com/all'}
# 在指定路径创建文件夹
def CreateDirectory(DirectoryName):
DirectoryNameNew = [] # 创建新的存储列表
if DirectoryName == "": # 如果传进来的文件夹名为空
DirectoryNameNew = "新建文件夹"
for l in DirectoryName: # 如果文件夹名不为空,剔除其中的“/”
if l != "/" and l != "\":
DirectoryNameNew.append(l)
root = "E:\Programing\Python_project\meizituSpider\Python妹子图\"
path = root + "".join(DirectoryNameNew) # join将列表转为为字符串
print("创建相册" + root + "".join(DirectoryNameNew))
try:
os.makedirs(path)
except Exception as e:
print(root + "".join(DirectoryNameNew) + "已经存在")
return path
# 得到http://www.mzitu.com/all这个网址所有的相册的链接
# site为站点网址,默认为http://www.mzitu.com/all
def GetXiangceUrl(siteUrl):
# 设置代理IP访问
# 抓捕解析网站过程中的错误
try:
# time.sleep(1)
#req = request.Request(siteUrl, headers=headers1 or headers2 or headers3)
req=request.Request(siteUrl,headers=headers1)
html = urlopen(req)
bsObj = BeautifulSoup(html.read(), "html.parser")
html.close()
except UnicodeDecodeError as e:
print("-----UnicodeDecodeError url", siteUrl)
except urllib.error.URLError as e:
print("-----urlError url:", siteUrl)
except socket.timeout as e:
print("-----socket timout:", siteUrl)
i = 0
XiangceZidian = OrderedDict() # 声明字典,并排序
print("正在连接到网站" + siteUrl)
for templist in bsObj.findAll("a", href=re.compile("https://www.mzitu.com/([0-9]+)")): # 正则匹配相册链接标签
i = i + 1
xiangceUrl = templist.attrs['href']
xiangceName = str(i) + " " + templist.get_text()
print(str(i)+" "+xiangceName+" "+xiangceUrl)
# XiangceUrl=str(i)+" "+xiangceName+" "+xiangceUrl
XiangceZidian[xiangceName] = xiangceUrl
# XiangceZidianNew=OrderedDict(XiangceZidian)
return XiangceZidian # 返回相册名称:相册链接字典
# 获取当前相册内所有的图片页面链接
# xiangceurl为相册链接
def GetXiangcePageUrl(xiangceurl):
# 设置代理IP访问
try:
# time.sleep(1)
#req = request.Request(xiangceurl, headers=headers1 or headers2 or headers3)
req=request.Request(xiangceurl,headers=headers1)
html = urlopen(req)
bsObj = BeautifulSoup(html.read(), "html.parser")
html.close()
except UnicodeDecodeError as e:
print("-----UnicodeDecodeError url", xiangceurl)
except urllib.error.URLError as e:
print("-----urlError url:", xiangceurl)
except socket.timeout as e:
print("-----socket timout:", xiangceurl)
bianhaolist = [] # 声明页面编号list,为的是找出这个页面中最大的页面编号
XiangCePageUrlList = [] # 存储该网页下所有的相册链接
print("正在连接到" + xiangceurl + ",并解析该链接的页面链接")
if bsObj != "":
for pagelist in bsObj.findAll("div", {"class": "pagenavi"}):
for link in pagelist.findAll("a", href=re.compile("https://www.mzitu.com/([0-9]+)/([0-9]+)*$")): # 正则匹配相册链接
lianjie = link.attrs['href']
# print(link.attrs['href'])
bianhao = lianjie.replace(xiangceurl + "/", "") # 得到编号
#print(bianhao)
bianhaolist.append(int(bianhao)) # 添加到编号列表
bianhaoMax = max(bianhaolist) # 找到图片链接最大编号
for i in range(1, bianhaoMax + 1):
XiangCePageUrl = xiangceurl + "/" + str(i) # 得到每个相册的图片的链接
# print(XiangCePageUrl)
XiangCePageUrlList.append(XiangCePageUrl)
return XiangCePageUrlList # 返回图片链接列表
# 得到当前页面的图片链接
# XiangCePageUrl为当前相册某个页面的链接,path为文件夹路径,replaceurl为相册的链接
def GetPageImageUrl(XiangCePageUrl, path, replaceurl):
# 设置代理IP访问
try:
# time.sleep(1)
#req = request.Request(XiangCePageUrl, headers=headers1 or headers2 or headers3)
req=request.Request(XiangCePageUrl,headers=headers1)
html = urlopen(req)
bsObj = BeautifulSoup(html.read(), "html.parser")
html.close()
bianhao = XiangCePageUrl.replace(replaceurl + "/", "") # 这里的编号每个相册图片的编号,例如XXXX1.jpg
print("path:" + path)
for templist in bsObj.findAll("div", {"class": "main-image"}): # 寻找图片链接以及名称
for ImageUrllist in templist.findAll("img"):
# print(ImageUrllist.attrs['src'])
Imageurl = ImageUrllist.attrs['src'] # 图片链接
print(XiangCePageUrl + "的图片链接为" + Imageurl)
# print(ImageUrllist.attrs['alt'])
ImageName = ImageUrllist.attrs['alt'] # 图片名称
ImageNameNew = [] # 新的图片名称列表,为了剔除原有名称中包含的"/"符号
for l in ImageName:
if l != "/" and l != " " and l != "\" and l != ".":
ImageNameNew.append(l)
if len(ImageNameNew) != 0:
print("正在下载文件:" + str(path) + "\" + "".join(ImageNameNew) + str(bianhao) + ".jpg") # join将列表转为字符串
try:
ImageNameNewStr = ''.join(ImageNameNew)
localImagefilePath = os.path.join(path,f"{ImageNameNewStr}{bianhao}.jpg")
# 第一种下载反盗链图片的方法
imageHeader = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0','Referer':XiangCePageUrl}
imagedata = requests.get(Imageurl,headers=imageHeader).content
with open(localImagefilePath,'wb') as f:
f.write(imagedata)
# 第二种下载反盗链图片的方法
# opener = urllib.request.build_opener()
# opener.addheaders = [('User-Agent',
# 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36'),('Referer',XiangCePageUrl)]
# urllib.request.install_opener(opener)
# urllib.request.urlretrieve(Imageurl, localImagefilePath)
print("myper=" + str(myper))
while (True == jindu): # 判断是否下载完成
print("下载完成!")
print('')
# time.sleep(1)#下载完成后sleep3秒,防止反爬虫机制
break;
# else:
# time.sleep(10)#如果失败,先休眠10秒
# urlretrieve(Imageurl,str(path)+"\"+"".jion(ImageNameNew)+str(bianhao)+".jpg",jindu)#再下载,直到下载完成
# time.sleep(3)
except Exception as e:
time.sleep(10) # 如果失败,先休眠10秒
# urlretrieve(Imageurl,str(path)+"\"+"".jion(ImageNameNew)+str(bianhao)+".jpg",jindu)#再下载,直到下载完成
# print("下载链接"+Imageurl+"出错,重新下载")
# time.sleep(3)
except UnicodeDecodeError as e:
print("-----UnicodeDecodeError url", XiangCePageUrl)
except urllib.error.URLError as e:
print("-----urlError url:", XiangCePageUrl)
except socket.timeout as e:
print("-----socket timout:", XiangCePageUrl)
print("正在获取" + XiangCePageUrl + "的图片链接")
# urlretrieve()的回调函数,显示当前的下载进度
# a为已经下载的数据块
# b为数据块大小
# c为远程文件的大小
global myper
def jindu(a, b, c):
if not a:
print("连接打开")
if c < 0:
print("要下载的文件大小为0")
else:
global myper
per = 100 * a * b / c
if per > 100:
per = 100
myper = per
print("myper0=" + str(myper))
print("当前下载进度为:" + '%.2f%%' % per)
if per == 100:
return True
# GetXiangceUrl("http://www.mzitu.com/all")
# GetXiangcePageUrl("http://www.mzitu.com/85695")
#GetPageImageUrl("https://www.mzitu.com/85695/2","E:PythonPython妹子图","https://www.mzitu.com/85695")
if __name__ == '__main__':
XiangCe = GetXiangceUrl("http://www.mzitu.com/all") # 返回字典,并将返回字典排序
for xiangceurllist in XiangCe.keys():
print("相册名字" + xiangceurllist) # 相册名字
# print(XiangCe.get(xiangceurllist))#相册链接
mypath = CreateDirectory(xiangceurllist)
# print(mypath)
XiangCePageUrl = GetXiangcePageUrl(XiangCe.get(xiangceurllist))
# time.sleep(1)
# print(XiangCePageUrl)
for xiangcepageurllist in XiangCePageUrl:
# print(xiangcepageurllist)
# print(XiangCePageUrl.index(xiangcepageurllist))
GetPageImageUrl(xiangcepageurllist, mypath, XiangCe.get(xiangceurllist))
time.sleep(1)
刚刚开始写的时候,会遇到服务器断开链接,以及主机在一定时间内无法连接的错误,在百度上,谷歌上看了半天,可能是由于网站的反爬虫机制,我自己的解决办法如下,不知道你们可不可行:
1、关闭windows防火墙,爬完再开
2、采用多headers,伪装不同的浏览器
3、每下载完,sleep一下,减缓访问网站的频率,如果过快,网站服务器会认为你是爬虫
4、设置全局超时时间,有时候因为网速的问题会出现这种情况
5、设置代理IP
刚刚开始不知道为什么这个网站不能用代理IP访问,一访问,网站服务器就强制断开连接,后来又可以了,尽量用代理IP,免得被封IP,代理ip网站可上http://ip.zdaye.com/查询
在这个代码中,没有采用多线程机制,所以爬起来比较慢,勿喷。
最好在晚上爬,网速快。
爬取结果(隐藏福利):
如果您觉得这篇博文有用,请访问我的个人站:http://www.stubbornhuang.com,更多博文干货等着您。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!