开头再来波小程序摇一摇:php
上一章节,咱们讲到了经过Rules来获取下一个待爬页面的URL,那么咱们今天就来说讲具体的怎么爬取一个页面。html
由于咱们的目的是爬取整个36页的所有美剧列表,可是在36页数据里面,除了第一页,其余35也的网页都是很规律很整齐的,为啥第一页不同?由于第一页有前三甲的图片,这个咱们须要特殊处理一下。因此,今天这节,咱们主要来讲爬取剩下35页内荣怎么爬。shell
http://www.ttmeiju.me/index.php/summary/index/p/2.html编程
前一篇文章『Scrapy爬虫进阶操做之CrawlSpider(一)』,咱们主要讲到了编写 Rule 来提取下一页的URL:json
rules = ( Rule(LinkExtractor(restrict_xpaths='//ul[@class="pagination"]//a[@class="next"]'), callback='parse_item', follow=True), )复制代码
今天,咱们就来完善咱们这个 Rule 里面的 callback : parse_item() 方法。小程序
咱们要爬取的页面,大概长上图的那个样子。经过观察,看到这里面每项都是很规则的排布,在看网页源码里面,bash
咱们就发现,每一条数据,其实都是一个 tr
标签,这个标签的特色就是 class
里面含有 Scontent
或者 Scontent1
这两个字段。dom
接着在看 tr
标签内部,每一条美剧信息里面有 7 个内容,即:序号,剧名,分类,状态,更新日期,回归日期和倒计时。咱们的目的就是要把这 7 个都摘出来。因此,咱们在 items.py 里面建立一个 scrapy.item 类:scrapy
class TtmjTvPlayItem(scrapy.Item): tv_play_name = scrapy.Field() tv_play_rank = scrapy.Field() tv_play_category = scrapy.Field() tv_play_state = scrapy.Field() tv_play_update_day = scrapy.Field() tv_play_return_date = scrapy.Field() tv_play_counting_data = scrapy.Field() tv_play_url = scrapy.Field()复制代码
这个 scrapy.Item 就是scrapy将爬取结果封装到一个bean中,以后再由用户随便处置这个bean。这些变量就是咱们前面说的那 7 个变量。ide
为了更好的调试,咱们先把 Rule 里面的 follow 去掉,或者改成 False。 这个作的目的是咱们先用:
http://www.ttmeiju.me/index.php/summary/index/p/2.html复制代码
页面进行调试。将 follow 改成 False,这个时候咱们运行下面的代码:
class PlayrankingSpider(CrawlSpider): name = 'playranking' allowed_domains = ['www.ttmeiju.me'] # start_urls = ['http://www.ttmeiju.me/'] root_url = "http://www.ttmeiju.me/" start_urls = ['http://www.ttmeiju.me/index.php/summary/index/p/1.html'] rules = ( # Rule(LinkExtractor(restrict_xpaths='//ul[@class="pagination"]//a[@class="next"]'), callback='parse_item',follow=True), Rule(LinkExtractor(restrict_xpaths='//ul[@class="pagination"]//a[@class="next"]'), callback='parse_item'), ) def parse_item(self, response): print(response.url)复制代码
看到结果显示的是这个样子:
咱们看到,去掉follow以后,果真只是爬取两页内容,可是这里 parse_item() 只打印出来第二页的 URL,第一页的URL没有打印出来。这其中的过程在这里简单说一下:
CrawlSpider首先是经过 startUrl 来做为爬虫开始爬取的页面的,全部的 Rules 生效的页面都是在第一个页面以及以后的页面,因此这里,当 Scrapy 向 startUrl 发送第一个请求并获得 response,咱们开始调用 Rules 来萃取出来接下来须要爬取的 URL , 因此, parse_item() 这个回调生效的页面就是除了 startUrl 页面的其余页面。
但是咱们也要爬取 startUrl 页面里面的东西的啊,别急,咱们下一节说这个问题。这一节咱们先来搞定除第一个页面的其余页面的爬取。
那么接下来咱们就是要从 URL 里面来获取到咱们上面 TtmjTvPlayItem 里面各个变量的值了。
页面数据很规律,咱们仍是经过 xPath 来定位元素信息。
每一条美剧的信息,都包裹在 tr
标签下,并且 tr
标签的特色就是 class
里面含有 Scontent
字段。
因此,咱们这里能够按照这样的思路来操做:先将全部美剧的 tr
标签爬取出来,成一个 list, 而后再遍历 list 里面的每个变量,再从中获取出来 各个变量赋值到 TtmjTvPlayItem 里面。
咱们使用在上一节提到的 Scrapy Shell
来作 xpath 的定位:
$ scrapy shell http://www.ttmeiju.me/index.php/summary/index/p/2.html$ response.xpath('//tr[contains(@class,"Scontent")]') 复制代码
能够看到结果倒是是全部的美剧列表:
而后咱们只须要遍历这每个 tr
标签便可,因此咱们的 parse_item() 方法写成这样:
def parse_item(self, response): tr_list = response.xpath('//tr[contains(@class, "Scontent")]') for content in tr_list: name = content.xpath('//td[@align="left"]//a/text()').extract() print(name)复制代码
这里咱们要答应全部的名字,结果发现:
WTF!这尼玛好像把全部的名字都打印出来了,这不是咱们想要的结果啊。
这里就是坑!!!
xpath选出来 selectorList 以后,若是想要针对每个 selector 进行操做,就须要把每个 selector 先 extract() 出来,再封装成 Selector,以后的操做,就对这个 Selector 操做就能够。
咱们将代码改为下面这个样子:
def parse_item(self, response): tr_list = response.xpath('//tr[contains(@class, "Scontent")]').extract() for content in tr_list: selector = Selector(text=content) name = selector.xpath('//td[@align="left"]//a/text()').extract() print(name)复制代码
注意第二行和第四行,第二行调用了extract(),先提取成 string,而后第四行咱们在针对每一个 string 封装成 selector供下面的使用。这样咱们再看打印结果:
看到每一个名字都答应出来了。这一步成功了,咱们接下来把其余数据搞定就能够了。
针对每一条美剧,名字分布颇有规律,就是一个有align
属性的 a
标签,其余的内容都是在 td
标签里面。
咱们按照上面的思路,把代码改一下,找到全部的td来看一下:
def parse_item(self, response): tr_list = response.xpath('//tr[contains(@class, "Scontent")]').extract() for content in tr_list: selector = Selector(text=content) td_list = selector.xpath('//td/text()').extract() tv_name = selector.xpath('//td[@align="left"]//a/text()').extract_first() print(tv_name) for td_item in enumerate(td_list): print(td_item)复制代码
结果很让咱们意外:
找到是找到了,结果里面有好多换行符和空格,那么咱们只须要简单处理一下:
def parse_item(self, response): tr_list = response.xpath('//tr[contains(@class, "Scontent")]').extract() for content in tr_list: selector = Selector(text=content) td_list = selector.xpath('//td/text()').extract() tv_name = selector.xpath('//td[@align="left"]//a/text()').extract_first() print(tv_name) for index, td_item in enumerate(td_list): td_item = td_item.replace('\t', '').strip() print(td_item)复制代码
这样,咱们就能够打印出来正确的结果了:
上面的数据,咱们还差一个美剧的URL,这个URL咱们分析,是在美剧名字里面:
因此,这里咱们用 xpath 定位出来这个标签,而后把数据读取出来就好:
# xpath 取属性用 @+属性名tv_play_url = self.root_url + selector.xpath('//td[@align="left"]//a/@href').extract_first()[1:]复制代码
这里咱们发现, xpath 读取 tag 的标签里面某个属性的值,就用 @属性名
就能够。
咱们最后在把 TtmjTvPlayItem 整合到咱们的 parse_item() 里面:
def parse_item(self, response): tr_list = response.xpath('//tr[contains(@class, "Scontent")]').extract() for content in tr_list: tv_item = TtmjTvPlayItem() selector = Selector(text=content) td_list = selector.xpath('//td/text()').extract() tv_name = selector.xpath('//td[@align="left"]//a/text()').extract_first() tv_item['tv_play_name'] = tv_name for index, td_item in enumerate(td_list): td_item = td_item.replace('\t', '').strip() if index == 0: tv_item['tv_play_rank'] = td_item elif index == 1: tv_item['tv_play_category'] = td_item elif index == 2: tv_item['tv_play_state'] = td_item elif index == 3: tv_item['tv_play_update_day'] = td_item elif index == 4: tv_item['tv_play_return_date'] = td_item elif index == 5: tv_item['tv_play_counting_data'] = td_item # xpath 取属性用 @+属性名 tv_item['tv_play_url'] = self.root_url + selector.xpath('//td[@align="left"]//a/@href').extract_first()[1:] print(tv_item)复制代码
这样,最后的结果就很完美:
读到这里的同窗确定发现了咱们还有几个问题没有解决:
这些东西都会在下一讲以及以后的文章里面说,咱们最后的目的就是创建一个能够经过json文件动态配置的爬虫。别慌,皮爷一点一点的给你们细细的讲解。
这里说一下哈,全部的爬虫代码,我均已发布到网上了,获取方法很简单,关注公号『皮爷撸码』,回复『代码』便可找到,记住对应的代码编号是『CS001』,代码编号是『CS001』,代码编号是『CS001』。
『皮爷撸码』,一个很硬核的公号,若是你能从这里学到知识,皮爷很高兴,很但愿你也可以将这分内容分享出去,让更多的人感觉到编程的乐趣。