批量爬取某网站漂亮姐姐图片并保存至本地
批量爬取某网站漂亮姐姐图片并保存至本地

批量爬取某网站漂亮姐姐图片并保存至本地

1.前言

最近群友在群里发来几张小姐姐的写真,看了一下发现太好看了,私聊之后群友竟然直接把网站抛给我,那我可就不客气了,立马开工


2.站点分析

我们要爬取的是某个模特的主页,主页中有各个主题的写真,其子页面为各个不同主题的写真,打开之后发现子页面之间规则是不同的,有的页面是一页会显示所有的图片,有的页面是需要翻页的,所以这里需要进行判断。

这是一个静态网站,也没有反爬,但是对搜索操作进行了限制,每次搜索间隔为30秒,可能服务器资源有限,但是这里我们并不需要搜索操作,也没有太大的影响

所以我们要在主页面抓取主题页面链接,再遍历每个链接,找到子页面的所有图片链接,根据每个图片链接保存图片到本地

爬取中使用到了re, os, time, urllib, requests, bs4库


3.爬取分析

3.1.发起请求

首先是调用所有需要用到的库

  1. import re
  2. import os
  3. import time
  4. import urllib
  5. import requests
  6. from bs4 import BeautifulSoup

然后写出请求函数

  1. def getHTMLText(url):   #获取页面
  2.     try:
  3.         r = requests.get(url, timeout=30)
  4.         r.raise_for_status()
  5.         r.encoding = r.apparent_encoding
  6.         return r.text
  7.     except:
  8.         return ""

因为是常规的静态页面,所以请求直接用轮子就行

3.2.获取主页面所有链接

先来看一下主页面的源码,直接使用检查,跳到页面链接代码所在行

可以看到,子页面的链接是在<li>标签中,而其父级标签为<div>,在使用Beautifulsoup解析的时候只需要从div向下遍历li即可

同时,需要注意子页面的链接不是完整链接,而是一个拼接url,所以我们拼接的时候也要在前面加上主页面链接

最后将所有的链接存入一个列表即可

  1. def parsePage1(ilt, html):      #页面处理,抓取总网址链接
  2.     try:
  3.         soup = BeautifulSoup(html, "html.parser")
  4.         for li in soup.find('div', class_="hezi").findAll('li'):
  5.             t_url = "http://www.meibang88.com" + li.a.attrs['href']        #网址拼接,结果为字符串
  6.             ilt.append(t_url)
  7.         print("主页面链接:")
  8.         print(ilt)
  9.     except:
  10.         return ""

使用for循环遍历li标签,并使用.attrs方法获取href的内容,再和主页的url进行拼接,得到一个字符串并存入列表中,运行时打印链接,方便运行时看运行状况

这里需要注意class是python的一个类,所以需要在之后加_进行区分,不然会报错。

3.3.获取子页面图片链接

先审查图片所在源码

可以看到,图片链接在div标签中,class选择器为content,而链接在img标签中的src属性中,可以使用正则表达式对其进行提取

  1. def parsePage2(ilt, html):      #抓取单个主题页面的图片链接
  2.     try:
  3.         soup = BeautifulSoup(html, "html.parser")
  4.         t = soup.findAll('div', class_='content')
  5.         res = re.findall('src="(.+?)"', str(t))                 #结果为列表
  6.         if len(res) != 0:                                      #判断列表是否为空
  7.             ilt += res                                              #更新列表,追加
  8.         print("分列表链接:")
  9.         print(ilt)
  10.     except:
  11.         return ""

变量t为div标签内的所有内容,为列表类型,使用str将其转化为字符串便于后面正则匹配

正式表达式使用了最小匹配,直接提取src中的所有内容

正则表达式findall方法会返回一个列表,直接判断列表的长度就可以判断当前页面是否还有链接,如果不是空再追加到总链接的列表中

为什么要判断是否为空呢?因为这个函数不仅可以用于单页主题的写真,还可以用于需要翻页的主题的写真,因为每个主题的页数不一样,在翻页的时候可能会出现翻过头的情况(由于太懒,所有没有写判断多少页的代码,直接设了个最大值,每页有5张照片,大概看一下主页面照片数就能知道应该设最大值是多少),翻过头的时候会获得一个空列表,就不需要再追加了

判断页数的代码我放在主函数中了,所以这个函数只需要获取当前页面的链接,不需要翻页功能

再打印获取的列表,方便知道运行的进程

3.4.图片存储

