很久之前就写了这个爬妹子图的Python爬虫,并且开源到Github上,然而居然没有人给小心心,虽然现在失效了,妹子图网站开启了反爬虫机制,但还是可以作为大家学习的样例,注释也很清晰。
0x01 起因
很多人一开始学习编程技术都是源于兴趣,小东当时看到某博主,居然用Python爬虫批量下载图片,当时就给我震惊了
于是瞬间激发了我的学习兴趣,最后也就促成了这个GetMM爬虫项目。
0x02 分析
拿到妹子图的网站,先对网页结构进行了分析,然后使用python3.5版本,利用BeautifulSoup模块和urlib模块,同时实现了多线程抓取,下载,保存到对应文件夹的整个一个流程。
我知道你们看代码肯定很无聊,所以先放上一张成果图…
0x03 核心代码如下#这是主文件 This is the main file!
#coding: utf-8
import argparse #argparse模块的作用是用于解析命令行参数
import urllib
import requests#urllib的request模块抓取URL内容
import os#系统啦
#看到以上的引入,所以你需要配置这些环境,urllib和BeautifulSoup,我觉得真的是很强大,有Urlib2更新了,学习一波吧!
#不得不说正则表达,给我带来的感觉是非常不错的,这也体现了程序的便利性!-技术改变生活-2017-04-27
from MuchThread import MuchThread #引入多线程
from urllib import request #引入request
from bs4 import BeautifulSoup #BeautifulSoup 4.x引入,beautifulsoup可以从HTML或XML文件中提取(解析)数据的Python库
#得到网页HTML的内容,源代码 get_html(URL)
def get_html(url_address):
#构造请求头的客户端headers
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
#urllib.request.Request(url,date,headers)
#获取网页
req = urllib.request.Request(url=url_address,headers=headers)
return urllib.request.urlopen(req)
#小东留名!啊哈哈-www.dyboy.cn
#get_soup(html) 将urlib获取的html封装到bs中,方便文档处理,返回bs的一个实例
def get_soup(html):
#判断是否成功获取了网页
if html == None:
return None
#使用方法BeautifulSoup(markup, "html.parser")
#html.read(),读取当前对象html文档的内容,html转化为unicode编码的,解析成xml
return BeautifulSoup(html.read(), "html.parser")
#get_img_dirs(soup),获取所有相册标题&链接,参数为bs的一个实例,返回字典(key:标题,value:内容)
def get_img_dirs(soup):
#判断对象是否存在
if soup == None:
return None
#bs.find(tag,attributes,recursive,text,keywords)函数
#bs.findAll(tag,attributes,recursive,text,limit,keywords)
#find(id="pic")find(name,attrs,recursive,text,**wargs)
'''这些参数相当于过滤器一样可以进行筛选处理,不同的参数过滤可以应用到以下情况:
查找标签,基于name参数
查找文本,基于text参数
基于正则表达式的查找
查找标签的属性,以及基于attrs参数
基于函数的查找'''
# soup.find(id="pins").findAll(name='li'),找到id="pins",下所有的li标签
lis = soup.find(id="pins").findAll(name='li') # findAll(name='a') # attrs={'class':'lazy'}
if lis != None:
img_dirs = {};#新的存储相册目录字典
for li in lis:
links = li.find('a')#找到li标签下的最近一个a标签内容
alt = links.find('img').attrs['alt']#在a标签内容中找到最近的img的属性alt的值
site = links.attrs['href']#在a标签下的属性href值为相册地址
img_dirs[alt] = site;#alt相册名字,site相册链接
print(img_dirs)#输出获取到的相册字典
return img_dirs#返回字典
#获取相册中的内页图片get_album_num(links, dir_soup),links:链接,get_soup:一个bs对象,返回图片总数
def get_album_num(links, dir_soup):
##############后期修改的地方#######################
#找到links页面下属性class为pagenavi的div
divs = dir_soup.findAll(name='div', attrs={'class':'pagenavi'})
navi = divs[0]#有相同的取第一个
code = navi['class']#code 为 div下class对应的值-pagenavi
links2 = navi.findAll(name='a')
if links2 == None:
return None
a = []
url_list = []
#循环获取div下的数字
for link in links2:
h = str(link['href'])#获取links2下的href的属性值-网址,转化为字符串便于使用str.replace(old, new[, max])
n = h.replace(links+"/", "")#得到字符数字
#异常处理,把int(n)加到a[]的末尾
try:
a.append(int(n))
except Exception as e:
print(e)
_max = max(a)#获取a[]下最大的数字,即为最大页数
for i in range(1, _max):
u = str(links+"/"+str(i))#构造页面链接
url_list.append(u)#添加到链接列表
return url_list#返回链接列表
#获取相册下的图片download_img_from_page(name, page_url)
def download_img_from_page(name, page_url):
dir_html = get_html(page_url)#获取网页HTML
dir_soup = get_soup(dir_html)#封装到bs中
# 得到当前页面的图片
main_image = dir_soup.findAll(name='div', attrs={'class':'main-image'})#找到当前页面下的“main-image”对应的div
if main_image != None:
for image_parent in main_image:
imgs = image_parent.findAll(name='img')#找到img标签的内容
if imgs != None:
img_url = str(imgs[0].attrs['src'])#找到第一个img下属性src的值,转换为字符串
filename = img_url.split('/')[-1]#文件名为src的网址用split分割/,取最后一个作为文件名,避免重复
print("开始下载:" + img_url + ", 保存为:"+filename)
save_file(name, filename, img_url)#保存
#定义保存图片save_file()
def save_file(name, filename, img_url):
print(img_url+"=========")
img = requests.get(img_url)#获取图片
name = str(name+"/"+filename)#文件夹名
#with expresion as variable
with open(name, "wb") as code:
code.write(img.content)
#download_imgs(info):下载图片函数info为一个字典(相册列表)
def download_imgs(info):
if info == None:
return
name = info[0]#album->alt
links = info[1]#album->href
if name == None or links == None:
return None#获取内容失败
print("正在创建相册:" + name +" " + links)
#异常处理
try:
os.mkdir(name)#当前目录创建文件夹 还有makedirs(path[,mode]) mode默认0777
except Exception as e:
print("文件夹:"+name+",已经存在了,呱唧~")
print("正在获取相册《" + name + "》内,图片的数量...")
dir_html = get_html(links)#获取网页内容,返回HTML
dir_soup = get_soup(dir_html)#编码HTML,封装到bs中
img_page_url = get_album_num(links, dir_soup)#获取当前相册下的照片数量
'''
# 得到当前相册的封面,Just for a test!
main_image = dir_soup.findAll(name='div', attrs={'class':'main-image'})
if None != main_image:
for image_parent in main_image:
imgs = image_parent.findAll(name='img')
if None != imgs:
img_url = str(imgs[0].attrs['src'])
filename = img_url.split('/')[-1]
print("开始下载:" + img_url + ", 保存为:"+filename)
save_file(t, filename, img_url)'''
# 获取相册下的图片
for photo_web_url in img_page_url:
download_img_from_page(name, photo_web_url)
#main
if __name__ == '__main__':
parser = argparse.ArgumentParser()#命令行下
parser.add_argument("echo")
# parser.add_argument("url")
# url = int(args.url)
args = parser.parse_args()
url = str(args.echo)
print("开始解析:" + url)
html = get_html(url)#获取用户输入的链接的相册列表网页
soup = get_soup(html)#封装至bs
img_dirs = get_img_dirs(soup)#获取相册的名字及链接
if img_dirs == None:
print("呱唧!无法获取该网页下的相册内容...")
else:
for d in img_dirs:
my_thread = MuchThread(download_imgs, (d, img_dirs.get(d)))
my_thread.start()
my_thread.join()
0x04 总结
其实python写爬虫真的很简单,之前在CSDN上发的文章很细节,同时在Github上有对应的源码,小东就不再赘述了,如果您有任何问题,欢迎交流!
项目开源地址:Github