Scrapy框架的运用,获取豆瓣电影信息-02
第一节:豆瓣电影信息的爬取
1.分析源码
page页面:https://www.douban.com/doulist/3936288/
关键源码截图:
外部div:
<div class="bd doulist-subject">
<div class="doulist-video-items">
<span class="title">播放全片</span>
<a class="doulist-video-item" target="_blank" href="https://v.qq.com/x/cover/1o29ui77e85grdr.html?ptag=newdouban.movie"><span class="icon-circle"><img src="https://img1.doubanio.com/f/movie/f3c173002946c88386e659caef9842e97c19d273/pics/movie/video-qq.png" /></span> 腾讯视频</a>
<a class="doulist-video-item" target="_blank" href="https://m.bilibili.com/bangumi/play/ss28274?bsource=doubanh5"><span class="icon-circle"><img src="https://img1.doubanio.com/f/movie/f536fe0ea1cbb0914658ae803125d078351f9047/pics/movie/video-bilibili.png" /></span> 哔哩哔哩</a>
<a class="doulist-video-item" target="_blank" href="http://v.youku.com/v_show/id_XMjgwNDkwNzE2.html?tpa=dW5pb25faWQ9MzAwMDA4XzEwMDAwMl8wMl8wMQ&refer=esfhz_operation.xuka.xj_00003036_000000_FNZfau_19010900"><span class="icon-circle"><img src="https://img1.doubanio.com/f/movie/886b26a83d18bc60de4ee1daac38145f03c88792/pics/movie/video-youku.png" /></span> 优酷视频</a>
<a href="javascript:;" class="more-video-item"><img src="https://img1.doubanio.com/f/sns/e2ebb96e1a69c9d9ab686fdf1fda829f43aa1116/pics/sns/ic_circle_more@2x.png" /> 更多</a>
<div class="video-items-other">
<a target="_blank" href="https://www.douban.com/link2/?url=http%3A%2F%2Fwww.iqiyi.com%2Fv_19rra0h3wg.html%3Fvfm%3Dm_331_dbdy%26fv%3D4904d94982104144a1548dd9040df241&subtype=9&type=online-video"><span class="icon-circle"><img src="https://img1.doubanio.com/f/movie/7c9e516e02c6fe445b6559c0dd2a705e8b17d1c9/pics/movie/video-iqiyi.png" /></span> 爱奇艺视频</a>
</div>
</div>
内部div:
内部div:
<div class="source">
来自:豆瓣电影
</div>
<div class="post">
<a href="https://movie.douban.com/subject/1292052/" target="_blank">
<img src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp"/>
</a>
</div>
<div class="title">
<a href="https://movie.douban.com/subject/1292052/" target="_blank">
<img style="width: 16px; vertical-align: text-top;" src="https://img1.doubanio.com/f/sns/5741f726dfb46d89eb500ed038833582c9c9dcdb/pics/sns/doulist/ic_play_web@2x.png"/>
肖申克的救赎 The Shawshank Redemption
</a>
</div>
<div class="rating">
<span class="allstar50"></span>
<span class="rating_nums">9.7</span>
<span>(2954899人评价)</span>
</div>
<div class="abstract">
导演: 弗兰克·德拉邦特
<br />
主演: 蒂姆·罗宾斯 / 摩根·弗里曼 / 鲍勃·冈顿
<br />
类型: 犯罪 / 剧情
<br />
制片国家/地区: 美国
<br />
年份: 1994
</div>
</div>
2.通过命令创建spiders文件db.py
3.Scrapy shell 的运用(交互式平台)
用来调试Scrapy 项目代码的 命令行工具。 启动的时候预定义了Scrapy的一些对象 启动后如下图所示 作用: 调试 调试 调试
启动Scrapy shell的命令语法格式如下: scrapy shell [option] [url|file] url 就是你想要爬取的网址 注意:分析本地文件是一定要带上路径,scrapy shell默认当作url
获取page页面25条电影名字信息(以列表形式展示):response.xpath('//div[@class="title"]/a/text()')
获取Selector对象数值:response.xpath('//div[@class="title"]/a/text()').extract()
获取第一条电影名字信息(strip()为去除空格):response.xpath('//div[@class="title"]/a/text()').extract()[1].strip()
数据的存储
items.py
pipelines.py(存储管道)
运行代码:
运行结果:(这样写的话电影名字、主演、评分信息分开显示,没有写在一起)
源代码:
import scrapy
from ..items import DoubanItem # ..上上级
class DbSpider(scrapy.Spider):
name = "db"
# 域名可以在创建项目的时候自定义随便写,但是必须要有,不用的话在这里可以直接注释掉自己写想要的域名
# allowed_domains = ["www.db233"]
# start_urls = ["https://www.db233"]
# allowed_domains = ["m.baidu.com"] # 域名
allowed_domains = ["www.douban.com"] # 域名
# 这个网址通过交互式(scrapy shell +网址)爬不到数据
# start_urls = ["https://m.baidu.com/sf?pd=topone_multi&top=%7B%22sfhs%22%3A1%7D&atn=index&word=%E8%B1%86%E7%93%A3%E7%94%B5%E5%BD%B1top250&lid=16638235655785603014&key=bd%2FbPtJ7umUkuPF3fz0H4wbbfR%2FIB8veb6rRjTDeeqEVS%2F1TIXYtLVMW25bRVfFa%2BdvPb0y98zHLTQOcnk8wmJEQqtz76fON2BcDkEuHIdg%3D&type=bpage"]
# page页面的地址,开始请求
start_urls =["https://www.douban.com/doulist/3936288/"]
# 一次性拿到xpath数据不一定正确,所以可以通过交互式平台先进行测试
# scrapy shell+网址
def parse(self, response):
# 获取电影信息(先在交互式平台测试好 通过scarpy shell) <以下是response 自带的xpath 要与交互式平台的xpath区分开,交互式安装了scrapy 就会有xpath
# 通过extract()方法 从select()对象中获取需要的信息
# 1、通过内部div获取电影名字
film_name = response.xpath('//div[@class="title"]/a/text()').extract() # 是个列表
# 2、评分
score=response.xpath('//div[@class="rating"]/span[2]/text()').extract()
# 3、获取导演信息
# response.xpath('//div[@class="abstract"]/text()')
main_star = response.xpath('//div[@class="abstract"]/text()').extract()
# 通过外部div获取电影名字
# film_name=response.xpath('//div[@class="bd doulist-subject"]/div/div/div/div/a/text()').extract() # 这样写为啥空值,待确认
# film_name=response.xpath('//div/div/div/div[@class="title"]/a/text()').extract()[1].strip()
# 输出电影信息: {'film_name': '肖申克的救赎 The Shawshank Redemption'}
# films_list = response.xpath('//div[@class="bd doulist-subject"]') # 共25个电影信息
# print(len(films_list)) # 25
# #
# for node in films_list:
#
# film_name = node.xpath('./div[@class="title"]/a/text()').extract()
# print('电影名字:',film_name)
# print('================================================')
# 要交给管道存储,先在 items.py中定义字段名,然后在db.py文件中导入from ..items import DoubanItem
# 然后使用DoubanItem
item = DoubanItem() # 创建对象
item['film_name']=film_name # 这里就是一个赋值,item可以理解为一个安全的字典
item['score']=score
item['main_star']=main_star
print('输出电影信息:',dict(item)) # 可以转换成真正的字典
# 只要是返回值就是交个引擎的
return item # 解析出的数据交给引擎,引擎会交给管道==》需要打开管道,在settings.py
# ITEM_PIPELINES = {
# "douban.pipelines.DoubanPipeline": 300,
# }
# 数据交给管道,管道也要进行处理的pipelines.py
# print('#######################################')
# print('#######################################')
# print('#######################################')
# print(response.text) # 获取page页数据
pass
'''
xpath抓取数据值有\r\n\t时,去掉的方法normalize-space()
'''
第二节:豆瓣电影信息的爬取(优化:实现将电影信息写在一起显示出来)
1.分析page页面源代码,先通过shell先测试一下
外部div
与内部div
获取电影名字:
>> node_list=response.xpath('//div[@class="bd doulist-subject"]')
>>> len(node_list)
25
>>> node_xsk=node_list[0]
>>> node_xsk.xpath('./div[@class="title"]/a/text()').extract()
['\n ', '\n 肖申克的救赎 The Shawshank Redemption\n ']
>>> node_xsk.xpath('./div[@class="title"]/a/text()').extract()[1].strip()
'肖申克的救赎 The Shawshank Redemption'
发现电影名字输出不规则,有些名字前没有空格,无法统一使用xpath().extract[1].strip() 打印出字符串形式
获取主演:
node_xsk.xpath('./div[@class="abstract"]/text()').extract()
['\n \n 导演: 弗兰克·德拉邦特\n ', '\n 主演: 蒂姆·罗宾斯 / 摩根·弗里曼 / 鲍勃·冈顿
\n ', '\n 类型: 犯罪 / 剧情\n ', '\n 制片国家/地区: 美国\n ', '\n
年份: 1994\n ']
>>> node_xsk.xpath('./div[@class="abstract"]/text()').extract()[1]
'\n 主演: 蒂姆·罗宾斯 / 摩根·弗里曼 / 鲍勃·冈顿\n '
>>> node_xsk.xpath('./div[@class="abstract"]/text()').extract()[1].strip()
'主演: 蒂姆·罗宾斯 / 摩根·弗里曼 / 鲍勃·冈顿'
获取评分:
2.主要代码部分
与db.py不同的地方:因为是个for循环要不断的输出25条数据给到引擎,由引擎给到管道。
运行结果:
film2.text(电影名字输出不规则导致无法统一打印输出,待解决!!!)
db2.py:
import scrapy
from ..items import DoubanItem # ..上上级
import re
class DbSpider(scrapy.Spider):
name = "db2"
# 域名可以在创建项目的时候自定义随便写,但是必须要有,不用的话在这里可以直接注释掉自己写想要的域名
# allowed_domains = ["www.db233"]
# start_urls = ["https://www.db233"]
# allowed_domains = ["m.baidu.com"] # 域名
allowed_domains = ["www.douban.com"] # 域名
# 这个网址通过交互式(scrapy shell +网址)爬不到数据
# start_urls = ["https://m.baidu.com/sf?pd=topone_multi&top=%7B%22sfhs%22%3A1%7D&atn=index&word=%E8%B1%86%E7%93%A3%E7%94%B5%E5%BD%B1top250&lid=16638235655785603014&key=bd%2FbPtJ7umUkuPF3fz0H4wbbfR%2FIB8veb6rRjTDeeqEVS%2F1TIXYtLVMW25bRVfFa%2BdvPb0y98zHLTQOcnk8wmJEQqtz76fON2BcDkEuHIdg%3D&type=bpage"]
# page页面的地址
start_urls =["https://www.douban.com/doulist/3936288/"]
# 一次性拿到xpath数据不一定正确,所以可以通过交互式平台先进行测试,
# scrapy shell+网址
def parse(self, response):
# 获取电影信息(先在交互式平台测试好 通过scarpy shell) <以下是response 自带的xpath 要与交互式平台的xpath区分开,交互式安装了scrapy 就会有xpath
# 通过extract()方法 从select对象中获取需要的信息
# film_name=response.xpath('//div[@class="title"]/a/text()').extract() # 是个列表
node_list = response.xpath('//div[@class="bd doulist-subject"]') # 共25个电影信息
for node in node_list:
# 1.获取电影名字
# film_name=node.xpath('./div[@class="title"]/a/text()').extract()
# 按以下写会出现 list index out of range,因为存在有几个第一个不存在空格情况
# film_name=node.xpath('./div[@class="title"]/a/text()').extract()[1].strip()
# 2.获取评分
# response.xpath('//div[@class="rating"]/span[2]')
score = node.xpath('./div[@class="rating"]/span[2]/text()').extract()[0].strip()
# >> > node_xsk.xpath('./div[@class="rating"]/span[2]/text()').extract()[0]
# '9.7'
# >> >
# 3.获取主演
#>>> node.xpath('./div[@class="abstract"]/text()').extract()[1].strip()
'导演: 弗兰克·德拉邦特'
# 第一种写法:
main_star=node.xpath('./div[@class="abstract"]/text()').extract()[1].strip()
# 第二种写法:
# '''
# import re
# for node in node_list:
# main_star = node.xpath('./div[@class="abstract"]/text()').extract()
# star_name = re.findall("主演?:? ?(.*)",str(main_star))[1]
# star_name
#
#
# '''
# main_star = node.xpath('./div[@class="abstract"]/text()').extract()
# if '主' in main_star:
# # 第一个?是演是0或者1个,第二个?是:是0或者1个,第三个?空格可以是0或者1个,(.*)匹配后面所有的意思
# # star_name=re.findall("主演?:? ?(.*)",str(main_star))
# star_name = re.findall("主演:? ?(.*)",str(main_star))[1]
#
# else:
# star_name='空' # 如果没有主演的情况下
# 注意:extract()跟extract_first()一样
# 要交给管道存储,先在 items.py中定义字段名,然后在db.py文件中导入from ..items import DoubanItem
# 然后使用DoubanItem
item=DoubanItem() # 创建对象
# item['film_name']=film_name # 这里就是一个赋值,item可以理解为一个安全的字典
item['score'] = score
item['main_star'] = main_star
print('输出:',dict(item)) # 可以转换成真正的字典
# 只要是返回值就是交个引擎的
# return item # 解析出的数据交给引擎,引擎会交给管道==》需要打开管道,在settings.py
# return 返回的是方法,这里要使用生成器,不断的生成25条数据不断的给到引擎,然后引擎就不断的给到管道 yield item ,使用return 只能输出一条数据
yield item # 这里与db.py文件不同的地方
# ITEM_PIPELINES = {
# "douban.pipelines.DoubanPipeline": 300,
# }
# 数据交给管道,管道也要进行处理的pipelines.py
# print(response.text) # 获取page页数据
'''
xpath抓取数据值有\r\n\t时,去掉的方法normalize-space()
'''
# 1、获取电影名字 extract()就是提取data里面的数据
以上已完成电影名字、主演信息、评分获取。
3.如何实现分页信息获取?如获取第2页...第n页
先解决一个问题,最近代码不显示行号了,先解决一波
File --> Settings -->Editor -->Appearance , 之后勾选Show Line Numbers。
先获取第2页电影数据:
url:https://www.douban.com/doulist/3936288/?start=25&sort=time&playable=0&sub_typ
以下绿色线条流程为获取第二页请求流程顺序
主要代码:
db3.py
import scrapy from ..items import DoubanItem # ..上上级 import re class DbSpider(scrapy.Spider): name = "db3" # 域名可以在创建项目的时候自定义随便写,但是必须要有,不用的话在这里可以直接注释掉自己写想要的域名 # allowed_domains = ["www.db233"] # start_urls = ["https://www.db233"] # allowed_domains = ["m.baidu.com"] # 域名 allowed_domains = ["www.douban.com"] # 域名 # 这个网址通过交互式(scrapy shell +网址)爬不到数据 # start_urls = ["https://m.baidu.com/sf?pd=topone_multi&top=%7B%22sfhs%22%3A1%7D&atn=index&word=%E8%B1%86%E7%93%A3%E7%94%B5%E5%BD%B1top250&lid=16638235655785603014&key=bd%2FbPtJ7umUkuPF3fz0H4wbbfR%2FIB8veb6rRjTDeeqEVS%2F1TIXYtLVMW25bRVfFa%2BdvPb0y98zHLTQOcnk8wmJEQqtz76fON2BcDkEuHIdg%3D&type=bpage"] # page页面的地址 start_urls =["https://www.douban.com/doulist/3936288/"] # 一次性拿到xpath数据不一定正确,所以可以通过交互式平台先进行测试, # scrapy shell+网址 def parse(self, response): # 获取电影信息(先在交互式平台测试好 通过scarpy shell) <以下是response 自带的xpath 要与交互式平台的xpath区分开,交互式安装了scrapy 就会有xpath # 通过extract()方法 从select对象中获取需要的信息 # film_name=response.xpath('//div[@class="title"]/a/text()').extract() # 是个列表 node_list = response.xpath('//div[@class="bd doulist-subject"]') # 共25个电影信息 for node in node_list: # 1.获取电影名字 # film_name=node.xpath('./div[@class="title"]/a/text()').extract() # 按以下写会出现 list index out of range,因为存在有几个第一个不存在空格情况 # film_name=node.xpath('./div[@class="title"]/a/text()').extract()[1].strip() # 2.获取评分 # response.xpath('//div[@class="rating"]/span[2]') score = node.xpath('./div[@class="rating"]/span[2]/text()').extract()[0].strip() # >> > node_xsk.xpath('./div[@class="rating"]/span[2]/text()').extract()[0] # '9.7' # >> > # 3.获取主演 #>>> node.xpath('./div[@class="abstract"]/text()').extract()[1].strip() '导演: 弗兰克·德拉邦特' # 第一种写法: main_star=node.xpath('./div[@class="abstract"]/text()').extract()[1].strip() # 第二种写法...... # 注意:extract()跟extract_first()一样 # 要交给管道存储,先在 items.py中定义字段名,然后在db.py文件中导入from ..items import DoubanItem # 然后使用DoubanItem item=DoubanItem() # 创建对象 # item['film_name']=film_name # 这里就是一个赋值,item可以理解为一个安全的字典 item['score'] = score item['main_star'] = main_star print('输出:',dict(item)) # 可以转换成真正的字典 # 只要是返回值就是交个引擎的 # return item # 解析出的数据交给引擎,引擎会交给管道==》需要打开管道,在settings.py # return 返回的是方法,这里要使用生成器,不断的生成25条数据不断的给到引擎,然后引擎就不断的给到管道 yield item ,使用return 只能输出一条数据 yield item # 这里与db.py文件不同的地方 # 如何获取第2页电影信息? # 需要先把第一页25条的数据for循环执行完成才能开始爬取第2页信息,第2页有24条数据 # 第二页就是一个新的请求,找到对应url,并再次发起请求,引擎将对应的response交给self.parse重新解析,即再次执行for循环 # url2="https://www.douban.com/doulist/3936288/?start=25&sort=time&playable=0&sub_type=" # 如何获得第n页?要对url分析找到规律 yield scrapy.Request('https://www.douban.com/doulist/3936288/?start=25&sort=time&playable=0&sub_type=',callback=self.parse) # callback 是回调函数
运行截图:(前两页共49条数据)
如何获取第n页?先判断每页url的规律
分析不同页的url找到规律:===》25的倍数
https://www.douban.com/doulist/3936288/?start=0&sort=time&playable=0&sub_type= 第1页 0*25
https://www.douban.com/doulist/3936288/?start=25&sort=time&playable=0&sub_type= 1*25
https://www.douban.com/doulist/3936288/?start=50&sort=time&playable=0&sub_type= 2*25
https://www.douban.com/doulist/3936288/?start=75&sort=time&playable=0&sub_type= ...
https://www.douban.com/doulist/3936288/?start=100&sort=time&playable=0&sub_type= ...
https://www.douban.com/doulist/3936288/?start=200&sort=time&playable=0&sub_type= 第9页
https://www.douban.com/doulist/3936288/?start=225&sort=time&playable=0&sub_type= 第10页 9*25
定义一个类变量:page_num=0
主要代码截图:
无法正常获取数据就增加下载延迟:
运行截图(总共有10页数据,到第11页的时候就爬不到数据了,空的,db5.py优化)
省略...
db4.py源代码(注意:凡是使用爬虫去爬取数据时,都应提前想想爬取的网站上有没有反爬虫,就需要在settings.py中增加一些内容才能爬取到数据)
import scrapy from ..items import DoubanItem # ..上上级 import re class DbSpider(scrapy.Spider): name = "db4" # 域名可以在创建项目的时候自定义随便写,但是必须要有,不用的话在这里可以直接注释掉自己写想要的域名 # allowed_domains = ["www.db233"] # start_urls = ["https://www.db233"] # allowed_domains = ["m.baidu.com"] # 域名 allowed_domains = ["www.douban.com"] # 域名 # 这个网址通过交互式(scrapy shell +网址)爬不到数据 # page页面的地址 start_urls =["https://www.douban.com/doulist/3936288/"] # 一次性拿到xpath数据不一定正确,所以可以通过交互式平台先进行测试, # scrapy shell+网址 page_num=0 # 为类变量 def parse(self, response): node_list = response.xpath('//div[@class="bd doulist-subject"]') # 共25个电影信息 for node in node_list: # film_name=node.xpath('./div[@class="title"]/a/text()').extract() score = node.xpath('./div[@class="rating"]/span[2]/text()').extract()[0].strip() main_star=node.xpath('./div[@class="abstract"]/text()').extract()[1].strip() item=DoubanItem() # 创建对象 # item['film_name']=film_name # 这里就是一个赋值,item可以理解为一个安全的字典 item['score'] = score item['main_star'] = main_star print('输出:',dict(item)) # 可以转换成真正的字典 yield item # 这里与db.py文件不同的地方 self.page_num+=1 print('page_num',self.page_num) # 如何获得第n页?要对url分析找到规律 page_url='https://www.douban.com/doulist/3936288/?start={}&sort=time&playable=0&sub_type='.format(self.page_num*25) yield scrapy.Request(page_url,callback=self.parse) # callback 是回调函数 ''' 分析不同页的url找到规律:===》25的倍数 https://www.douban.com/doulist/3936288/?start=0&sort=time&playable=0&sub_type= 第1页 0*25 https://www.douban.com/doulist/3936288/?start=25&sort=time&playable=0&sub_type= 1*25 https://www.douban.com/doulist/3936288/?start=50&sort=time&playable=0&sub_type= 2*25 https://www.douban.com/doulist/3936288/?start=75&sort=time&playable=0&sub_type= https://www.douban.com/doulist/3936288/?start=100&sort=time&playable=0&sub_type= https://www.douban.com/doulist/3936288/?start=200&sort=time&playable=0&sub_type= 第9页 https://www.douban.com/doulist/3936288/?start=225&sort=time&playable=0&sub_type= 第10页 9*25 '''
db5.py源代码(如果爬到第11页《共10页数据》) 没有数据程序就要自动停止,通过if...else实现
以上已经实现豆瓣电影1-10页信息的爬取。