爬取妹子图(requests + BeautifulSoup)
刚刚入门爬虫,今天先对于单个图集进行爬取,过几天再进行翻页爬取。
使用requests库和BeautifulSoup库
目标网站:妹子图
今天是对于单个图集的爬取,就选择一个进行爬取,我选择的链接为:http://www.mzitu.com/123114
首先网站的分析,该网站有一定的反爬虫策略,所以应对就是加入headers(目前是小白,目前不知道具体为毛这样做)
Hostreferer = { 'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', 'Referer':'http://www.mzitu.com' } Picreferer = { 'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', 'Referer':'http://i.meizitu.net' }
前一个头作为请求网站,后一个头作为破解盗链使用
获得页面HTML代码
用requests库的get方法,加上Hostreferer
def get_html(url):#获得页面html代码 req = requests.get(url, headers=Hostreferer) html = req.text return html
获得图集名称以及图集最大页数
分析网页构成如图所示,图集名称包含在h2标签内,且该标签在整个HTML代码里有唯一的class="main-title",
而最大页数只是被span标签包含,无法通过属性来提取。所以提取图集名称采取标签名+属性名一起提取,而最大页数就采取将span标签全部找出,最大页数在span标签中第11位,
def get_page_name(url):#获得图集最大页数和名称 html = get_html(url) soup = BeautifulSoup(html, 'lxml') span = soup.findAll('span') title = soup.find('h2', class_="main-title") return span[10].text, title.text
获得图片url链接
分析页面内容,含有图片链接的img标签中有一个alt属性的值是跟图集名称相同,可以用这个来直接找到这个标签,当然也可以先找到div标签中的class属性是main-inage,再找到img的src属性,这里我就采用第一种方法。
def get_img_url(url, name): html = get_html(url) soup = BeautifulSoup(html, 'lxml') img_url = soup.find('img', alt= name) return img_url['src']
将图片存入本地
得到图片url链接之后要讲图片存到本地,在请求图片url的时候要加入Picreferer,否则网站会认为你是一个爬虫,会返还给你一个盗链图
该方法传入的参数有3个,第一个是图片url,第二个当前图片的页数,用作创建文件,第三个是图集名称,在存储之前先创建了一个名称是图集名称的文件夹,这样就能将图片存入指定文件夹
def save_img(img_url, count, name): req = requests.get(img_url, headers=Picreferer) with open(name+'/'+str(count)+'.jpg', 'wb') as f: f.write(req.content)
爬取一个图集完整代码
import requests from bs4 import BeautifulSoup import os Hostreferer = { 'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', 'Referer':'http://www.mzitu.com' } Picreferer = { 'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', 'Referer':'http://i.meizitu.net' } def get_page_name(url):#获得图集最大页数和名称 html = get_html(url) soup = BeautifulSoup(html, 'lxml') span = soup.findAll('span') title = soup.find('h2', class_="main-title") return span[10].text, title.text def get_html(url):#获得页面html代码 req = requests.get(url, headers=Hostreferer) html = req.text return html def get_img_url(url, name): html = get_html(url) soup = BeautifulSoup(html, 'lxml') img_url = soup.find('img', alt= name) return img_url['src'] def save_img(img_url, count, name): req = requests.get(img_url, headers=Picreferer) with open(name+'/'+str(count)+'.jpg', 'wb') as f: f.write(req.content) def main(): old_url = "http://www.mzitu.com/123114" page, name = get_page_name(old_url) os.mkdir(name) for i in range(1, int(page)+1): url = old_url + "/" + str(i) img_url = get_img_url(url, name) #print(img_url) save_img(img_url, i, name) print('保存第' + str(i) + '张图片成功') main()
在main方法中先请求到图集的名称和最大页数,并且使用名称创建一个文件夹来存储图片。再从1到最大页数做一个for循环,
然后图片的每一页是 图集首页 + / + 当前页数,得到含有图片内容的url链接,后面就可以将得到图片存入本地。
爬取结果
文件夹名称即为图集名称,内部图片以页数作为文件名。
下面准备开始多个页面的爬取,先将前面爬取一个图集的方法进行封装
爬取一个图集
在进行爬取一个图集前先加入一个方法,在爬取图集名称的时候,由于名称的字符不限,所以可能出现含有文件夹中不能出现的一些字符,例如:/ \ : ? < > 等
所以需要将前面的代码进行修改,加入一个rename方法,将这些字符换成可行的字符。(在这里我就直接将这些字符去掉)
这里采用re库,将name中含有的非法字符换成空,可以看做直接去掉。
import re def rename(name): rstr = r'[\/\\\:\*\?\<\>\|]' new_name = re.sub(rstr, "", name) return new_name def save_one_atlas(old_url): page, name = get_page_name(old_url) new_name = rename(name) os.mkdir(new_name) print("图集--" + name + "--开始保存") for i in range(1, int(page)+1): url = old_url + "/" + str(i) img_url = get_img_url(url, name) # print(img_url) save_img(img_url, i, name) print('正在保存第' + str(i) + '张图片') print("图集--" + name + "保存成功")
爬取一整页图集
def get_atlas_list(url): req = requests.get(url, headers=Hostreferer) soup = BeautifulSoup(req.text, 'lxml') atlas = soup.find_all(attrs={'class':'lazy'}) atlas_list = [] for atla in atlas: atlas_list.append(atla.parent['href']) return atlas_list
分析一个页面中的url链接,发现如果找 target="_blank" 这一属性,则会产生很多多余的链接,所以我直接从字标签入手找到属性 class="lazy"的img标签,然后再在寻找到img标签的父标签中的href
def save_one_page(start_url): atlas_url = get_atlas_list(start_url) for url in atlas_url: save_one_atlas(url)
将爬取一整夜图集进行封装,方便后续的翻页爬取
翻页爬取
分析页面url,发现每一页均是初始网址+page/+页数/
第一页是初始网址,但是页数为1的链接也是直接进入第一页,所以所有页的url就可以用以上通式改变页数进行遍历。
start_url = "http://www.mzitu.com/" for count in range(1, 3): url = start_url + "page/" + str(count) +"/" save_one_page(url)
这里作为测试,所以只爬取前两页图集。改变range内的参数,即可改变爬取页数。
完整代码
1 import requests 2 from bs4 import BeautifulSoup 3 import os 4 import re 5 6 Hostreferer = { 7 'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', 8 'Referer':'http://www.mzitu.com' 9 } 10 Picreferer = { 11 'User-Agent':'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', 12 'Referer':'http://i.meizitu.net' 13 } 14 15 def get_page_name(url):#获得图集最大页数和名称 16 html = get_html(url) 17 soup = BeautifulSoup(html, 'lxml') 18 span = soup.findAll('span') 19 title = soup.find('h2', class_="main-title") 20 return span[10].text, title.text 21 22 def get_html(url):#获得页面html代码 23 req = requests.get(url, headers=Hostreferer) 24 html = req.text 25 return html 26 27 def get_img_url(url, name): 28 html = get_html(url) 29 soup = BeautifulSoup(html, 'lxml') 30 img_url = soup.find('img', alt= name) 31 return img_url['src'] 32 33 def save_img(img_url, count, name): 34 req = requests.get(img_url, headers=Picreferer) 35 new_name = rename(name) 36 with open(new_name+'/'+str(count)+'.jpg', 'wb') as f: 37 f.write(req.content) 38 39 def rename(name): 40 rstr = r'[\/\\\:\*\?\<\>\|]' 41 new_name = re.sub(rstr, "", name) 42 return new_name 43 44 def save_one_atlas(old_url): 45 page, name = get_page_name(old_url) 46 new_name = rename(name) 47 os.mkdir(new_name) 48 49 print("图集--" + name + "--开始保存") 50 for i in range(1, int(page)+1): 51 url = old_url + "/" + str(i) 52 img_url = get_img_url(url, name) 53 # print(img_url) 54 save_img(img_url, i, name) 55 print('正在保存第' + str(i) + '张图片') 56 print("图集--" + name + "保存成功") 57 58 59 def get_atlas_list(url): 60 req = requests.get(url, headers=Hostreferer) 61 soup = BeautifulSoup(req.text, 'lxml') 62 atlas = soup.find_all(attrs={'class':'lazy'}) 63 atlas_list = [] 64 for atla in atlas: 65 atlas_list.append(atla.parent['href']) 66 return atlas_list 67 68 def save_one_page(start_url): 69 atlas_url = get_atlas_list(start_url) 70 for url in atlas_url: 71 save_one_atlas(url) 72 73 74 if __name__ == '__main__': 75 start_url = "http://www.mzitu.com/" 76 for count in range(1, 3): 77 url = start_url + "page/" + str(count) +"/" 78 save_one_page(url) 79 print("爬取完成")
这个程序能够爬取图片,但是效率太低,正在学习多进程,希望之后可以提高该程序的爬取效率。
我爬了3个多G的图片花了我接近4个小时,足以证明效率是真的低。
并且在爬取过程中出现一个问题,提示 你的主机中的软件中止了一个已建立的连接 ,这个问题还未找到解决方法,也未找明产生的具体原因。
这是我写的第一篇较长的博客,还有很多未完善的地方,希望大家见谅。