夏日炎炎,屋外三十多度的烈日温度,着实一点出门的欲望都无了,小编还是窝在宿舍里当个“肥宅”吧。豆瓣爬虫属于动静结合的数据类型,即列表页动态、详情页静态。对豆瓣高分电影信息进行详细爬取,并存入Excel表格,后续再对数据进行数据分析,简单可视化展示。
操作环境: Windows10、Python3.6、Pycharm、谷歌浏览器
目标网址: https://movie.douban.com/ (豆瓣电影)
相关文章: 拉钩爬虫、腾讯招聘爬虫、新笔趣阁爬虫、链家网爬虫
随意选择复制一个电影名字段,右键点击选择源码,快捷键Ctrl+F查看数据是否存在。
快速寻找ajax加载的接口链接方法:
1、右键点击检查;
2、选择Network;
3、点击XHR;(一般接口链接处于XHR中)
4、查看Time类型里的最大数值链接
接口链接正确,如图可知rate参数为评分,title为电影名,url为电影详情链接。
json数据中的url为电影详情页链接,提取电影详情页中的电影名、导演、编剧、主演、评分等等。
导入爬虫项目需要的库,并设置全局请求头,以便列表页及详情页的调用请求身份。
# 导入爬虫需要的库
import json
import re
import requests
import openpyxl
import time
# 全局请求头 处理基本反爬
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'
}
get请求携带的params参数,定一个get_params函数写params参数的构建, 传入电影分类与页数,以便每次列表页请求的携带调用。
def get_params(tag,pn):
'''
:param tag: 电影分类
:param pn: 页数
:return: 返回请求体
'''
params = {
# 请求体
"type": "movie",
"tag": str(tag),
"sort": "recommend",
"page_limit": "20",
"page_start": str(pn),
}
return params
电影列表页为点击“加载更多”翻页,而不是下拉翻页等。只能自己手动查看“豆瓣高分”这一分类的总页数为 25 ,不能像之前的动态项目那样提取json数据中的总数来while循环翻页。
for pn in range(0,500,20): # 翻页 间隔20
# 获取请求体
params = get_params('豆瓣高分',pn)
测试请求列表页,这里设置一页验证结果。另外需要注意的爬虫程序请求速率过快的情况下,会被豆瓣服务器反爬机制识别,进而封掉识别请求的ip。这时使用time模块延迟睡眠即可。
for pn in range(0,20,20): # 翻页 间隔20
# 获取请求体
params = get_params('豆瓣高分',pn)
# 请求列表页数据并编码
response = requests.get(url=url, headers=HEADERS, params=params).text
time.sleep(1.2) # 睡眠1.2秒,防止识别到是爬虫被封ip
print(response)
json.loads(): 将已编码的 JSON 字符串解码为 Python 对象
提取json数据比静态数据简单许多,直接一层层剥取到想要的数据即可。这里获取电影id、电影名、电影详情页链接。
def get_detail_url(url):
'''
获取电影详情页链接
:param url:列表页通用链接
'''
for pn in range(0,20,20): # 翻页 间隔20
# 获取请求体
params = get_params('豆瓣高分',pn)
response = requests.get(url=url, headers=HEADERS, params=params).text # 请求列表页数据并编码
time.sleep(1.2) # 睡眠1.2秒,防止识别到是爬虫被封ip
jsons = json.loads(response) # 将源码数据转换成json数据
for data in jsons['subjects']:
id = data['id'] # 电影id
title = data['title'] # 电影名
href_url = data['url'] # 详细电影链接
print(id,title,href_url)
if __name__ == '__main__':
url = 'https://movie.douban.com/j/search_subjects'
get_detail_url(url) # 获取详情页链接
定义一个get_detail_data函数,传入获取到的id、电影名、详情页链接,继续写请求规则。
response.status_code: 返回状态码
def get_detail_data(id,title,href_url):
'''
获取电影详情页数据
:param id: # 电影id
:param title: # 电影名
:param href_url: # 电影详情链接
:return:
'''
response = requests.get(url=href_url,headers=HEADERS) # 请求详情页
if response.status_code == 200: # 判断是否请求成功
text = response.text # 编码
print(text)
else:
print('详情页请求失败。。。')
请求详情页成功,进而提取详情数据。首先导入parsel库,即import parsel。将请求编译好的源码转化为Selector对象,以便于xpath或css语法提取数据。
import parsel
sele = parsel.Selector(text) # 转化为Selector对象
使用xpath语法提取数据,可在网页查看中按Ctrl + 快捷键,在出现的小框框内写xpath或者css语法提取数据。例如:导演。
以此类推,剩余有规律的属性字段皆可用xpath语法提取数据。例如:导演、编剧、电影评分、主演、电影类型、简介。
’’.join(): 将列表数据合并成字符串数据
extract_first()与get(): 同为获取一个,返回字符串型数据。
extract与getall(): 同为获取全部,返回列表型数据。
# 导演
director = sele.xpath('//div[@id="info"]/span[1]/span[@class="attrs"]/a/text()').extract_first()
# 编剧
scriptwriter = '/'.join(sele.xpath('//div[@id="info"]/span[2]/span[@class="attrs"]//a/text()').getall())
# 电影评分
movie_rating = sele.xpath('//strong[@class="ll rating_num"]/text()').extract_first()
if movie_rating == None:
movie_rating = 'None'
# 主演
protagonist = '/'.join(sele.xpath('//span[@class="actor"]//span[@class="attrs"]//a/text()').extract())
# 电影类型
movie_type = sele.xpath(
"//div[@class='subject clearfix']/div[@id='info']/span[@property='v:genre']/text()").extract()
movie_type = '/'.join(movie_type)
# 电影简介
movie_summary = ''.join(sele.xpath('//div[@id="link-report"]/span//text()').getall()).strip(
'©豆瓣')
wording = sele.xpath('//div[@id="link-report"]/span/a/text()').get()
if wording == '(展开全部)':
movie_summary = ''.join(sele.xpath('//span[@class="all hidden"]//text()').getall()).strip('©豆瓣')
正则表达式概念: 正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式是对字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,用来检索、替换那些符合规则的文本,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
剩余的字段数据:制片地区、语言、上映日期、片长、IMBD链接均在网页中属于不规律字段,若继续使用xpath语法提取这些字段信息的话,结果确实每个电影的字段顺序对应不上,即出现 “片长字段” 出现的是 “语言” 信息。
那么这些不规律的字段数据可以使用re正则匹配提取,导入模块import re。注意:re正则使用的是源码匹配,而不是parsel转化的Selector对象。这里以“制片地区”字段为例:
re.findall: 提取全部,为列表型数据,则最后[0]提取第一个数据。
pro_areas: 变量,为匹配的正则表达式。
text: 网页源码,提供正则匹配文本。
import re
# 制片地区
pro_areas = r'制片国家/地区: (.*?)
'
pro_area = re.findall(pro_areas, text)[0]
print(title,pro_area)
以此类推,提取的所有字段结果并将其填入空列表中。
print(
'id:',id+'\n','电影名:',title+'\n','导演:',director+'\n','编剧:',scriptwriter+'\n',
'电影评分:',movie_rating + '\n','主演:', protagonist + '\n', '电影类型:', movie_type + '\n',
'语言:', Language + '\n','制片地区:', pro_area + '\n', '上映日期:',movie_pubdate + '\n',
'片长:', length + '\n', 'IMBD链接:', IMBD_link + '\n', '电影简介:',movie_summary
)
print('*' * 150)
data_list.append([id, title, director, scriptwriter, movie_rating, protagonist, movie_type, pro_area,
movie_pubdate, length, IMBD_link, movie_summary])
openpyxl.Workbook(): 创建excel簿
wb.create_sheet(簿名): 写入簿名
row: 行
column: 列
value: 值
wb.save(文件名): 保存文件名
# 创建一个excel薄
wb = openpyxl.Workbook()
sheet1 = wb.create_sheet('movie')
# 写入excel表头
sheet1.cell(row=1, column=1, value='id')
sheet1.cell(row=1, column=2, value='电影名')
sheet1.cell(row=1, column=3).value = '导演'
sheet1.cell(row=1, column=4).value = '编剧'
sheet1.cell(row=1, column=5).value = '电影评分'
sheet1.cell(row=1, column=6).value = '主演'
sheet1.cell(row=1, column=7).value = '电影类型'
sheet1.cell(row=1, column=8).value = '制片地区'
sheet1.cell(row=1, column=9).value = '上映日期'
sheet1.cell(row=1, column=10).value = '片长'
sheet1.cell(row=1, column=11).value = 'IMBD链接'
sheet1.cell(row=1, column=12).value = '电影简介'
wb.save('豆瓣高分电影.xlsx')
定义save_excel函数,用于写Excel存储数据功能。
def save_excel(data_list):
wb = openpyxl.load_workbook('豆瓣高分电影.xlsx')
sheet = wb['movie'] # 选择簿名
for d in data_list: # 遍历列表数据
sheet.append(d) # 写入数据
wb.save('豆瓣高分电影.xlsx') # 保存
“豆瓣高分”电影分类共25页,每页至多20条数据。运行保存的文件结果为500条数据,成功提取无误。
豆瓣电影爬虫需要注意的地方有两点:
一、反爬机制
豆瓣的反爬措施是封禁频繁请求ip,少则几个小时,多则一天。使用time模块 time.sleep睡眠1至2秒即可。
二、匹配规则
电影详情页字段的匹配,有规律的字段数据则使用xpath语法提取,反之无规律且错乱位置的则使用re正则匹配提取。
此次项目到此结束,若有小伙伴有疑惑的地方,可在评论区留言,小编看到会尽快一一回复;此项目有需要改进的地方,也请大佬不吝赐教,感谢!
注:本项目仅用于学习用途,若用于商业用途,请自行负责!!