爬虫进阶:CrawlSpider爬取169ee全站美女图片
CrawlSpider
前面,我们用了scrapy中的CrawlSpider爬取了糗事百科中大量段子数据。
但是,qiubai这个爬虫没有充分利用CrawlSpider的优点。其实,在qiubai这个爬虫里面我们只是把CrawlSpider当做普通的Spider用而已。
CrawlSpider继承自Spider,提供了Rule和LinkExtractor,使得爬虫框架能够自动按照规则提取Response中所有符合条件的链接,并且跟进这些链接继续爬取。从而,CrawlSpider非常适合爬取比较规则的网站。并且,我们只需写上合适的Rule和LinkExtractor,就能够避免自己在parse方法中写一大堆提取逻辑来提取链接和数据。
使用了CrawlSpider的特性(Rule和LinkExtractor)之后,我们只需要关注那些我们感兴趣的页面,编写parse方法从这些页面对应的Response上提取item数据即可。继续跟进和爬取页面的事情让CrawlSpider做就好。
让我们再来看一下Rule和LinkExtractor的作用:
首先,我们看一下CrawlSpider的主要属性和方法:
rules:列表,定义了从Response提取符合要求的链接的Rule对象。
parse_start_url:CrawlSpider默认的回调函数,我们在使用CrawlSpider时,不应该覆盖parse方法,而应该覆盖这个方法。因为CrawlSpider使用了parse函数来处理自己的逻辑,所以我们不能覆盖parse方法。
其中,Rule有以下几个参数:
link_extractor:LinkExtractor对象,用于定义需要提取的链接。Link Extractors是链接提取器,用来从返回网页的Response中提取符合要求的链接。
callback:回调函数,当link_extractor提取到符合条件的链接时,就会把该链接继续处理获得相应的Response,然后把该Response传递给这个函数处理。由于CrawlSpider内部使用了parse方法,所以我们自己设置的该回调函数一般不能为parse方法。callback不为空时,follow为false,表示该提取的链接对应的Response应该传递给callback设置的函数处理,而不应该继续用于跟进。
follow:布尔值,指定了根据link_extractor规则从response提取的链接是否需要跟进,跟进的意思就是指让CrawlSpider对这个提取的链接进行处理,继续拿到该链接的Response,然后继续使用所有的rule去匹配和提取这个Response,然后再不断的做这些步骤。callback为空时,follow默认为true,否则就是false。
process_links:函数或者函数名,用于过滤link_extractor提取到的链接。
process_request:函数或者函数名,用于过滤Rule中提取到的request。
其中,LinkExtractor对象主要有以下几个参数:
allow:字符串或元组,满足括号中所有的正则表达式的那些值会被提取,如果为空,则全部匹配。
deny:字符串或元组,满足括号中所有的正则表达式的那些值一定不会被提取。优先于allow参数。
allow_domains:字符串或元组,会被提取的链接的域名列表。
deny_domains:字符串或元组,一定不会被提取链接的域名列表。
restrict_xpaths:字符串或元组,xpath表达式列表,使用xpath语法和allow参数一起提取链接。
restrict_css:字符串或元素,css表达式列表,使用css语法和allow参数一起提取链接。
分析网页结构
进169ee网我们发现,网站总的来说有三种类型的链接,如图:
编写Rule和LinkExtractor处理上面类型的链接
所以我们在rules变量可以定义不同的rule分别对这三种链接进行提取和跟进。
- 对于相册列表页链接,因为页面中全是相册列表,所以我们只需要跟进该链接即可;
- 对于相册链接,由于里面都是图片,也就是说,页面里面包含我们感兴趣的数据,所以这样的页面,我们应该设置callback函数对Response进行处理,提取出我们需要的数据。而不仅仅是简单的跟进该链接。
- 对于相册翻页链接,由于翻页之后,页面其实跟相册链接页面一样,所以,我们需要把该类型链接的处理设置成跟2中一样。
最终,我们设置的rule列表如下:
Rule(SgmlLinkExtractor(allow=(r'^http://www.169ee.com/[a-z]+$',))),
Rule(SgmlLinkExtractor(allow=(r'^http://www.169ee.com/[a-z]*?/20[0-9]*?/[0-9]*?/[0-9]*?\.html$', r'^http://www.169ee.com/[a-z]*?/20[0-9]*?/[0-9]*?/\d*?_\d*?\.html$')), callback='parse_album'))
更详细的可以参见github源码!
编写parse_album函数,提取相册中的图片
设置好了rules之后,CrawlSpider会自动跟进链接了,所以我们集中精力编写提取图片的代码。
我们进入相册页面查看网页结构,发现,xpath表达式为//div[@id="content"]/div[@class="big-pic"]/div[@class="big_img"]
的div标签中的每一个p标签里面都包含了一张图片。如,
定义Item
所以,提取这里面的所有图片链接,然后提取网页的title作为相册名字,从相册链接中提取年月日,和相册id,再从图片的链接中提取出图片的id。然后,我们定义的item如下:
class OnesixnineItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()_id = scrapy.Field() # 该图片在该相册中的idtitle = scrapy.Field() # 该相册的名字year = scrapy.Field() # 该图片上传的年份folder_num = scrapy.Field() # 该图片是该年份的第几个相册month_day = scrapy.Field() # 该相册是该年份的几月几日创建的url = scrapy.Field() # 该图片的urlalbum_id = scrapy.Field()album_url = scrapy.Field()
提取图片
定义好item之后,我们继续提取图片信息。编写提取函数如下:
class OnesixnineSpider(CrawlSpider):name = "onesixnine"allowed_domains = ['www.169ee.com', '724.169pp.net']start_urls = ['http://www.169ee.com']year_monthday_albumid_pattern = re.compile(r'http://www\.169ee\.com/[a-z]+/(\d*?)/(\d*?)/(\d*?)\.html')image_year_foldernum_imageid_pattern = re.compile(r'http://724\.169pp\.net/169mm/(\d*?)/(\d*?)/(\d*?)\.[a-z]*?')rules = (Rule(LinkExtractor(allow=(r'^http://www.169ee.com/[a-z]+$',))),Rule(LinkExtractor(allow=(r'^http://www.169ee.com/[a-z]*?/20[0-9]*?/[0-9]*?/[0-9]*?\.html$',r'^http://www.169ee.com/[a-z]*?/20[0-9]*?/[0-9]*?/\d*?_\d*?\.html$')),callback='parse_album'))def parse_album(self, response):img_srcs = response.xpath('//div[@id="content"]/div[@class="big-pic"]/div[@class="big_img"]/p/img/@src').extract()if not img_srcs:returnpage_r_index = response.url.rfind('_')slash_r_index = response.url.rfind('/')if page_r_index > slash_r_index:# 当前页面链接中存在分页的部分,去掉当前链接中的分页部分album_url_prefix, album_url_suffix = os.path.split(response.url)album_url = response.urljoin(re.compile(r'_\d+').sub('', album_url_suffix))else:album_url = response.url# 从当前页面链接中解析出该相册的年、月日、相册idyear_monthday_albumid_match = self.year_monthday_albumid_pattern.search(album_url)if not year_monthday_albumid_match:returnyear = year_monthday_albumid_match.group(1)month_day = year_monthday_albumid_match.group(2)album_id = year_monthday_albumid_match.group(3)for img_src in img_srcs:item = OnesixnineItem()item['title'] = response.xpath('/html/head/title/text()').extract_first()item['year'] = yearimage_year_foldernum_imageid_match = self.image_year_foldernum_imageid_pattern.search(img_src)if not image_year_foldernum_imageid_match:continueitem['folder_num'] = image_year_foldernum_imageid_match.group(2)image_local_id = image_year_foldernum_imageid_match.group(3)item['_id'] = year + month_day + album_id + item['folder_num'] + image_local_iditem['month_day'] = month_dayitem['album_id'] = album_iditem['album_url'] = album_urlitem['url'] = img_srcyield item
编写pipeline,保存图片
提取了item之后,scrapy会把Spider中产生的item转移给pipeline处理。所以我们可以在pipeline中保存图片。同时,我们可以保存图片信息到MongoDB中,待日后查看。
编写的pipeline如下:
class OnesixninePipeline(object):logger = logging.getLogger('OnesixninePipeline')connection = pymongo.MongoClient('localhost', 27017)def __init__(self):self.logger.info('pipeline init')self.db = self.connection.scrapy # 切换到scrapy数据库self.collection = self.db.onesixnine # 获取到onesixnine集合def process_item(self, item, spider):self.save_image(item)return itemdef save_image(self, item):if not item:returnif os.path.exists(os.path.dirname(settings.DEFAULT_OUTPUT_FOLDER)):save_folder = settings.DEFAULT_OUTPUT_FOLDERelse:save_folder = settings.CANDIDATE_DEFAULT_OUTPUT_FOLDERif not os.path.exists(save_folder):os.mkdir(save_folder)# 获取图片扩展名ext = os.path.splitext(item['url'])[1]# 得到图片保存的名字image_save_path = os.path.join(save_folder,item['year'] + '_' + item['month_day'] + '_' + item['folder_num'] + '_' + item['album_id'] + '_' + item['_id'] + ext)urllib.request.urlretrieve(item['url'], image_save_path)if not self.connection:return itemself.collection.save(item)def __del__(self):self.logger.info('pipeline exit!')
翻页抓取
我们还漏了一步。在相册链接页面,我们抓取了该页面所有的图片信息,但是一个相册有很多页,我们需要翻页抓取才不会漏掉相册中的图片。所以,我们通过xpath表达式,提取出下一页的链接://div[@id="content"]/div[@class="big-pic"]/div[@class="dede_pages"]/ul/li[last()]/a/@href
,最终,翻页的处理为:
next_page_in_album = response.xpath('//div[@id="content"]/div[@class="bigpic"]/div[@class="dede_pages"]/ul/li[last()]/a/@href').extract_first()
if next_page_in_album and next_page_in_album != '#':next_page_url = response.urljoin(next_page_in_album)yield Request(next_page_url, callback=self.parse_album)
运行,查看结果
使用scrapy crawl onesixnine运行,可以自行查看运行结果
源码
源码在github上
喜欢的可以star,欢迎提issue或者pull request。
喜欢的可以关注微信公众号:
参考
- 我自己的头条号:爬虫进阶:CrawlSpider爬取169ee全站美女图片
爬虫进阶:CrawlSpider爬取169ee全站美女图片相关推荐
- Python爬虫-带你爬取高清美女图片
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 以下文章来源于腾讯云 作者:明天依旧可好 ( 想要学习Python?Python学 ...
- Python爬虫递归调用爬取动漫美女图片
效果 代码,码云地址 import datetime import os import randomfrom lxml import etreeurl_src='https://www.nvshens ...
- python 爬视频下载_Python爬虫进阶之爬取某视频并下载的实现
这篇文章我们来讲一下在网站建设中,Python爬虫进阶之爬取某视频并下载的实现.本文对大家进行网站开发设计工作或者学习都有一定帮助,下面让我们进入正文. 这几天在家闲得无聊,意外的挖掘到了一个资源网站 ...
- 爬虫爬取二次元网站美女图片
爬虫爬取二次元网站美女图片 前言 xpath解析 需求分析 代码编写 总代码 前言 本次需要爬取二次元网站cos板块下的图片 需要用到request模块与lxml模块,所以请提前安装好这两个模块,打开 ...
- Python爬虫之爬取绝对领域美女图片
Python爬虫之爬取绝对领域美女图片 第一步: 导入模块: import requests from lxml import etree 第二步:定义函数: def get_url(start_ur ...
- 2021-7-30 Python-爬虫练手:爬取高质量美女图片
爬取高质量美女图片 目标网站 思路分析 封面页 用bs4确定封面页图片链接 拿到图片链接和名称 保存封面 子页面(套图) 确定子页面位置 获取子页面里图片位置和图片名称 保存组图 完整代码 运行结果 ...
- python爬取街拍美女图片
python爬取街拍美女图片 完整代码: import requests from urllib.parse import urlencode import os from hashlib impor ...
- Java爬虫之利用Jsoup+HttpClient爬取类叔叔不约匿名聊天网站的图片,未果——后爬取某网站美女图片案例
博主最近学了一点爬虫的知识,闲着无聊,秉承学以致用的理念,于是突然想到何不挑战一下,爬取一些叔叔不约网站的图片,来巩固一下所学知识(#滑稽).说干就干,打开eclipse或idea,创建maven工程 ...
- python3.6爬虫案例:爬取朝秀帮图片
一.写在前面 之前写的两篇博客: python3.6爬虫案例:爬取百度歌单点击打开链接: python3.6爬虫案例:爬取顶点小说(爱看小说同学的福利)点击打开链接 第一个案例写了如何在百度音乐歌单中 ...
- python爬虫:新手爬取NASA每日精选图片
作为一位科幻爱好者,初学python后,便想要使用python爬虫爬些漂亮的宇宙图片,于是乎就把目标瞄准了NASA的每日精选图片. 不过话说回来,毕竟NASA这样的官方组织我们还是要看看是否允许爬虫访 ...
最新文章
- mysql 求数据的长度_mysql 如何求数据的长度
- Circuit Breaker(电路熔断器)模式原理
- Flashback Query
- 浅析多个单列索引和联合索引的区别
- linux 关闭web服务器端口,linux(solaris)下如何关掉端口及服务
- java 多项式拟合最多的项数_Matlab概率统计与曲线拟合
- java 7 jboss_JBoss7 入门指南
- 家的N次方 经典台词
- Dynamips ADSL实验之一pppoeoa(工大瑞普修正版)
- php中字符串的截取函数,PHP字符串截取和截取函数的介绍
- THULAC 词性表
- php独孤九剑,(独孤九剑)--PHP 视频学习 -- 文件系统
- TC Games教你如何在电脑上玩手游梦幻西游,无需模拟器
- java blackjack card game_Java BlackJack Game Ace值
- 手机邮箱怎么弄_安卓手机邮箱设置教程 教您如何使用手机接收邮件
- [WPF系列] 高级 调试
- 贵大和云大计算机,云南大学和贵州大学哪个实力更强?有哪些优势学科?
- 一文让你彻底理解关于消息队列的使用
- FPGA视频拼接器 信号裁剪功能
- iOS 全景播放器最简单的解决方案
热门文章
- 盛天海电商:拼多多的定价策略有哪些?
- 小组取什么名字好_如何给公司取一个好名字?让你的公司脱颖而出
- CodeWarrior使用教程第一部分: 认识 CodeWarrior
- python 小说 云_Python 爬虫之网络小说下载
- 使用Android studio开发Android App
- 现在世界上到底有多少飞机?
- 【清华大学】操作系统 陈渝 Part4 ——物理内存管理 之 非连续内存分配
- 「计算机网络」五层因特网协议栈的简要介绍和分组名称
- http://atom8023.blog.51cto.com/2651558/1333582
- matlab rms数据滤波,与RMS相关的5个信息,如何通过RMS结果滤波来提高精度