最近在自学爬虫,自己也做了一些实例,(自认为)写的比较好的就是对整个网站的爬取了。接下来我将会说一说我使用的工具及方法。
注意:为了尊重网站维护人员,我只爬取了网站首页的24个套图,其余的,需要的小伙伴可以自行添加翻页操作!!!
编译环境和所需库
IDE: PyCharm Community Edition 2020.1 x64
python版本: 3.8.2
所需库: requests , BeautifulSoup4,os,time,random
废话不多说,我们直接开始:
第一件事就是导库了,这个简单,就不用我多说了吧。直接上代码:
import os
import time
from random import random
import requests
from bs4 import BeautifulSoup
这第二件事当然是要去请求网站了,但是我们了解到该网站有反爬机制,所以我们必须先包装一下自己。
ref_url='https://www.mzitu.com'
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36 Edg/81.0.416.53',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'max-age=0',
'Referer': ref_url
}
问:为什么 headers 字段中 Referer 的值要设为全局变量?
答:因为我在测试代码可行性的时候发现,当 Referer 的值为一个定值时,请求大量数据,就会触发反爬机制,导致出现下标越界(或者是找不到某个标签)的异常。从而导致爬取中断。另外根据网站的反爬机制,Referer 字段是必须要有的。
当我们打开主页就会发现,主页上有24个套图链接,每个套图链接就是一套写真。所以,我们首先爬取的应该是主页上这24个套图链接:
打开浏览器进入主页后查看源码,我们可以看到,首页中24个套图链接都在 ‘ li ’ 标签里面。
所以在代码中 ,我先用 BeautifulSoup 解析源码,将每套图的 ‘ li ’ 标签保存到 Temp_list 中。
再利用 for 提取出每个套图链接,再次进行解析。取出每个 ’ a ’ 标签属性中的 ’ href ’ 字段的值。
又将 ’ href ’ 字段的值作为关键字,保存每套图的名字(每个 ’ img ’ 标签属性中的 ’ alt ’ 字段的值),生成一个字典。
最后,提示当前页面的所有链接都保存成功。
代码如下:
def get_main_url(url): # 获取每个主页的24个图片链接
res = requests.get(url,headers=header)
soup = BeautifulSoup(res.text, 'html.parser')
Temp_list = soup.find_all('li')[8:]
# [8:] 的目的是过滤掉多余的 li 标签
for lab_list in Temp_list:
div = BeautifulSoup(str(lab_list), 'html.parser')
main_url.append(div.a.attrs['href'])
# 每个主页中套图详情列表链接
name_dict[div.a.attrs['href']] = div.img.attrs['alt']
# 每个主页中套图名字列表
print("获取当前页面所有链接成功!")
来到这里,我首先用 for 挨个取出这24个套图链接。
然后取出每一个套图链接的名字,判断是否有同名文件夹,如果有,则跳过,如果没有,则创建。
接着对每个链接进行解析,找出该套图链接中的图片页数。
紧接着,利用 for 进行翻页操作。
for 每翻一页,就对当前页面进行解析。
通过 BeautifulSoup 解析后,取出 ’ div ’ 标签下, ’ img ’ 属性中的 ‘ arc ’ 字段的值,即为高清图片的地址。
通过 requests 库请求该图片链接(同样要带上 header ,不然仍然会触发反爬,导致图片出错)。
接下来利用 ‘ split ’ 对图片链接进行处理,取出当前图片名字作为图片名。
【 ’ split ‘ 不会用?点击查看使用方法】
最后,将转换为二进制的图片保存下来。
注意:当我们以一定频率或者速度(requests请求速度几乎是一定的)去大量的连续请求时,还是会触发反爬,所以在每爬完一张照片后开始随机休眠。(如果还是不够的话,可以写成 ’ time.sleep(random() * 5) ’ ,数字随便定,不过要注意:数字太大下载会很慢,太小会触发反爬,根据情况可以自行设置)
代码如下:
def get_image():
global main_num,ref_url
for now_url in main_url:
main_num+=1 #第 main_num 套图
ref_url=now_url #修改当前的 Referer
if os.path.exists(path + '/' + name_dict[now_url]):
pass
else:
os.mkdir(path + '/' + name_dict[now_url])
response = requests.get(now_url, headers=header)
soup = BeautifulSoup(response.text, 'html.parser')
page_nums = soup.find('div', attrs={
'class': "pagenavi"})
page_num = BeautifulSoup(str(page_nums), 'html.parser').find_all('span')[-2].string
for i in range(1, int(page_num) + 1):
now_new_url = now_url + '/' + '{}'.format(i)
new_response = requests.get(now_new_url, headers=header)
Temp = BeautifulSoup(new_response.text, 'html.parser')
image_url = Temp.find('div', attrs={
'class': "main-image"}).img.attrs['src']
image = requests.get(image_url,headers=header)
name = str(image_url).split('/', 5)[-1]
with open(path + '/' + name_dict[now_url] + '/' + name, 'wb') as f:
f.write(image.content)
print('正在爬取第'+str(main_num)+'个图库的第'+str(i)+'张图片,本页共'+page_num+'张照片。')
time.sleep(random())
主要功能写完后,就可以开始用主函数调用了:
if __name__ == '__main__':
start_time = time.time()
get_main_url(url)
get_image()
end_time = time.time()
print('耗时:' + str(end_time - start_time))
运行时展示:
最最最最最后:
附上我总结了三天经验后写出的源码:
import os
import time
from random import random
import requests
from bs4 import BeautifulSoup
path = './spider'
if os.path.exists(path):
pass
else:
os.mkdir(path)
url = 'https://www.mzitu.com'
main_url = []
name_dict = {
}
main_num = 0
ref_url='https://www.mzitu.com'
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36 Edg/81.0.416.53',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'max-age=0',
'Referer': ref_url
}
def get_main_url(url): # 获取每个主页的24个图片链接
res = requests.get(url,headers=header)
soup = BeautifulSoup(res.text, 'html.parser')
Temp_list = soup.find_all('li')[8:]
for lab_list in Temp_list:
div = BeautifulSoup(str(lab_list), 'html.parser')
main_url.append(div.a.attrs['href']) # 每个主页中图片详情列表链接
name_dict[div.a.attrs['href']] = div.img.attrs['alt']
print("获取当前页面所有链接成功!")
# 图片标签
# 页码标签
def get_image():
global main_num,ref_url
for now_url in main_url:
main_num+=1
ref_url=now_url
if os.path.exists(path + '/' + name_dict[now_url]):
pass
else:
os.mkdir(path + '/' + name_dict[now_url])
response = requests.get(now_url, headers=header)
soup = BeautifulSoup(response.text, 'html.parser')
page_nums = soup.find('div', attrs={
'class': "pagenavi"})
page_num = BeautifulSoup(str(page_nums), 'html.parser').find_all('span')[-2].string
for i in range(1, int(page_num) + 1):
now_new_url = now_url + '/' + '{}'.format(i)
new_response = requests.get(now_new_url, headers=header)
Temp = BeautifulSoup(new_response.text, 'html.parser')
image_url = Temp.find('div', attrs={
'class': "main-image"}).img.attrs['src']
image = requests.get(image_url,headers=header)
name = str(image_url).split('/', 5)[-1]
with open(path + '/' + name_dict[now_url] + '/' + name, 'wb') as f:
f.write(image.content)
print('正在爬取第'+str(main_num)+'个图库的第'+str(i)+'张图片,本页共'+page_num+'张照片。')
time.sleep(random())
if __name__ == '__main__':
start_time = time.time()
get_main_url(url)
get_image()
end_time = time.time()
print('耗时:' + str(end_time - start_time))