图片存储主要用到了os库创建路径,urllib保存图片

  1. def SaveImage(ilt, n):         #图片存储
  2.     try:
  3.         print("开始保存图片...........")
  4.         path = 'D:\Program_A\centos\Controller\阿\写真'       #需要修改为自己的文件路径
  5.         os.makedirs(path + "\_" + str(n))                   #加_区别转义符
  6.         for i in range(len(ilt)):
  7.             urllib.request.urlretrieve(ilt[i], path + '\_' + str(n) + '\_' + '%s.jpg'%str(i))
  8.             print("已爬取第{:.0f}张图片".format(i))
  9.     except:
  10.         return ""

因为os库也是临时补的知识,如何根据不同主题创建不同的文件夹,如何把图片保存到一个文件夹中,我也是摸索了很久,最后想出来用文件路径分隔符\加上_来创建新的文件夹和保存命名图片,因为分隔符在python中为转义符,所以我们需要在它之后加一个没有转义作用的符号,在用传进来的一个变量n就可以进行不重复地创建和命名了

然后遍历链接列表,保存每一个链接的图片并重命名就好了

每次保存也是打印出数量,方便了解进程

3.5.主函数

因为主函数要调用以上子函数并且对页面进行判断,所以要稍微复杂一些

  1. def main():                 #主函数
  2.     start = time.perf_counter()                             #设置计时器
  3.     print("------Beauty主页爬取------\n")
  4.     start_url = "http://www.meibang88.com/t/894/"           #修改url为模特主页即可爬取所有写真
  5.     Ori_site = []
  6.     indolist = []
  7.  
  8.     html = getHTMLText(start_url)                       #获取主页链接
  9.     parsePage1(Ori_site, html)
  10.  
  11.     for i in range(len(Ori_site)):                      #根据主页链接爬取各个主题
  12.         print("当前为第{:.0f}个主题".format(i))
  13.         html = getHTMLText(Ori_site[i])                 #爬取第一页
  14.         parsePage2(indolist, html)
  15.         if len(indolist) < 7:                            #当单个主题爬取链接数小于7时说明需要翻页
  16.             for j in range(2, 25):                       #拼接数字从2开始
  17.                 print("当前为第{:.0f}个主题第{:.0f}页".format(i, j))
  18.                 each_url =  Ori_site[i].replace('.html', '') + '_' + str(j) + '.html'       #replace替换为空再拼接url
  19.                 html = getHTMLText(each_url)
  20.                 parsePage2(indolist, html)
  21.         SaveImage(indolist, i)             #每爬取一个主题进行保存
  22.         indolist.clear()                #每爬取一个主题后清空主题链接列表
  23.  
  24.     end = time.perf_counter()
  25.     ti = round((end - start), 1)
  26.     print("爬取完毕")
  27.     print("本次用时:{:.1f}s, {:.1f}min".format(ti, ti/60))

在开始我们设置一个计时函数,最后可以输出花费时间

首先写一个start_url,为模特主页的url,便于后面进行url拼接

创建两个列表,Ori_site用于存储所有主题的链接,indolist用于存储每个主题中所有图片链接

  1. html = getHTMLText(start_url)                       #获取主页链接
  2. parsePage1(Ori_site, html)

这两行代码是获取主页链接,只需要运行一次,把所有的主题链接保存在Ori_site列表中

后面写了一个for循环来遍历Ori_site列表

首先是爬取第一页,不管需不需要翻页,都会进行这次操作

对于需要翻页的主题,观察之后会发现,第一页只有5张或者6张图片,如果函数parsePage第一次运行后,列表indolist的长度小于7,即为需要翻页的主题,所以我们用这个条件来判断是否翻页,即

  1. if len(indolist) < 7:                            #当单个主题爬取链接数小于7时说明需要翻页

那么需要翻多少页呢,观察之后发现,一般写真都在120以下,所以我直接把页面设为25,一页为5张,合起来就是25×5=125张,至于多的也不用担心,函数中有列表长度的判断,返回的空列表并不会被追加进来

然后是分页的url拼接,这里需要注意的是,主页和分页的不同

这是主页:

这是分页:

第一页为4个数字,而第N页是4个数字之后加上_再加上页数

而我们主页链接列表中的url已经是一个完整的url,需要在中间插入数字,怎么办呢?

这里我们使用到了.replace方法,先把固定的.html替换成空,再在后面加上_和数字,再加上.html,就可以构成新的url了

在我们获取了一个主题所有的图片链接之后就可以进行图片保存了,这个图片保存的函数是放在if判断语句之外的,因为不管判断结果是否为真,我们都需要保存图片

最后,在每爬取完一个主题之后,我们用clear函数把临时存放图片链接的indolist列表清空,为下一次循环做准备

在所有的图片爬取完成之后,我们输出爬取完成,并打印所花费的时间