目录
有开源的豆瓣电影评论数据集,但是主要是评论数据,电影信息很少。没有找到电影信息数据集,也就是主键为电影,附带电影的特征的数据,特征包括导演、演员、时长、电影类型、出品公司、制片地区、上映时间、对白语言、剧情描述等。
拿来主义行不通,那就只好自己动手丰衣足食。好在豆瓣电影网站的结构并不复杂,爬取其电影信息的工作并不会耽误太多的时间。本文记录电影信息爬取的一种可行过程,不一定为最佳方案,欢迎读者发表自己的想法。
0 写在前面
本文并不是专业的爬虫指导,只能说是一位爬虫菜鸟的学习笔记。仅就粗浅的爬虫经验,总结一些探索过程。
0.1 内容网站用户旅程
我们先总结内容网站成熟用户一般旅程。成熟用户省略了注册、信息补充等内容,日常活跃的基本行为就是消费网站提供的内容,所以最终目标都是抵达感兴趣的内容详情页或使用页。除了内容详情页,用户访问较多的页面还有:
(1)首页
进入网站的第一页,一般将罗列重要的栏目,每个栏目下罗列重要的频道,每个频道下罗列推荐的内容。当然,如果某个栏目是最重要的,首页上可能只展示这个栏目下的频道及包含的推荐内容。次要的栏目需要用户在网页的上部或左边等显眼的位置去切换。首页上往往也提供了进入内容筛选页的入口,一般是点击搜索栏、带有“更多”、“库”等字眼的按钮。
(2)栏目
有些内容网站提供的内容类型很丰富,先将内容按照大类区分,分别放置在不同的栏目,比如视频类网站,给长视频、短视频分别设置栏目。用户点击栏目后,一般都会罗列该栏目下的常用频道,每个频道下展示少许推荐内容。
(3)频道
进入频道后一般网页就会变得比较简单了,往往只包含内容列表,有些会提供少量内容基础属性的筛选和排序,内容列表在多个页码(上一页、下一页)上展示,或者根据用户滑动页码的动作对列表进行刷新。
(4)内容筛选
上面几个页面上出现的默认内容往往都是网站推荐曝光的,而内容全量的筛选展示页则赋予了用户更多选择权,该页也是用户能从网站上看到最全的内容列表的位置,一般将栏目、频道和其他重要内容特征作为筛选选项。我们只要遍历必要的选项的所有排列组合,将每种组合下的内容列表加载完毕,就能获取足够的内容列表。
用户从上面几种页面都可以抵达内容详情页,所以有多种常见旅程:
(1)用户进入首页后,可能被首页推送的内容吸引,直接进入内容详情/使用页。
(2)用户进入首页后,选择栏目,被栏目内推送的内容吸引,直接进入内容详情/使用页。
(3)用户进入首页后,选择栏目、频道,被频道内推送或筛选出的内容吸引,进入内容详情/使用页。
(4)用户进入首页后,直奔内容筛选,按照喜好配置筛选选项、排序方式,点击结果列表中的兴趣内容,进入内容详情/使用页。
0.2 内容网站信息爬取一般步骤
使用爬虫获取内容信息,也是要顺着这样的路线触达目标网页。上面的路径中,(1)(2)往往只会包含少了内容,(3)(4)一般会看到所有的上架内容。由此总结内容信息爬取的一般步骤:
步骤1:确定完整的主体列表网页及网址组成规律
我们想要爬取豆瓣电影网站上的电影信息,主体就是电影,相关的信息肯定是需要在具体某部电影独占的详情页获取,但是我们并不能直接知道所有电影的详情页的网址,必须从上一级链接的网页(电影清单页)中获取电影的列表,这个列表中往往直接包含了每部电影网址。
但是一般网站不会将所有内容都放在一页上展示(但是也有),这未免有些无趣,也不便于用户在列表中获取兴趣内容。常见的做法是网站给一些单选筛选项设置了默认值,用户可以修改这些筛选项,每次只展示选项下单个值覆盖的内容列表;遍历这些选项值就能看到所有的内容列表。这些选项有时会作为参数在网址上体现,这样我们就能获取所有的内容列表页的网址。
步骤2:确定主体信息网页及从主体列表网页获取主体信息网址的方式
步骤1中的内容列表蕴含着跳转每个内容详情页的链接,我们可以先点击内容列表中的一些内容进入详情页,查看并记住页面网址,然后查看内容列表页的HTML代码,看看这些内容详情页网址在代码中的位置。如此我们便获取了所有内容详情页的网址。
步骤3:确定从主体信息网页获取信息的方式
进入内容详情页后,查看我们关心的信息在页面的位置和在代码位置。
1 豆瓣电影网站情况
开始对豆瓣电影网站进行探索之前,需要先明确爬取这些数据是想用在一些自然语言处理的实验中,并不是有使用化意图。所以我们只需要获取足够数量的电影即可,最好在电影类型维度的关键枚举值上都有分布。
1.1 电影清单页选择
豆瓣电影首页如下图所示,从网页上面的导航栏来看,电影清单页就在选电影
或排行榜
里。当然,这个网页结构比较简单,我们可以每个导航栏都点进去看看情况。
通过对该网站的探索,我们发现选电影
和排行榜
都包含电影清单,相似但有略微的差别。
(1)“选电影”
该页面支持电影类型、地区、年代、标签多个维度的筛选,还能选择排序方式。我们最关注的是电影类型维度,需要在不同类型中都摘取一些电影。选定类型后,默认展示19部电影,每点击最下面的加载更多
一次,会再添加20部电影。
我们选定电影类型、排序方式后,这些设定并没有体现在网址上,所以爬虫代码中需要模拟鼠标点击功能,依次完成每种类型的选择和排序方式的选择(排序方式并不重要,但是我们还是希望摘取更有关注度的电影),并在每种类型下多次点击加载更多
,点击次数取决于你准备每种类型摘取多少部电影。
(2)“排行榜”
该页面支持电影类型的选择,支持在固定排序(看起来应该是按照打分排序)中选择排名区间,区间长度固定为10个百分点,但可以滑动选择区间的位置。电影清单默认展示20部电影,滚动鼠标到底部自动加载更多电影,也是每次增加20部。该网页提醒,那些没有打分的电影不会在这个网页上展示。没有打分的电影关注度也太低了,此处我们选择放弃这些电影。
电影类型的选择和区间的选择都有规律地体现在网址上,这就为爬虫提供了很大的便捷。但是想要获取更多电影,还是需要在爬虫代码中多次模拟鼠标滚动到底部,并延时等待数据刷新。
对比来看,“排行榜”页面能满足我们的需要,且通过对网址的拼接处理,省去了很多页面点击的需求,对编码的要求较低。我们以此页面为突破口进一步探索背后的数据。
1.2 电影排行榜页探索
(1)分类排行榜网址规律
通过遍历排行榜页所有电影类型,观察网址变化情况,并通过修改网址各部分请求到的网页情况,总结网址各组成部分的作用如下:
https://movie.douban.com/typerank? | type_name=类型名称 | &type=类型ID | &interval_id= 排名区间 | &action= |
---|---|---|---|---|
前缀 | 类型名称 | 类型ID | 排名区间 | 后缀 |
必须 | 必须但不起作用 | 必须 | 必须 | 可空 |
网址中对电影类型的筛选起作用的是type参数,类型名称和类型ID的对应关系我们总结如下:
序号 | 类型ID | 类型名称 |
---|---|---|
1 | 1 | 纪录片 |
2 | 2 | 传记 |
3 | 3 | 犯罪 |
4 | 4 | 历史 |
5 | 5 | 动作 |
6 | 6 | 情色 |
7 | 7 | 歌舞 |
8 | 8 | 儿童 |
9 | 10 | 悬疑 |
10 | 11 | 剧情 |
11 | 12 | 灾难 |
12 | 13 | 爱情 |
13 | 14 | 音乐 |
14 | 15 | 冒险 |
15 | 16 | 奇幻 |
16 | 17 | 科幻 |
17 | 18 | 运动 |
18 | 19 | 惊悚 |
19 | 20 | 恐怖 |
20 | 22 | 战争 |
21 | 23 | 短片 |
22 | 24 | 喜剧 |
23 | 25 | 动画 |
24 | 27 | 西部 |
25 | 28 | 家庭 |
26 | 29 | 武侠 |
27 | 30 | 古装 |
28 | 31 | 黑色电影 |
当然,表1-1内容最好是从“排行榜”网页的“更多类型”处获取。
28种电影类型和10个排名区间排列组合,形成28×10=280个网址,遍历这些网址就能获取排行榜网页中的所有电影。
确定排行榜网址后,每个网址还需要确定滚动到网页最下面加载更多电影的次数,网页加载出来后会显示符合筛选条件的电影的数量,与每次加载的电影数量相除就能知道需要额外加载的次数。例如图1-3中喜剧类型的排名100%~90%区间的电影数为646,需要再加载math.ceil(646 / 20 - 1)(=32)次才能完全展示。
(2)排行榜页电影清单代码
在该页面使用F12
键或者在菜单栏调出开发者工具,选择查看栏目为元素
(Edge、Chrome中为这个名称,FireFox中为查看器
),点击左上角的鼠标指针图标(①位置),将鼠标指针移动到网页的电影清单区域(②位置)。可以看到鼠标指针到达的地方,被指定的位置的元素会被半透明的蓝色覆盖。不停地移动鼠标位置,确保我们选到了包含所有电影清单的最小范围,点击鼠标左键选定,元素
中会跳到对应的代码块(③位置)。
将鼠标指针移动到代码上,网页对应的元素依然会突出显示。我们展开③位置的代码,移动鼠标观察每行代码对应的元素,直到选中了具体某部电影的名称(点击电影名称可以跳转电影详情页),多次展开后可以看到④位置的代码情况。看起来我们似乎找到了电影详情页的网址。
③位置的代码下包含了20条类似④位置的代码,每条都对应一部电影。如果我们向下滑动左边的网页加载更多的电影,右边的代码处也会刷新更多的电影对应的代码。
1.3 电影详情页探索
点击图1-4中的某部电影链接进入电影详情页,可以看出图1-5网址与图1-4中④位置网址一致,这下我们终于确定了:电影详情页网址就在电影清单代码里。而且我们可以猜测,网址中最后一段数字就是该电影的编码。
由此,我们从电影清单页到电影详情页的路径已经打通。接下来就是获取详情页的信息。
我们需要的信息都集中在网页的上面,至于下面的评论、获奖,我们暂时并不关心。继续使用1.2(2)中的方法探索电影详情页信息在代码中的位置,如图1-5所示。
2 爬虫关键代码
2.1 爬取全流程
开始写代码之前需要有清晰的思路。再次梳理我们爬虫的过程:
(1)打开“排行榜首页”,获取所有电影类型及对应的类型ID;
(2)修改“分类排行榜”网址中的type
及interval_id
参数的值,形成28个电影类型的280个分类分区间排行榜网址;
(3)打开每个分类分区间排行榜网址,多次滚动到网页底部加载榜单中所有电影,获取电影名称和网址。由于一部电影会出现在多个类型中,最后获取的电影网址需要去重;
(4)遍历每部电影的网址,获取电影关键属性信息。
2.2 打开网址及滚动到底
由于在电影排行榜网页中需要滚动底部,所以需要模拟浏览器打开网址及滚动到网页底部的动作。
我们使用三方Python包selenium
模拟浏览器相关行为,该三方包需要具备浏览器对应版本的驱动才能正常工作。如何安装请自行百度。我们此处使用的是chrome浏览器。
(1)打开网址
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
url = 'https://xxxxxxxxxx'
options = webdriver.ChromeOptions()
options.add_experimental_option('detach', True)
service = Service(executable_path=chromedriver_path)
browser = webdriver.Chrome(options=options, service=service,
keep_alive='True')
browser.get(url)
(1)滚动到网页底部
js = "window.scrollTo(0, document.body.scrollHeight)"
browser.execute_script(js)
2.3 不同位置信息提取
selenium
提供了几种在html代码中定位目标元素的方法,可以按照xml路径(XPATH),可以按照标签名(TAG_NAME),可以按照分级名(CLASS_NAME)。本案例中使用如上三种定位方式就可以了,一些较复杂的定位可以分步抵达,即先定位到大区域,再在大区域中定位目标元素,较少不相关的内容的干扰。
selenium
的定位方法请自行百度,应该能找到更专业的教程。
需要注意的是,得确定你想定位的元素在同类网页的不同实例中的定位值不会变化。例如,图1-5中的电影属性模块①②③,“导演”、“编剧”、“演员”等属性在大部分电影中都是具备的,且保持固定的排序,但是也有少部分电影缺失部分属性,或多了一些属性。属性的增多或较少会导致属性序号的变化,当我们使用XPATH方式获取定位时就会发生问题。此处“导演”的元素的XPATH为//*[@id="info"]/span[1]/span[2]
,其中第一个span
表示整个①②③模块,“导演”位于这个模块的第一位,所以span[1]
就能定位到“导演”所在行。第二个span
定位的是导演姓名的元素,故而索引为2。如果某部电影没有导演信息,“编剧”在①②③模块中的顺序就由2变成了1。所以需要根据①②③模块的文本信息做相应的调整,动态修改xml路径:
'//*[@id="info"]/span[{}]/span[2]'.format(idx)
3 结果数据处理
3.1 电影详情页网址去重
一部电影是多种类型的,所以按照上面获取电影清单的方式,我们会在多个类型下看到同一部电影,所以需要对最终获取的全部电影网址进行去重。
3.2 电影属性处理
(1)电影名称
非国产电影详情页的电影名称是中文名称加外文名称的组合,我们还是采用电影清单页面的中文名称。
(2)多值属性
导演、编剧、演员、类型、制片地区、语言、上映日期、片长、别名等属性都为多值,我们采取拼接处理,分隔符与豆瓣保持一致,为斜杆符号“/”。
导演、编剧、演员除了文本信息,还有网址链接,跳转到创作者的主页。使用“|”对不同的网址进行隔离。
(3)评分相关
结果分、评论人数、不同打分的人数比例都使用单独的字段进行存储。
(4)剧情简介
存储之前先将文本中可能存在的表字段分隔符剔除。
3.3 样例数据
4 数据情况
数据浅层分析后续单开一篇文章。
数据下载:HuggingFace | CSDN
5 写在最后
俗话说:爬虫写得好,牢饭吃到饱(不是)。合理利用网络资源,避免给别人带来麻烦和损失。
代码可以在GitCode处获取。