1.前言
最近群友在群里发来几张小姐姐的写真,看了一下发现太好看了,私聊之后群友竟然直接把网站抛给我,那我可就不客气了,立马开工
2.站点分析
我们要爬取的是某个模特的主页,主页中有各个主题的写真,其子页面为各个不同主题的写真,打开之后发现子页面之间规则是不同的,有的页面是一页会显示所有的图片,有的页面是需要翻页的,所以这里需要进行判断。
这是一个静态网站,也没有反爬,但是对搜索操作进行了限制,每次搜索间隔为30秒,可能服务器资源有限,但是这里我们并不需要搜索操作,也没有太大的影响
所以我们要在主页面抓取主题页面链接,再遍历每个链接,找到子页面的所有图片链接,根据每个图片链接保存图片到本地
爬取中使用到了re, os, time, urllib, requests, bs4库
3.爬取分析
3.1.发起请求
首先是调用所有需要用到的库
import re
import os
import time
import urllib
import requests
from bs4 import BeautifulSoup
然后写出请求函数
def getHTMLText(url): #获取页面
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
因为是常规的静态页面,所以请求直接用轮子就行
3.2.获取主页面所有链接
先来看一下主页面的源码,直接使用检查,跳到页面链接代码所在行

可以看到,子页面的链接是在<li>标签中,而其父级标签为<div>,在使用Beautifulsoup解析的时候只需要从div向下遍历li即可
同时,需要注意子页面的链接不是完整链接,而是一个拼接url,所以我们拼接的时候也要在前面加上主页面链接
最后将所有的链接存入一个列表即可
def parsePage1(ilt, html): #页面处理,抓取总网址链接
try:
soup = BeautifulSoup(html, "html.parser")
for li in soup.find('div', class_="hezi").findAll('li'):
t_url = "http://www.xxxxxxx.com" + li.a.attrs['href'] #网址拼接,结果为字符串
ilt.append(t_url)
print("主页面链接:")
print(ilt)
except:
return ""
使用for循环遍历li标签,并使用.attrs方法获取href的内容,再和主页的url进行拼接,得到一个字符串并存入列表中,运行时打印链接,方便运行时看运行状况
这里需要注意class是python的一个类,所以需要在之后加_进行区分,不然会报错。
3.3.获取子页面图片链接
先审查图片所在源码

