Scrapy 入门_第一步_实战豆瓣电影TOP250
学习东西喜欢先做出效果,会用是第一步,然后再去了解原理。
1.爬虫需求
爬取豆瓣电影TOP250的数据,
爬取字段:排名、封面、电影名称、主演阵容、电影信息、评分、评价人数、名言
写入 txt 文件
2.实现效果
爬取的信息写入 txt文件
3.爬虫设计
3.1 目标分析
首先 F12 查看 html 代码结构,并观察分页参数
搜索的电影信息都在 一个 class=“item” 的div中
观察地址栏发现 分页规律
第一页:
https://movie.douban.com/top250?start=0&filter=
第二页:
https://movie.douban.com/top250?start=25&filter=
第三页:
https://movie.douban.com/top250?start=50&filter=
3.2 代码设计
这里学习使用 Scrapy 爬取数据。
Scrapy 项目结构
.
├── dianyingTop250 #外层目录
│ ├── __init__.py #初始化脚本
│ ├── __pycache__ #Python缓存文件。暂时无视
│ ├── items.py #Items代码模板,继承类自scrapy.Item
│ ├── middlewares.py #Middlewares代码模板(继承类)
│ ├── pipelines.py #Pipelines代码模板(继承类)
│ ├── settings.py #Scrapy爬虫的配置文件
│ └── spiders #Spiders代码模板目录 我们写爬虫的地方
│ ├── __init__.py
│ └── __pycache__
└── scrapy.cfg #部署爬虫的配置文件
先搭建一个Scrapy 工程
新建一个 python 工程
在case 1 下 运行 scrapy 创建工程的命令
没有安装 scrapy 的 运行 pip install scrapy 即可安装 scrapy。
scrapy startproject dianyingTop250
代码结构如下,这里是我已经搭建好,并运行的项目,所以多了mian.py 和 top250.txt 文件
进入到 dianyingTop250 的目录下执行命令,新建一个爬虫,并指定 要爬取的网站,注意不需要带http 或https协议。
scrapy genspider jobbole movie.douban.com
完成出现如下文件,这里的代码是我已经写好的
定义需要爬取的字段
爬虫主体代码
from urllib.request import Request
import scrapy
from ..items import Dianyingtop250Item
class Top250Spider(scrapy.Spider):
# 爬虫名称
name = 'top250'
# 规定爬虫爬取网页的域名
allowed_domains = ['movie.douban.com']
# 开始爬取的url
start_urls = []
# 我们爬取10页的 豆瓣电影 Top 250
# 简单点 我们知道要爬取10页的数据,先把要爬取的10页数据的 url 生成放入start_urls
for i in range(0, 250, 25):
start_urls.append('https://movie.douban.com/top250?start=' + str(i) + '&filter=')
def parse(self, response):
"""
此方法 负责解析 爬取的内容
以下主要考验 xpath 语法的熟练程度
xpath 学习 : https://www.runoob.com/xpath/xpath-tutorial.html
"""
list_li = response.xpath('//div[@class="item"]')
for li in list_li:
"""
这里有一个 很坑的地方,导入Dianyingtop250Item时,
工具生成的 import 语句 在运行时出错,需要手动改为
from ..items import Dianyingtop250Item
"""
item = Dianyingtop250Item()
# 排名
item['ranking'] = li.xpath('.//div[1]/em/text()').extract_first()
# 电影封面
item['front_cover'] = li.xpath('.//div/a/img//@src').extract_first()
title = li.xpath('.//div[2]/div[1]/a')
title_text = title[0].xpath('string(.)').extract()[0].strip()
# 电影title
item['movie_name'] = title_text.replace(' ','').replace('\n','')
# 主演信息
item['star_list'] = li.xpath('.//div[2]/div[2]/p[1]/text()[1]').extract_first().strip()
# 电影信息
item['movie_information'] = li.xpath('.//div[2]/div[2]/p[1]/text()[2]').extract_first().strip()
# 电影评分
item['grade'] = li.xpath('.//div[2]/div[2]/div/span[2]/text()').extract_first().strip()
# 评价人数
item['evaluators_count'] = li.xpath('.//div[2]/div[2]/div/span[4]/text()').extract_first().strip()
# 名言引用
quote = li.xpath('.//div[2]/div[2]/p[2]/span/text()')
if len(quote) > 0:
item['saying'] = quote.extract_first().strip()
else:
item['saying'] = ''
# 运行完成后 会进入管道,管道需要在 setting.py 中配置
yield item
管道代码
主要是 拿到item 中的数据,已追加的形式 塞入txt 文件。
# 处理管道
class Dianyingtop250Pipeline:
def process_item(self, item, spider):
# 写入文件夹
with open('top250.txt','a+',encoding='utf-8') as f:
f.write('排名:{}\n 封面:{} \n 电影名称:{} \n 主演阵容:{} \n 电影信息:{} \n 评分:{} \n'
'评价人数:{} \n 名言:{} \n\r'.format(item['ranking'],item['front_cover'],
item['movie_name'],item['star_list'],
item['movie_information'],item['grade']
,item['evaluators_count'],item['saying'],))
配置scrapy 爬虫
ROBOTSTXT_OBEY = False 设置不遵守爬虫规则,一般网站都有reboot.txt, 如果网站不允许你爬取,scrapy 默认会准守它的规则,这里 一般自行设置为 False。
其他的注释都写得很清楚了
3.3 扩展输出结果方式_输出JSON 格式文件
在 pipelines.py 文件 新增一个 管道 class, 并在 Dianyingtop250Pipeline 上面用到的管道中返回 item,
用于 此管道的对象使用。
class Dianyingtop250JSONPipeline:
def process_item(self, item, spider):
with open('top250.json', 'a+', encoding='utf-8') as f:
"""
Python 3中的json在做dumps操作时,会将中文转换成unicode编码,
并以16进制方式存储,再做逆向操作时,会将unicode编码转换回中文
json
dump有一个ensure_ascii参数,当它为True的时候,
所有非ASCII码字符显示为\\uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示
"""
f.write(json.dumps(dict(item),ensure_ascii=False) + '\n')
return item
设置中新增管道类,这里数字越小 优先级 越高。
写入结果集如下。
3.4 扩展 Scrapy 下载电影封面
###### 3.4.1 自定义管道实现
在 pipelines.py 文件 添加 CustomDownloadPipeline 类,把文件下载到
#自定义管道 实现文件下载
class CustomDownloadPipeline:
def create_dir(self, dir_path):
if not os.path.exists(dir_path): os.mkdir(dir_path)
def process_item(self,item,spider):
#电影封面
front_cover = item['front_cover']
ranking = item['ranking']
# 创建目录,如果没有则创建
self.create_dir('frontCover')
request.urlretrieve(front_cover,'frontCover/'+ranking+'.png')
return item
# 暂时没用到,本来想用电影名称的, 电影名称含有特殊符号
def convert_to_pinyin(self,world):
# 将中文汉字转换成不带声调的拼音
to_pinyin = [''.join(i) for i in pypinyin.pinyin(world, style=pypinyin.NORMAL)]
to_pinyin = ''.join(to_pinyin)
return to_pinyin
配置中 添加 自定义管道类 CustomDownloadPipeline
效果如下
3.4.2 使用 Scrapy 提供的 FilesPipeline 下载封面
item 添加两个属性,file_urls 用于存放待下载的文件链接,files 用于存放 下载完成的文件对象。
爬虫下 多赋值属性 file_urls
最后设置 文件下载的管道类 scrapy.pipelines.files.FilesPipeline 固定写法,并设置文件下载后存储的位置。
效果如下:
FilesPipeline 请求的请求头是固定的,有些网站对Scrapy 做了特定处理,那么就会爬取不了,这时候可以自定义类,继承 FilesPipeline 重写 get_media_requests 方法。
3.4.3 使用 Scrapy 提供的 ImagesPipeline下载封面
同 FilesPipeline ,使用 ImagesPipeline 也需要 特定的属性
赋值图片链接地址
为了区分,我把 FilesPipeline 的存储路径换成了 files
ps: 一定要安装 pip install pillow ,否则执行后无效,这是 ImagesPipeline 的依赖库。
运行效果
虽然不是每天都能完成计划,但也要坚持,赶回来。加油 2021-05-12 1:26:00
继续继续,每天一点点 都是在进步 2021-05-13 1:31:00
成功不是凭梦想和希望,而是凭努力和实践。
评论区