前言
本章内容会在scrapy(四) 翻页请求文章的基础上继续编写(人太懒,从头开始太累了)。有时间的朋友可以去看看这边文章,自认为写得还算详细,希望得到大家的认可。
继续
回到正题,昨晚完成了电影的列表爬取,但是单个电影信息并不会都展示到列表中,此时我们需要读取单个电影的详细信息了。
如图:我们还需要获取该电影的导演是谁,分类,地区,年份等信息。
scrapy(四) 翻页请求文章中有和大家着重介绍过scrapy.Request,其中有个callback参数。这个参数可以指定交由哪个parse函数做处理。所以我们需要自定义parse函数。
网页分析
进行爬虫之前,分析网页的结构是很重要的,我们了解了网页结构之后,才能知道想要的数据存放在哪里。
通过xpath插件调试,最终确认我们想要的详情信息都在ul标签中
详情页结构分析
自定义parse函数
- 获取详情页的url
# 详情页的href
item["details_href"]=li.xpath("./a[1]/@href").extract_first()
# 拼接详情页的url
details_url= host_name+item["details_href"]
- 自定义parse函数
# 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
def details_parse(self, response):
li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
for li in li_list:
print(li)
- 调用自定义parse函数
yield scrapy.Request(url=details_url,callback=self.details_parse)
- 完整代码
import scrapy
class MovieSpider(scrapy.Spider):
name = 'movie' # 项目名称
allowed_domains = ['77dianshi.com'] # 爬取范围
start_urls = ['http://www.77dianshi.com/kdongzuopian/'] # 爬取地址
def parse(self, response):
li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
#网址
host_name="http://www.77dianshi.com"
for li in li_list:
item={} # 用于封装数据
# 获取第一个 a 标签
a1=li.xpath("./a[1]")
# 封面
item["cover"]=li.xpath("./a[1]/@data-original").extract_first()
# 详情页的href
item["details_href"]=li.xpath("./a[1]/@href").extract_first()
# 评分
item["score"]=a1.xpath("./span[2]/text()").extract_first()
# HD
item["hd"]=a1.xpath("./span[3]/text()").extract_first()
# 电影名称
item["movie_name"]=li.xpath("./a[2]/text()").extract_first()
# 演员名单
item["cast_list"]=li.xpath("./span/text()").extract_first()
#请求详情页
details_url= host_name+item["details_href"]
yield scrapy.Request(url=details_url,callback=self.details_parse)
# 获取下页的url
next_href=response.xpath('//div[@class="fed-page-info fed-text-center"]/a[contains(text(),"下页")]/@href').extract_first()
next_url=host_name+next_href
#获取当前页的url
current_url=response.url
#为了方便查看进度,打印当前url地址
print("已经爬取了到了:",current_url,"下页地址:",next_url)
# 通过判断是否以next_href为结尾,来判断是否是最后一页
if not current_url.endswith(next_href):
# url:下一页的url地址
# callback:需要交由那个parse方法处理(可以自定义),因为下一页的数据结构,和当前页的数据一样,所以处理方式都是一样的。若不一样,那么需要自定义
yield scrapy.Request(url=next_url,callback=self.parse)
else:
print("爬取完毕")
# 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
def details_parse(self, response):
li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
for li in li_list:
print(li) # 测试是否爬取成功
- 启动爬虫
获取以下信息,表示xpath没有问题,并且details_parse函数是没有问题的
$ scrapy crawl movie
已经爬取了到了: http://www.77dianshi.com/kdongzuopian/ 下页地址: http://www.77dianshi.com/kdongzuopian-2/
<Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs12 fed-col-md6 f...'>
<Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs12 fed-col-md6 f...'>
<Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
<Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
<Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
<Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs6 fed-col-md3 fe...'>
<Selector xpath='//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li' data='<li class="fed-col-xs12 fed-hide fed-...'>
解析
接下里的工作就是对li标签里的内容进行解析了。
<ul class="fed-part-rows"><li class="fed-col-xs12 fed-col-md6 fed-part-eone"><span class="fed-text-muted">主演:</span><a href="/q/-%E6%A2%81%E5%AE%B6%E8%BE%89------------/" target="_blank">梁家辉</a> <a href="/q/-%E5%88%98%E5%98%89%E7%8E%B2------------/" target="_blank">刘嘉玲</a> <a href="/q/-%E9%83%91%E8%A3%95%E7%8E%B2------------/" target="_blank">郑裕玲</a> <a href="/q/-%E5%BC%A0%E5%9D%9A%E5%BA%AD------------/" target="_blank">张坚庭</a> <a href="/q/-%E5%90%B4%E5%AE%B6%E4%B8%BD------------/" target="_blank">吴家丽</a> </li><li class="fed-col-xs12 fed-col-md6 fed-part-eone"><span class="fed-text-muted">导演:</span><a href="/q/-----%E5%BC%A0%E5%9D%9A%E5%BA%AD--------/" target="_blank">张坚庭</a> </li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">分类:</span><a href="/kdongzuopian/" target="_blank">动作</a>,<a href="/q/------------1990%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">1990动作片</a> <a href="/q/------------%E9%A6%99%E6%B8%AF%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">香港动作片</a> <a href="/q/------------%E7%B2%A4%E8%AF%AD%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">粤语动作片</a> <a href="/q/------------%E5%8F%A4%E6%83%91%E5%A4%A7%E5%BE%8B%E5%B8%88-/" target="_blank">古惑大律师</a> <a href="/q/------------%E6%AD%A6%E6%89%93%E7%89%87-/" target="_blank">武打片</a> <a href="/q/------------%E5%8A%A8%E4%BD%9C%E7%94%B5%E5%BD%B1-/" target="_blank">动作电影</a> <a href="/q/------------1990%E9%A6%99%E6%B8%AF%E5%8A%A8%E4%BD%9C%E7%89%87-/" target="_blank">1990香港动作片</a> </li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">地区:</span><a href="/q/--%E9%A6%99%E6%B8%AF-----------/" target="_blank">香港</a> </li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">年份:</span><a href="/q/-------------1990/" target="_blank">1990</a> </li><li class="fed-col-xs6 fed-col-md3 fed-part-eone"><span class="fed-text-muted">更新:</span>06.11</li><li class="fed-col-xs12 fed-hide fed-show-md-block"><div class="fed-part-esan"><span class="fed-text-muted">简介:</span>已改邪归正浪子陈子辉(梁家辉饰)临去外地工作前与女友(刘嘉玲饰)话别。村长之子根与女友莲丛林追逐玩耍,根色欲薰心使莲受重伤,根慌忙而逃。辉与玲话别后经过丛林时被绊倒。原来莲昏迷时,辉欲施救之际被村民</div></li></ul>
经过分析,发现共有7个 li标签
- 第一个li 存放着演员信息
- 第二个li 存放着导演姓名
- 第三个li 存放着电影分类
- 第四个li 存放着地区
- 第五个li 存放着年份
- 第六个li 存放着更新时间
- 第七个li 存放着电影简介
编写代码:
# 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
def details_parse(self, response):
print(response.url)
li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
#定义一个字典,用于存放爬取的数据
detail_item={}
# 经过分析,使用for循环的方式,好像并不是很合理
# 演员列表
detail_item["star_list"]=li_list[0].xpath("./a/text()").extract()
# 导演
detail_item["director_name"]=li_list[1].xpath("./a/text()").extract_first()
# 分类
detail_item["type"]=li_list[2].xpath("./a/text()").extract()
# 地区
detail_item["region"]=li_list[3].xpath("./a/text()").extract_first()
# 年份
detail_item["year"]=li_list[4].xpath("./a/text()").extract_first()
# 更新时间
detail_item["update_time"]=li_list[5].xpath("./text()").extract_first()
# 简介
detail_item["introduction"]=li_list[6].xpath("./div/text()").extract_first()
print(detail_item)
爬取结果:这里注意的是,采用的是异步的方式进行爬取,数据结果并不是按照电影列表的顺序获取的。爬取的数据如下,数据很多,只展示了其中一部分。
$ scrapy crawl movie
http://www.77dianshi.com/t/xuedou/
{'star_list': ['朴熙顺', '晋久', '高昌锡'], 'director_name': '朴勋政', 'type': ['动作', '2010动作', '韩国动作', '韩语动作', '血斗', '动作', '2010韩国动作', '动作血斗'], 'region': '韩国', 'year': '2010', 'update_time': '06.04', 'introduction': '以朝鲜时代作为背景,讲述了光海君11年间,朝鲜军队帮助明朝反抗满清却遭大败,战争中幸存的3人在随后与追杀的清军展开殊死血战的故事。'}
http://www.77dianshi.com/t/mowang2011/
{'star_list': ['沙鲁克·汗', '卡琳娜·卡普尔', '阿俊·拉姆鲍', 'Tom', 'Wu', 'Shahana', 'Goswami', '萨蒂什·沙阿', '达里普·塔西尔'], 'director_name': '安布哈雅·辛哈', 'type': ['动作', '2011动作', '美国', '印度动作', '英语', '北印
度语动作', '魔王2011', '动作', '2011美国', '印度动作', '动作魔王2011'], 'region': '美国', 'year': '2011', 'update_time': '06.04', 'introduction': '夏卡(沙鲁克·罕ShahrukhKhan饰)是一位出色的游戏程序设计师,而他的儿子帕蒂则是
一个不折不扣的游戏迷。为了增进父子之间的感情,夏卡创造出了一个名为RaOne的反派游戏角色,这个角色不仅有着'}
...
此时还没有结束,details_parse 的数据结果是 parse的详情,他们应该是一块的。也就是说,detail_item 的数据应该存放到 item 里。
那么如何将 detail_item 存放到 parse 函数的 item 字典中呢?在 scrapy.Request 中还有一个参数meta,它可以用于传递一些参数给另一个parse 函数。所以我们可以可将 item 作为参数传递到 details_parse 中。这样,detail_item 就可以存放到 item 中,最后进行返回了。
修改亿点点代码
- 传递 item
#请求详情页
details_url= host_name+item["details_href"]
yield scrapy.Request(url=details_url,callback=self.details_parse,meta={"item":item})
2.在 details_parse 中接收 item
def details_parse(self, response):
# 获取item
item=response.meta["item"]
- 将 detail_item 存入到 time中(依旧在 details_parse 函数中操作)
# 将detail_item 存放到item
item["detail_item"]=detail_item
yield item #这里使用 yield 主要用于将 item 的数据结果推送到 ScrapyDemoPipeline 中
- 输出结果
$ scrapy crawl movie
http://www.77dianshi.com/t/fenglingcao2011/
模拟将数据入库: {'cover': 'https://img.huishij.com/upload/vod/20210604-1/83de12082acb1c0f9589eceb8f66fdd1.jpg', 'details_href': '/t/fenglingcao2011/', 'score': '4.0', 'hd': 'HD', 'movie_name': '风铃草2011', 'cast_list': '伊
万·格罗德尔,Jessie,Wiseman,Tyler,Dawson,丽贝卡·布兰德斯,文森特·格拉肖,Zack,Kraus,Keghan,Hurst,Alexandra,Boylan,Bradshaw,Pruitt,Brian,Thomas,Evans,Britta,Jacobellis,Ceaser,Flores,Chris,Snyder,Dan,Dulle,Jon,Huck,乔尔·霍奇', 'detail_item': {'star_list': ['伊万·格罗德尔', 'Jessie', 'Wiseman', 'Tyler', 'Dawson', '丽贝卡·布兰德斯', '文森特·格拉肖', 'Zack', 'Kraus', 'Keghan', 'Hurst', 'Alexandra', 'Boylan', 'Bradshaw', 'Pruitt', 'Brian', 'Thomas', 'Evans', 'Britta', 'Jacobellis', 'Ceaser', 'Flores', 'Chris', 'Snyder', 'Dan', 'Dulle', 'Jon', 'Huck', '乔尔·霍奇'], 'director_name': '伊万·格罗德尔', 'type': ['动作', '2011动作', '美国动作', '英语动作', '风铃草2011', '动作', '2011美国动作', '动作风铃草2011'], 'region': '美国', 'year': '2011', 'update_time': '06.04', 'introduction': '两个好朋友改装了一辆带着火焰喷射器的死亡战车,为即将到来的全球灾难做准备。而后,他们遇见了一个充满魅力的姑娘,开启了一
段奇妙的冒险旅程。但是,这个女人破坏自然的行为可能为他们招来灾祸……花絮·本片参'}}
....
movie.py 完整代码
import scrapy
from scrapy import item
class MovieSpider(scrapy.Spider):
name = 'movie' # 项目名称
allowed_domains = ['77dianshi.com'] # 爬取范围
start_urls = ['http://www.77dianshi.com/kdongzuopian/'] # 爬取地址
def parse(self, response):
li_list=response.xpath('//ul[@class="fed-list-info fed-part-rows"]/li')
#网址
host_name="http://www.77dianshi.com"
for li in li_list:
item={} # 用于封装数据
# 获取第一个 a 标签
a1=li.xpath("./a[1]")
# 封面
item["cover"]=li.xpath("./a[1]/@data-original").extract_first()
# 详情页的href
item["details_href"]=li.xpath("./a[1]/@href").extract_first()
# 评分
item["score"]=a1.xpath("./span[2]/text()").extract_first()
# HD
item["hd"]=a1.xpath("./span[3]/text()").extract_first()
# 电影名称
item["movie_name"]=li.xpath("./a[2]/text()").extract_first()
# 演员名单
item["cast_list"]=li.xpath("./span/text()").extract_first()
#请求详情页
details_url= host_name+item["details_href"]
yield scrapy.Request(url=details_url,callback=self.details_parse,meta={"item":item})
# 获取下页的url
next_href=response.xpath('//div[@class="fed-page-info fed-text-center"]/a[contains(text(),"下页")]/@href').extract_first()
next_url=host_name+next_href
#获取当前页的url
current_url=response.url
# 通过判断是否以next_href为结尾,来判断是否是最后一页
if not current_url.endswith(next_href):
# url:下一页的url地址
# callback:需要交由那个parse方法处理(可以自定义),因为下一页的数据结构,和当前页的数据一样,所以处理方式都是一样的。若不一样,那么需要自定义
yield scrapy.Request(url=next_url,callback=self.parse)
else:
print("爬取完毕")
# 由于详情页的数据结构与列表的结构不同,所以我们不能使用框架默认的parse函数,需要自定义。
def details_parse(self, response):
# 获取item
item=response.meta["item"]
print(response.url)
li_list=response.xpath('//div[@class="fed-part-layout fed-part-rows fed-back-whits"]/div[1]//dd[1]//ul/li')
#定义一个字典,用于存放爬取的数据
detail_item={}
# 经过分析,使用for循环的方式,好像并不是很合理
# 演员列表
detail_item["star_list"]=li_list[0].xpath("./a/text()").extract()
# 导演
detail_item["director_name"]=li_list[1].xpath("./a/text()").extract_first()
# 分类
detail_item["type"]=li_list[2].xpath("./a/text()").extract()
# 地区
detail_item["region"]=li_list[3].xpath("./a/text()").extract_first()
# 年份
detail_item["year"]=li_list[4].xpath("./a/text()").extract_first()
# 更新时间
detail_item["update_time"]=li_list[5].xpath("./text()").extract_first()
# 简介
detail_item["introduction"]=li_list[6].xpath("./div/text()").extract_first()
# 将detail_item 存放到item
item["detail_item"]=detail_item
yield item #这里使用 yield 主要用于将 item 的数据结果推送到 ScrapyDemoPipeline 中
pipelines.py 代码
class ScrapyDemoPipeline:
def process_item(self, item, spider):
#获取item 数据
print("模拟将数据入库:",item)
完结
爬取详情页的demo就写完了,写代码没花多少时间,写博客却花了我一个多小时,真的太苦逼了。scrapy框架用起来很简单,很多东西都是大佬帮我们做好了,我们直接用就行了。使用scrapy框架写爬虫是一件很好玩的事情,大家千万不要用爬虫做什么坏事。我写这些作用主要用于对scrapy的学习总结,能够帮助你我很荣幸。等到以后对scrapy框架越来越熟悉,我将重新整理前三篇内容。
网友评论