可以看到,图片链接在div标签中,class选择器为content,而链接在img标签中的src属性中,可以使用正则表达式对其进行提取
def parsePage2(ilt, html): #抓取单个主题页面的图片链接
try:
soup = BeautifulSoup(html, "html.parser")
t = soup.findAll('div', class_='content')
res = re.findall('src="(.+?)"', str(t)) #结果为列表
if len(res) != 0: #判断列表是否为空
ilt += res #更新列表,追加
print("分列表链接:")
print(ilt)
except:
return ""
变量t为div标签内的所有内容,为列表类型,使用str将其转化为字符串便于后面正则匹配
正式表达式使用了最小匹配,直接提取src中的所有内容
正则表达式findall方法会返回一个列表,直接判断列表的长度就可以判断当前页面是否还有链接,如果不是空再追加到总链接的列表中
为什么要判断是否为空呢?因为这个函数不仅可以用于单页主题的写真,还可以用于需要翻页的主题的写真,因为每个主题的页数不一样,在翻页的时候可能会出现翻过头的情况(由于太懒,所有没有写判断多少页的代码,直接设了个最大值,每页有5张照片,大概看一下主页面照片数就能知道应该设最大值是多少),翻过头的时候会获得一个空列表,就不需要再追加了
判断页数的代码我放在主函数中了,所以这个函数只需要获取当前页面的链接,不需要翻页功能
再打印获取的列表,方便知道运行的进程
3.4.图片存储
图片存储主要用到了os库创建路径,urllib保存图片
def SaveImage(ilt, n): #图片存储
try:
print("开始保存图片...........")
path = 'D:\Program_A\centos\Controller\阿朱\写真' #需要修改为自己的文件路径
os.makedirs(path + "\_" + str(n)) #加_区别转义符
for i in range(len(ilt)):
urllib.request.urlretrieve(ilt[i], path + '\_' + str(n) + '\_' + '%s.jpg'%str(i))
print("已爬取第{:.0f}张图片".format(i))
except:
return ""
因为os库也是临时补的知识,如何根据不同主题创建不同的文件夹,如何把图片保存到一个文件夹中,我也是摸索了很久,最后想出来用文件路径分隔符\加上_来创建新的文件夹和保存命名图片,因为分隔符在python中为转义符,所以我们需要在它之后加一个没有转义作用的符号,在用传进来的一个变量n就可以进行不重复地创建和命名了
然后遍历链接列表,保存每一个链接的图片并重命名就好了
每次保存也是打印出数量,方便了解进程
3.5.主函数
因为主函数要调用以上子函数并且对页面进行判断,所以要稍微复杂一些
def main(): #主函数
start = time.perf_counter() #设置计时器
print("------Beauty主页爬取------\n")
start_url = "http://www.xxxxxx.com/t/894/" #修改url为模特主页即可爬取所有写真
Ori_site = []
indolist = []
html = getHTMLText(start_url) #获取主页链接
parsePage1(Ori_site, html)
for i in range(len(Ori_site)): #根据主页链接爬取各个主题
print("当前为第{:.0f}个主题".format(i))
html = getHTMLText(Ori_site[i]) #爬取第一页
parsePage2(indolist, html)
if len(indolist) < 7: #当单个主题爬取链接数小于7时说明需要翻页
for j in range(2, 25): #拼接数字从2开始
print("当前为第{:.0f}个主题第{:.0f}页".format(i, j))
each_url = Ori_site[i].replace('.html', '') + '_' + str(j) + '.html' #replace替换为空再拼接url
html = getHTMLText(each_url)
parsePage2(indolist, html)
SaveImage(indolist, i) #每爬取一个主题进行保存
indolist.clear() #每爬取一个主题后清空主题链接列表
end = time.perf_counter()
ti = round((end - start), 1)
print("爬取完毕")
print("本次用时:{:.1f}s, {:.1f}min".format(ti, ti/60))
在开始我们设置一个计时函数,最后可以输出花费时间
首先写一个start_url,为模特主页的url,便于后面进行url拼接
创建两个列表,Ori_site用于存储所有主题的链接,indolist用于存储每个主题中所有图片链接
html = getHTMLText(start_url) #获取主页链接
parsePage1(Ori_site, html)
这两行代码是获取主页链接,只需要运行一次,把所有的主题链接保存在Ori_site列表中
后面写了一个for循环来遍历Ori_site列表
首先是爬取第一页,不管需不需要翻页,都会进行这次操作
对于需要翻页的主题,观察之后会发现,第一页只有5张或者6张图片,如果函数parsePage第一次运行后,列表indolist的长度小于7,即为需要翻页的主题,所以我们用这个条件来判断是否翻页,即
if len(indolist) < 7: #当单个主题爬取链接数小于7时说明需要翻页
那么需要翻多少页呢,观察之后发现,一般写真都在120以下,所以我直接把页面设为25,一页为5张,合起来就是25×5=125张,至于多的也不用担心,函数中有列表长度的判断,返回的空列表并不会被追加进来

然后是分页的url拼接,这里需要注意的是,主页和分页的不同
第一页为4个数字,而第N页是4个数字之后加上_再加上页数
而我们主页链接列表中的url已经是一个完整的url,需要在中间插入数字,怎么办呢?
这里我们使用到了.replace方法,先把固定的.html替换成空,再在后面加上_和数字,再加上.html,就可以构成新的url了
在我们获取了一个主题所有的图片链接之后就可以进行图片保存了,这个图片保存的函数是放在if判断语句之外的,因为不管判断结果是否为真,我们都需要保存图片
最后,在每爬取完一个主题之后,我们用clear函数把临时存放图片链接的indolist列表清空,为下一次循环做准备
在所有的图片爬取完成之后,我们输出爬取完成,并打印所花费的时间
