python爬虫案例_Python“豆瓣电影TOP250爬虫案例”详解

实现目标

本案计划实现:通过网络请求,获取豆瓣电影TOP250的数据,并存储到Json文件中。

案例应用技巧:

  • GET请求(requests):headers
  • 翻页
  • 网页解析(BeautifulSoup)

实现过程

总体来说,简单的单线程爬虫的实现流程如下:

  • 确定数据所在的Url,以及控制翻页的参数
  • 执行网页请求并解决请求中出现的问题
  • 解析网页,获取格式化数据
  • 实现翻页
  • 存储数据

下面我们按着以上步骤来依次完成。

确定数据所在Url

在Chrome浏览器中打开豆瓣电影TOP250,其Url为:https://movie.douban.com/top250。

e3934b7cd39bbba9b61ff63d730c04f9.png

通过观察,可以发现目标数据是存在于网页源代码中的,直接请求网页Url即可获得。

同时,第1页仅显示了25部电影,说明我们需要请求10次不同的页面才能完成所有250部电影的抓取。于是我们在页面上点击打开第2页,发现第2页的Url是:

https://movie.douban.com/top250?start=25&filter=

相较于第1页的Url,第2页增加了start参数和filter参数,因为filter参数内容为空值,很可能是用来”我没看过的“选项的筛选的,因此start参数应该是实际起作用的。因为第1页有25个电影,而第2页的start参数为25,说明start很可能是指当前页面是从第多少部电影开始显示的。

为了检验这个结果,我们使用浏览器打开start参数值为0的页面,果然正确地打开了第1页。因此,我们只需要修改start参数就可以有效地实现翻页了。

网页请求

下面我们开始尝试使用Python实现请求,在这个过程中,建议大家使用IDLE或PyCharm的Python Console模式来进行测试,减少网站的实际请求次数,以减少对目标网站的影响,以免被IP封锁。

362b8c40300f2df65e6682f65943d0ed.png

我们先以第1页为例进行尝试,发现直接使用requests的请求并不能获得数据,说明我们的请求被拒绝了。因此,我们可以打开Chrome控制台中访问页面时的请求,选择headers选项卡,查看其中的Request Headers,并依据这个headers来伪装我们的请求。实现代码如下:

 import requests ​ headers = {     "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",     "accept-language": "zh-CN,zh;q=0.9",     "cache-control": "no-cache",     "Connection": "keep-alive",     "host": "movie.douban.com",     "pragma": "no-cache",     "Sec-Fetch-Dest": "document",     "Sec-Fetch-Mode": "navigate",     "Sec-Fetch-Site": "none",     "Sec-Fetch-User": "?1",     "Upgrade-Insecure-Requests": "1",     "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36", } ​ response = requests.get("https://movie.douban.com/top250", headers=headers) ​ print(response.content.decode(errors="ignore"))

暂时先不要在headers中添加“Accept-Encoding”,否则请求结果可能会被压缩,影响解析。

在打印出的内容中,我们通过搜索可以找到我们需要的数据。

网页解析

下面,我们使用CSS选择器将电影的各类信息解析出来(先定位到每个电影的标签,再定位到各类信息的标签,最后将各类信息从标签中提取出来)。在解析过程中,我们要随时考虑到我们当前解析的标签可能不存在,并努力避免因标签不存在而报错。实现代码如下:

 from bs4 import BeautifulSoup ​ bs = BeautifulSoup(response.content.decode(errors="ignore"), 'lxml') for movie_label in bs.select("#content > div > div.article > ol > li"):  # 定位到电影标签     url = movie_label.select_one("li > div > div.pic > a")["href"]  # 获取电影链接(标签的href属性)     title_text = movie_label.select_one("li > div > div.info > div.hd > a").text  # 获取标题行内容     info_text = movie_label.select_one("li > div > div.info > div.bd > p:nth-child(1)").text  # 获取说明部分内容     rating_num = movie_label.select_one("li > div > div.info > div.bd > div > span.rating_num").text  # 获取评分     rating_people = movie_label.select_one("li > div > div.info > div.bd > div > span:nth-child(4)").text  # 获取评分人数     if quote_label := movie_label.select_one("li > div > div.info > div.bd > p.quote"):         quote = quote_label.text  # 获取评价     print(url, title_text, info_text, rating_num, rating_people, quote)

