写点什么

爬虫入门经典 (十二) | 一文带你快速爬取豆瓣电影

用户头像
不温卜火
关注
发布于: 2021 年 03 月 22 日

  大家好,我是不温卜火,是一名计算机学院大数据专业大三的学生,昵称来源于成语—不温不火,本意是希望自己性情温和。作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己所犯的错误希望能够帮助到很多和自己一样处于起步阶段的萌新。但由于水平有限,博客中难免会有一些错误出现,有纰漏之处恳请各位大佬不吝赐教!暂时只在 csdn 这一个平台进行更新,博客主页:https://buwenbuhuo.blog.csdn.net/


**PS:由于现在越来越多的人未经本人同意直接爬取博主本人文章,博主在此特别声明:未经本人允许,禁止转载!!!**


@TOC






推荐

  ♥各位如果想要交流的话,可以加下 QQ 交流群:974178910,里面有各种你想要的学习资料。♥


  ♥欢迎大家关注公众号【不温卜火】,关注公众号即可以提前阅读又可以获取各种干货哦,同时公众号每满 1024 及 1024 倍数则会抽奖赠送机械键盘一份+IT 书籍 1 份哟~♥

一、小小课堂


在古代,每当你闲暇之时,会同老友几人一起吃酒喝茶,觥筹交错,畅所欲言!而在如今,我们在无聊之时,又正值好友几人闲暇之时,可能会同去看当下最新的电影。但是如果只有我们自己一人,这时的你会怎如何做呢?


如果是我的话,我就会先自行查看豆瓣电影的评分,从中选择自己感兴趣的影片进行观看。这样不仅节省了我们的时间,更能陶冶我们的情操。同时我们还能在观影之后,查看别人给出的影评,从中给出中肯的评价,好为别人提供价值标杆。

好吧,说了那么多其实就是为了引出我们今天这篇博文所要爬取的网站——《豆瓣电影》。-。-


由于咱是技术博主,所以这些文邹邹的话咱就不写了哈。


以往的博文,大都是讲解的怎样爬取静态网页。这不,本篇博文博主专门选择了动态网页豆瓣电影进行数据采集。


所谓动态网页加载是通过 js 的 ajax 加载的数据或 js 算法(加密)得到的数据,并不是直接可以得到的数据结果。


豆瓣电影这个网站是通过 ajax 加载的数据。为什么会这样说呢?一会儿在分析网页结构的时候,博主会进行解释!


二、大体过程分析

在此,先给出豆瓣电影的 URL:https://movie.douban.com/chart


- [ ] 1. 分析获取的 URL

- [ ] 2. 单击分类信息,跳转到分类电影列表


这个页面是有多页数据加载的,当用户向下滚动右侧的滚动,加载数据,这个经过分析是 ajax 加载的数据,需要找到 ajax 请求的网址。

先找到分类,提取分类的名字和类型编号,然后再爬分类下的电影数据。


- [ ] 2.提取数据的方法


ajax 返回的数据是 json,response.json()得到的是字典,用字典操作就可以了,当然用正则是肯定可以的。其实专门操作 json 的有一个模块叫 jsonpath。


三、具体细节分析

3.1 先获取整个网页的源码


import requestsfrom lxml import etree
type_url = "https://movie.douban.com/chart"
headers = { "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",}
def parse_url(url): """解析url,得到html""" response = requests.get(url=url, headers=headers) return response.content.decode("utf-8")

def parse_html(html): """解析url,得到字典""" etree_obj = etree.HTML(html) return etree_obj
content = parse_url(type_url)print(content)
复制代码


3.2 获取分类

我们首先先看下网页内存在 iframe 没有

由于有 iframe 所以不能使用 xpath


下面我们先来看下我们所要爬取分类的格式

我们可以看到其格式为<a href="/typerank?type_name=剧情&amp;type=11&amp;interval_id=100:90&amp;action=">剧情</a>


既然 xpath 不能使用,那么我们就使用正则进行解析


<a href="/typerank?type_name=剧情&amp;type=11&amp;interval_id=100:90&amp;action=">剧情</a>
r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>'
复制代码


🆗,我们已经成功拿到电影的分类,下面开始尝试拿到其中一个分类中的所有数据


3.3 获取一页的所有数据

movie_url = "https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20"response = requests.get(url=movie_url,headers=headers)content = response.json()print(content)
复制代码


3.4 循环获取所有 URL 及内容

我们先看下地址栏的 URL


URL 对比


https://movie.douban.com/typerank?type_name=%E5%89%A7%E6%83%85%E7%89%87&type=11&interval_id=100:90&action=https://movie.douban.com/typerank?type_name=%E5%89%A7%E6%83%85%E7%89%87&type=11&interval_id=90:80&action=
复制代码

我们可以看到其中的interval_id是以 10 为单位移动的。那么我们是不是能够有一个大胆的想法能否拼接这个网址,然后循环爬取内容么呢?

答案是可以的,在此博主就不多讲解了,直接给出代码



movie_url = "https://movie.douban.com/j/chart/top_list"
def get_movie(movie_type, low_score, high_score): """获取电影""" movie_type_name = movie_type[0] movie_type_num = movie_type[1] print(movie_type_num)
i = 0 while True: # 参数 params = { "type": movie_type_num, "interval_id": "{}:{}".format(high_score, low_score), "action": "", "start": i, "limit": 20 } # 发请求获取数据 content = parse_json(movie_url, params) print(content) exit()

