爱美之心人皆有之,去下载美女图片吧。
百度随便搜索一下:美女图,找到mm131.com网站。
以“性感车模”为例(http://www.mm131.com/chemo/
),尝试编写一个下载图片的爬虫程序。
车模图片示例
结果:仅此“性感车模”一类图片达到惊人的16347张。
所以,实践中以切片选取部分来验证程序正确性,全部下载完太累,也太慢。
图片数量太惊人,请以切片实验
一、车模页面链接(导航)的构建
“性感车模”地址入口:http://www.mm131.com/chemo/
点击第3页,查看底部车模页面链接源代码,可以发现:
<dd class="page"> <a href="/chemo/" class="page-en">首页</a> <a href="list_3_2.html" class="page-en">上一页</a> <a href="/chemo/" class="page-en">1</a> <a href="list_3_2.html" class="page-en">2</a> <span class="page_now">3</span> <a href="list_3_4.html" class="page-en">4</a> <a href="list_3_5.html" class="page-en">5</a> <a href="list_3_6.html" class="page-en">6</a> <a href="list_3_7.html" class="page-en">7</a> <a href="list_3_8.html" class="page-en">8</a> <a href="list_3_9.html" class="page-en">9</a> <a href="list_3_10.html" class="page-en">10</a> <a href="list_3_4.html" class="page-en">下一页</a> <a href="list_3_10.html" class="page-en">末页</a></dd>
于是构建出导航地址的完整链接:
第1页:http://www.mm131.com/chemo/
第2页:http://www.mm131.com/chemo/list_3_2.html
第3页:http://www.mm131.com/chemo/list_3_3.html
……
最后一页(末页):http://www.mm131.com/chemo/list_3_10.html
代码片断如下:
def model_nav_links(): page_urls = [] page_urls.append('http://www.mm131.com/chemo/') for page_index in range(2, 11): page_urls.append('http://www.mm131.com/chemo/list_3_' + str(page_index) + '.html') return page_urls
思考:为什么是10?如果更多呢?如何以程序方式获取页面数?
网站链接分析
二、页面上每位车模的链接入口
查看源代码发现,每位车模的链接入口并没有规律,类似http://www.mm131.com/chemo/****.html
。
<dd><a target="_blank" href="http://www.mm131.com/chemo/525.html"><img class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="http://img1.mm131.me/pic/525/m525.jpg" alt="美女车模小乔黑色吊带丝袜诱惑" width="120" height="160" />美女车模小乔黑色吊带丝</a></dd>
采取的处理方法:用BeautifulSoup库来解析页面,使用soup对象的select()获取每位车模的链接入口,以及对应的车模图片img标签的alt属性(图片替代文本)。
取此文字作图片存放文件夹名称使用。
代码片断如下:
page_soup = BeautifulSoup(page_html, 'html.parser') page_a_links = page_soup.select('body > div.main > dl > dd a')for page_a_link in page_a_links: # 有img标签的是车模 if page_a_link.find('img'): page_model_hrefs.append(page_a_link.get('href')) page_model_texts.append(page_a_link.find('img').get('alt'))
车模首页_页面总数_图片地址
三、某位车模的图片总页面数及图片页面地址构建
图片总页数用正则表达式 r'共(\d.*?)页'
匹配获取。
图片页面地址则根据其规律,直接用url.replace('.html', '_'+str(i)+'.html')
来实现。
这里的实现代码是不是有点眼熟?
代码片断如下:
def get_img_page_links(url): """构建某位车模的所有图片页面链接""" # url: 某位车模链接首页。返回:某位车模所有页面链接列表 # 加入第1页 img_pages = [url] # 获取图片总页数img_page_total img_html = get_html(url).text img_soup = BeautifulSoup(img_html, 'html.parser') img_page_nums = img_soup.select('body > div.content > div.content-page > span') img_page_total = re.findall(r'共(\d.*?)页', img_page_nums[0].string)[0] # 生成相应的图片页面地址 for i in range(2, int(img_page_total)+1): # 页面构建方式: # 第1页:http://www.mm131.com/chemo/1603.html # 第2页:http://www.mm131.com/chemo/1603_2.html img_href = url.replace('.html', '_'+str(i)+'.html') img_pages.append(img_href) return img_pages
四、某位车模的某张图片地址
车模某张图片的选择器:body > div.content > div.content-pic > a > img
运行后发现最后一位车模的最后一张图片会出错!
检查http://www.mm131.com/chemo/20_15.html
发现:
其它图片点击会链接跳转到下一页图片,而最后一张图片并没有链接。
此时要改变选择器:body > div.content > div.content-pic > img
获取图片地址后,下载大法上演。
五、下载图片
比如获取一张图片地址:http://img1.mm131.me/pic/20/15.jpg
直接访问会出现403错误(资源不能访问)。You do not have permission to get URL '/pic/20/15.jpg' from this server.
很显然,这是网站采取的反爬虫机制之一。
Chrome浏览器F12开发者工具
继续使用的Chrome浏览器F12开发者工具,选“Network”项,按F5刷新网页,此时会显示网络请求及响应信息。
找到图片15.jpg
,会发现Request Headers部分有数据请求头要求。
重新梳理一下:
我们浏览的网页是:http://www.mm131.com/chemo/20_15.html
,相对应headers部分的Referer
。
我们要的图片地址是:http://img1.mm131.me/pic/20/15.jpg
,headers部分的Host
对应图片地址中的域名。
构建请求头数据(字典),代码片断如下:
headers = {} headers["Accept"] = "image/webp,image/*,*/*;q=0.8"headers["Accept-Encoding"] = "gzip, deflate, sdch"headers["Accept-Language"] = "zh-CN,zh;q=0.8,en;q=0.6"headers["Cache-Control"] = "no-cache"headers["Connection"] = "keep-alive"headers["Host"] = get_from_host(img_url) headers["Pragma"] = "no-cache"headers["Referer"] = img_from_url headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64)<省略……>"
至此,辛苦的分析完毕!
Talk is cheap. Show me the code --Linus
#!/usr/bin/env python# -*- coding: utf-8 -*-# @Time : 2018-05-01 21:27# @Author : AntsPi.com# @File : car_show_model.pyimport osimport timeimport reimport requestsfrom bs4 import BeautifulSoupfrom urllib.parse import urlparsedef get_from_host(url): # 网址解析 from urllib.parse import urlparse urlParse = urlparse(url) # print(urlParse.netloc) return urlParse.netlocdef get_html(url): try: headers = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/" "537.36 (KHTML, like Gecko) Chrome/" "42.0.2311.90 Safari/537.36"} r = requests.get(url, headers=headers, timeout=30) r.raise_for_status() r.encoding = 'gb2312' return r except Exception as e: print('Error: ', e) returndef model_nav_links(): page_urls = [] page_urls.append('http://www.mm131.com/chemo/') for page_index in range(2, 11): page_urls.append('http://www.mm131.com/chemo/list_3_' + str(page_index) + '.html') return page_urlsdef get_img_page_links(url): """构建某位车模的所有图片页面链接""" # url: 某位车模链接首页。返回:某位车模所有页面链接列表 # 加入第1页 img_pages = [url] # 获取总页数img_page_total img_html = get_html(url).text img_soup = BeautifulSoup(img_html, 'html.parser') img_page_nums = img_soup.select('body > div.content > div.content-page > span') img_page_total = re.findall(r'共(\d.*?)页', img_page_nums[0].string)[0] # 生成相应的图片页面地址 for i in range(2, int(img_page_total)+1): # 页面构建方式: # 第1页:http://www.mm131.com/chemo/1603.html # 第2页:http://www.mm131.com/chemo/1603_2.html img_href = url.replace('.html', '_'+str(i)+'.html') img_pages.append(img_href) return img_pagesdef download_img(url, dir_name): """下载1张图片""" # url: 车模页面的链接(页面里面含有1张图片) # dir_name: 下载后图片保存的文件夹名 # 获取车模图片的地址 img_html = get_html(url).text img_soup = BeautifulSoup(img_html, 'html.parser') img_href = img_soup.select('body > div.content > div.content-pic > a > img') if img_href: img_url = img_href[0].get('src') else: # 最后一页是没有链接a标签的,要直接找img标签。 img_href = img_soup.select('body > div.content > div.content-pic > img') img_url = img_href[0].get('src') # 下载后的图片文件名 only_file_name = os.path.basename(img_url) save_file_name = os.path.join(dir_name, only_file_name) # 文件不存在 或 文件长度为0时,下载数据 if not os.path.exists(save_file_name) or os.path.getsize(save_file_name) == 0: print('正在下载:{} ...'.format(save_file_name)) get_img_data(url, img_url, save_file_name) time.sleep(0.5) else: print('文件已存在:{} ...'.format(save_file_name))def get_img_data(img_from_url, img_url, file_name): """下载图片数据""" # img_from_url: 包含图片的页面链接地址 # img_url: 图片的地址 # file_name: 下载后的图片文件名 # 构建请求头数据(字典)。不加会被网站拒绝(反爬虫) headers = {} headers["Accept"] = "image/webp,image/*,*/*;q=0.8" headers["Accept-Encoding"] = "gzip, deflate, sdch" headers["Accept-Language"] = "zh-CN,zh;q=0.8,en;q=0.6" headers["Cache-Control"] = "no-cache" headers["Connection"] = "keep-alive" headers["Host"] = get_from_host(img_url) headers["Pragma"] = "no-cache" headers["Referer"] = img_from_url headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36" try: r = requests.get(img_url, headers=headers, timeout=30) r.raise_for_status() if r.status_code == 200: with open(file_name, 'wb') as fw: fw.write(r.content) except Exception as e: print('Error: ', e) returnif __name__ == '__main__': # 图片计数 img_total_num = 0 # 车模页面的链接 page_links = model_nav_links() # 页面上车模们的链接及对应说明文字 page_model_hrefs = [] page_model_texts = [] # 实验时只用了 1 页page_links[:1] for page_link in page_links[:1]: page_html = get_html(page_link).text page_soup = BeautifulSoup(page_html, 'html.parser') page_a_links = page_soup.select('body > div.main > dl > dd a') for page_a_link in page_a_links: # page_a_link 的类型是:class 'bs4.element.Tag # 有img标签的是车模 if page_a_link.find('img'): page_model_hrefs.append(page_a_link.get('href')) page_model_texts.append(page_a_link.find('img').get('alt')) # 图片存放总文件夹 model_save_dir = 'model' model_save_dir = os.path.join(os.getcwd(), model_save_dir) if not os.path.exists(model_save_dir): os.mkdir(model_save_dir) # 网页链接索引编号及网页链接。实验时取3位车模[:3] for i, page in enumerate(page_model_hrefs[:3]): # 以车模的文字作为文件夹名 img_save_dir = os.path.join(model_save_dir, page_model_texts[i]) if not os.path.exists(img_save_dir): os.mkdir(img_save_dir) # 单个车模的页面链接page(某位车模的首页链接) for img_page_link in get_img_page_links(page): img_total_num = img_total_num + 1 print('No.{}'.format(str(img_total_num))) download_img(img_page_link, img_save_dir)
原创初稿:2018-05-03
作者:代码小工蚁
链接:https://www.jianshu.com/p/915eee3261c6
共同学习,写下你的评论
评论加载中...
作者其他优质文章