通过以上代码,我们成功地将各部分的数据都解析出来了,但是解析出的标题行内容、说明部分内容、评分、评分人数和评价中格式相对混乱,包括很多不需要的内容,需要进一步的清洗。

解析、清洗标题行:

 title_text = movie_label.select_one("li > div > div.info > div.hd > a").text.replace("", "")  # 提取标题行内容+清除换行符 title_chinese = clear_space_in_polar(title_text.split("/")[0])  # 提取中文标题+清除前后空格 title_other = [clear_space_in_polar(title) for title in title_text.split("/")[1:]]  # 提取其他标题+清除前后空格

解析导演信息(因长度原因,大部分主演名字不全暂不解析):

 info_text = movie_label.select_one("li > div > div.info > div.bd > p:nth-child(1)").text  # 获取说明部分内容 info_text = re.sub(" *", "", info_text)  # 清除行前多余的空格 info_text = re.sub("^", "", info_text)  # 清除开头的空行 info_line_1 = info_text.split("")[0]  # 获取第1行内容信息:包括导演和主演 info_line_2 = info_text.split("")[1]  # 获取第2行内容信息:包括年份、国家和类型 director = re.sub(" *(主演|主.{3}|.{3}).*$", "", info_line_1)  # 仅保留导演部分 year = int(re.search("[0-9]+", info_line_2.split("/")[0]).group())  # 提取电影年份并转换为数字 country = clear_polar_space(info_line_2.split("/")[1]) if len(info_line_2.split("/")) >= 2 else None  # 提取电影国家 classify = clear_polar_space(info_line_2.split("/")[2]) if len(info_line_2.split("/")) >= 3 else None  # 提取电影类型 classify = re.split(" +", classify)  # 将电影类型转换为list形式

解析评分、评分人数和评价:

 # 解析评分 rating_num = movie_label.select_one("li > div > div.info > div.bd > div > span.rating_num").text  # 提取评分 rating_num = float(re.search("[0-9.]+", rating_num).group())  # 将评分转换为浮点型数字 ​ # 解析评分人数 rating_people = movie_label.select_one("li > div > div.info > div.bd > div > span:nth-child(4)").text  # 提取评分人数 rating_people = int(re.search("[0-9]+", rating_people).group())  # 将评分人数转换为数字 ​ # 解析评价(该标签可能会不存在) if quote_label := movie_label.select_one("li > div > div.info > div.bd > p.quote"):     quote = quote_label.text.replace("", "")  # 提取评价+清除换行符 else:     quote = None

在解析的过程中,我们将每个电影解析的结果临时存储在循环前定义的movie_list列表中。

 movie_list.append({     "title": {         "chinese": title_chinese,         "others": title_other     },     "director": director,     "year": year,     "country": country,     "classify": classify,     "rating": {         "num": rating_num,         "people": rating_people     },     "quote": quote })

实现翻页

在完成了单页面的解析后,我们开始实现翻页。根据之前对页面的了解,我们只需要将页面的请求和解析嵌套到一个循环中即可。在每次循环中均需进行延迟,以免请求频率过高。实现代码如下:

 import time for page_num in range(10):     url = "https://movie.douban.com/top250?start={0}&filter=".format(page_num * 25)     response = requests.get(url, headers=headers)     ......     time.sleep(5)

存储数据

最后,我们将存储在临时变量movie_list列表中的数据存储到本地Json文件(若使用IDLE或PyCharm的Python Console模式,建议使用绝对路径)。

 import json with open("豆瓣TOP250电影.json", "w+", encoding="UTF-8") as file:     file.write(json.dumps({"data": movie_list}, ensure_ascii=False))

本系列案例采集的一切数据仅可用于学习、研究用途!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值