def start(): """爬虫开始""" low_score = int(input("输入要爬取的最低分(以5为单位),最高分默认是加10>")) high_score = low_score + 10
movie_type_list = get_movie_type() for movie_type in movie_type_list: get_movie(movie_type, low_score, high_score)

if __name__ == '__main__': start()
复制代码


3.5 得到数据

至于此部分只是单纯的提取 JSON 中的数据,在此就不多解释了


def get_movie(movie_type, low_score, high_score):    """获取电影"""    movie = {        "title": "",  # 电影名称        "actors": "",  # 主演        "release_date": "",  # 上映日期        "regions": "",  # 上映地        "types": "",  # 类型        "score": "",  # 评分        "vote_count": "",  # 评论数        "url": "",  # url    }
movie_type_name = movie_type[0] movie_type_num = movie_type[1]
i = 0 while True: # 参数 params = { "type": movie_type_num, "interval_id": "{}:{}".format(high_score, low_score), "action": "", "start": i, "limit": 20 } # 发请求获取数据 data_list = parse_json(movie_url, params) # 判断循环退出 if not data_list: break # 循环 for data in data_list: movie["title"] = data["title"] movie["actors"] = data["actors"] movie["release_date"] = data["release_date"] movie["regions"] = data["regions"] movie["types"] = data["types"] movie["score"] = data["score"] movie["vote_count"] = data["vote_count"] movie["url"] = data["url"] save(movie)
i += 20
复制代码


四、完整源码


# encoding: utf-8'''  @author 李华鑫  @create 2020-10-09 8:27  Mycsdn:https://buwenbuhuo.blog.csdn.net/  @contact: 459804692@qq.com  @software: Pycharm  @file: 豆瓣电影.py  @Version:1.0  '''import requestsimport timeimport reimport randomimport csvfrom lxml import etree
type_url = "https://movie.douban.com/chart"movie_url = "https://movie.douban.com/j/chart/top_list"
headers = { "user-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",}

def parse_html(url, params={}): """解析url,得到html""" response = requests.get(url=url, headers=headers, params=params) return response.content.decode("utf-8")

def parse_json(url, params={}): """解析url,得到字典""" response = requests.get(url=url, headers=headers, params=params) return response.json()

def get_movie_type(): """获取电影分类""" content = parse_html(type_url) return re.findall(r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>', content)

def get_movie(movie_type, low_score, high_score): """获取电影""" movie = { "title": "", # 电影名称 "actors": "", # 主演 "release_date": "", # 上映日期 "regions": "", # 上映地 "types": "", # 类型 "score": "", # 评分 "vote_count": "", # 评论数 "url": "", # url }
movie_type_name = movie_type[0] movie_type_num = movie_type[1]
i = 0 while True: # 参数 params = { "type": movie_type_num, "interval_id": "{}:{}".format(high_score, low_score), "action": "", "start": i, "limit": 20 } # 发请求获取数据 data_list = parse_json(movie_url, params) # 判断循环退出 if not data_list: break # 循环 for data in data_list: movie["title"] = data["title"] movie["actors"] = data["actors"] movie["release_date"] = data["release_date"] movie["regions"] = data["regions"] # 国家 movie["types"] = data["types"] # 类型 movie["score"] = data["score"] # 评分 movie["vote_count"] = data["vote_count"] # 评论条数 movie["url"] = data["url"] # url save(movie)
i += 20
def save(item): """将数据保存到csv中""" with open("./豆瓣电影.csv", "a", encoding="utf-8") as file: writer = csv.writer(file) writer.writerow(item.values())
def start(): """爬虫开始""" low_score = int(input("输入要爬取的最低分(以5为单位),最高分默认是加10>")) high_score = low_score + 10
movie_type_list = get_movie_type() for movie_type in movie_type_list: print("{}爬取中...".format(movie_type[0])) get_movie(movie_type, low_score, high_score)

if __name__ == '__main__': start()


# 测试代码:# content = parse_url(type_url)# # 由于有iframe 所以不能使用xpath# print(re.findall(r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>',content))### """# <a href="/typerank?type_name=剧情&amp;type=11&amp;interval_id=100:90&amp;action=">剧情</a>## r'<a href="/typerank\?type_name=(.*?)&type=(\d+)&interval_id=100:90&action=">.*?</a>'# """## response = requests.get(url=movie_url,headers=headers)# content = response.json()# print(content)
复制代码


五、保存完成结果


!31


美好的日子总是短暂的,虽然还想继续与大家畅谈,但是本篇博文到此已经结束了,如果还嫌不够过瘾,不用担心,我们下篇见!




&emsp;&emsp;好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。

&emsp;&emsp;如果我的博客对你有帮助、如果你喜欢我的博客内容,请“点赞” “评论”“收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。

&emsp;&emsp;码字不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注我哦!




发布于: 2021 年 03 月 22 日阅读数: 11
用户头像

不温卜火

关注

一个小菜鸡 2021.03.22 加入

数据采集/数据清洗/数据可视化/大数据开发

评论

发布
暂无评论
爬虫入门经典(十二) | 一文带你快速爬取豆瓣电影