python爬虫:番号挖掘机

最近一直在学习python,想写一些练习。看别人都是爬壁纸什么的,感觉那些也没啥意思,所有我就去爬番号了。

  • 爬虫目录
    在这里插入图片描述
    __pycache__:这个python解释器会自动生成,不用自己创建。主要是下次再Start.py执行时,若解释器发现这个Html.py 脚本没有修改过,就会跳过编译这一步,直接运行以前生成的保存在 __pycache__文件夹里的 *.pyc 文件,大大缩短项目运行前的准备时间。
  • Html.py
    我自己对urllib.request和bs4类库的二次封装
    #1.网页操作类库
    import urllib.request,urllib.parse,bs4
    
    class Html(object):
    	'''[网页类]
    	
    	[对"GET"、"POST"方式获取网页的二次封装,基于urllib.request类库]
    	'''
    	def __init__(self,url,headers,requestMode = "GET",data = None):
    		'''[初始化]
    		
    		[类的数据初始化]
    		
    		Arguments:
    			url {[str]} -- [url网址]
    			headers {[dict]} -- [网站报头表单]
    		
    		Keyword Arguments:
    			requestMode {str} -- [请求模式["GET","POST"]] (default: {"GET"})
    			data {[dict]} -- [POST请求表单] (default: {None})
    		'''
    		self.url = url
    		self.headers = headers
    		self.requestMode = requestMode
    		self.data = data
    		#获取响应文本,并转换成soup对象
    		self.responseSoup = bs4.BeautifulSoup(self.getResponse(),"html.parser")
    
    		def __str__(self):
    			'''[类信息]
    			
    			[用于打印类信息]
    			'''
    			return self.url + "\n" + _ 
    			str(self.headers) + "\n" + _ 
    			str(self.requestMode) + "\n" + _ 
    			str(self.data)
    
    	def getResponse(self):
    		'''[获取响应]
    		
    		[获取网页响应,并返回字符串]
    		
    		Returns:
    			[str] -- [网页响应]
    		'''
    		#判断请求模式
    		if self.requestMode == "GET":
    			#创建请求对象
    			request = urllib.request.Request(self.url,headers = self.headers)
    		else:
    			#判断"POST"请求所需要的表单
    			if len(self.data) == 0:
    				#打印错误信息
    				print("POST请求所需的表单data为空,退出函数。")
    				return
    			else:
    				#将data表单编码
    				data = urllib.parse.urlencode(self.data).encode("utf-8")
    				#创建带data表单的请求
    				request = urllib.request.Request(url = self.url,headers = self.headers,data = data)
    		#循环获取响应,避免获取失败直接结束函数
    		while True:
    			#发送请求,得到响应
    			response = urllib.request.urlopen(request,timeout = 30)
    			#判断是否获取成功
    			if response.getcode() == 200:
    				break
    			else:
    				#打印错误信息,继续循环
    				print("获取网页失败,重新获取。错误报告:%d"%response.getcode())
    		#返回响应
    		return response
    
    	def labelSelect(self,label):
    		'''[标签选择]
    		
    		[用于获取soup对象中的标签]
    		
    		Arguments:
    			label {[str]} -- [选择条件]
    		'''
    		if self.responseSoup == None:
    			#打印错误提示
    			print("soup对象为空。")
    		else:
    			#标签
    			return self.responseSoup.select(label)
    
    	def printResponse(self):
    		'''[打印响应]
    		
    		[在控制台打印响应文本]
    		'''
    		print(self.responseSoup.prettify())
    
    def downloadImage(imageHtml,filename):
    	'''[下载图片]
    	
    	[下载图片到指点地址]
    	
    	Arguments:
    		imageHtml {[str]} -- [图片url]
    		filename {[type]} -- [图片地址]
    	'''
    	try:
    		#下载图片
    		urllib.request.urlretrieve(imageHtml,filename = filename)
    	except Exception as e:
    		print("%s--下载出错"%imageHtml)
    
    def main():
    	'''[主函数]
    	
    	[用于测试类,被引用时不会执行]
    	'''
    
    if __name__ == '__main__':
    	main()
    
  • Page.txt
    这个文件是用来保存爬取到的数据页,自己创建就好了。然后可以在里面输入页码(除了数字什么都不要有),范围:0~56。
  • Start.py
    程序入口共有两个线程
    1.主线线程:爬取数据
    2.下载线程:下载图片
    #1.自定义类
    import Html
    #2.系统模块
    import time,threading,queue,os
    
    #基础地址
    baseUrl = "http://nanrenvip.xyz"
    #女优作品,字典队列
    AV = queue.Queue()
    #首层报头
    firstHeaders = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
    ,"Accept-Language":"zh-CN,zh;q=0.9"
    ,"Cache-Control":"max-age=0"
    ,"Connection":"keep-alive"
    ,"Host":"nanrenvip.xyz"
    ,"Referer":"http://nanrenvip.xyz/olds.html"
    ,"Upgrade-Insecure-Requests":"1"
    ,"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6776.400 QQBrowser/10.3.2577.400"
    }
    #作品集合报头
    gatherHeaders = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
    ,"Accept-Language":"zh-CN,zh;q=0.9"
    ,"Cache-Control":"no-cache"
    ,"Connection":"keep-alive"
    ,"Host":"nanrenvip.xyz"
    ,"Referer":"http://nanrenvip.xyz/olds.html"
    ,"Upgrade-Insecure-Requests":"1"
    ,"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6776.400 QQBrowser/10.3.2577.400"
    }
    #封面报头
    coverHeaders = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
    ,"Accept-Language":"zh-CN,zh;q=0.9"
    ,"Cache-Control":"no-cache"
    ,"Connection":"keep-alive"
    ,"Host":"nanrenvip.xyz"
    ,"Referer":"http://nanrenvip.xyz/olds.html"
    ,"Upgrade-Insecure-Requests":"1"
    ,"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6776.400 QQBrowser/10.3.2577.400"
    }
    
    def getAV():
    	'''[获取AV]
    	
    	[将获取的女优名字,番号,封面url放入队列]
    	'''
    	global AV
    	#读取上一次下载到的页码
    	htmlPage = pageRecord("r")
    	#记录女优数
    	AVnumber = 0
    	#临时作品字典
    	tempGatherDict = {}
    	#第一层html对象
    	firstHtml = Html.Html(baseUrl + "/nvyouku/1-0-0-0-0-0-" + str(htmlPage) + ".html",firstHeaders,"GET")
    	#根据是否有a标签判断页尾
    	while len(firstHtml.labelSelect(".avps_ny a")) != 0:
    		#遍历女优
    		for a1 in firstHtml.labelSelect(".avps_ny a"):
    			#这里的a标签已经是bs4对象
    			AVname = a1.select("span.fh_bt")[0].get_text()
    			AVnumber += 1
    			print("-------------------------第%d名:%s-------------------------"%(AVnumber,AVname))
    			try:
    				#作品集合对象
    				gatherHtml = Html.Html(baseUrl + a1["href"],gatherHeaders,"GET")
    			except Exception as e:
    				print("作品集对象--创建失败")
    				continue
    			#遍历作品集
    			for index,a2 in enumerate(gatherHtml.labelSelect(".avps a")):
    				try:
    					#封面对象
    					coverHtml = Html.Html(baseUrl + a2["href"],coverHeaders,"GET")
    					AVid = coverHtml.labelSelect("h1.heading")[0].get_text()
    				except Exception as e:
    					print("封面对象--创建失败")
    					continue
    				#临时作品组装{番号:url}
    				tempGatherDict[AVid] = baseUrl + coverHtml.labelSelect("img.lazyload")[0]["data-original"]
    				print("爬取作品:%s\n还剩:%d张"%(AVid,(len(gatherHtml.labelSelect(".avps a")) - index - 1)))
    			#添加女优名字、作品
    			AV.put({AVname:tempGatherDict})
    			#清空临时字典
    			tempGatherDict = {}
    		#爬取下一页的女优们
    		htmlPage += 1
    		firstHtml = Html.Html(baseUrl + "/nvyouku/1-0-0-0-0-0-" + str(htmlPage) + ".html",firstHeaders,"GET")
    	#爬取完毕
    	print("爬取完毕")
    	AV.put("end")
    
    def pageRecord(mode,page = None):
    	'''[记录页码]
    	
    	[写入和读取TXT文件来保存页码]
    	
    	Arguments:
    		mode {[模式]} -- [对文件的操作模式]
    	
    	Keyword Arguments:
    		page {[页码]} -- [要写入的页码] (default: {None})
    	
    	Returns:
    		[整数] -- [返回页码]
    	'''
    	if mode == "w":
    		#写入页数
    		f = open(".\\Page.txt","w")
    		f.write(str(page))
    		f.close()
    	if mode == "r":
    		#读取页数
    		f = open(".\\Page.txt","r")
    		page = int(f.read())
    		f.close()
    		return page
    
    def downloadImage(queue):
    	'''[下载图片]
    	
    	[下载队列里的图片到指定路径]
    	
    	Arguments:
    		queue {[Queue]} -- [队列]
    	'''
    	while True:
    		print("等待下载")
    		#获取要下载的作品
    		gather = queue.get()
    		#判断是否已经没有作品
    		if gather == "end":
    			#结束线程
    			print("***全部下载完成***")
    			return
    		#遍历女优
    		for AVname in gather:
    			# #判断是否存在文件夹如果不存在则创建为文件夹
    			if not os.path.exists(".\\Performer\\" + AVname):
    				os.makedirs(".\\Performer\\" + AVname)
    			#遍历番号
    			for AVid in gather[AVname]:
    				#判断图片是否已经下载
    				if not os.path.exists(".\\Performer\\" + AVname + "\\" + AVid + gather[AVname][AVid][-4:]):
    					print("开始下载:%s"%gather[AVname][AVid])
    					Html.downloadImage(gather[AVname][AVid],".\\Performer\\" + AVname + "\\" + AVid + gather[AVname][AVid][-4:])
    		print("下载完成!:%s"%AVname)
    
    def main():
    	'''[主函数]
    	
    	[程序入口]
    	'''
    	print("程序已启动(*^▽^*)")
    	#启动下载线程
    	download = threading.Thread(target = downloadImage,args = ([AV]))
    	download.start()
    	#将AV添加到下载队列
    	getAV()
    
    if __name__ == '__main__':
    	#程序入口
    	main()
    
  • 注意
    下载的图片会在你运行Start.py的相对位置下,所以建议在用控制台先cd到自己创建的目录下运行。
    资源还是很多的,爬到十多页的时候都已经有1G多了。

猜你喜欢

转载自blog.csdn.net/qq_23685163/article/details/84198524