本篇讲介绍一个简单的Python爬虫案例–爬取豆瓣 TOP250 电影排行榜。 很多朋友在看一部电影前都喜欢先找一下网友们对该片的评价。
说到电影评分的网站,除了国外的 IMDB 和烂番茄,国内要数豆瓣最为出名。
主要原因是豆瓣有一套完整的评分和防水军机制 。
在这套机制下,豆瓣评分高的电影不一定是所有人都喜欢的,但是豆瓣评分低的电影,一定是实打实的烂片!
虽然每个人的喜好偏爱不同,但通常豆瓣评分 8 分以上的电影,都是值得一看的。
豆瓣还专门提供了一个 TOP250 的电影链接 -> https://movie.douban.com/top250
爬取的过程很好理解,这里只需要两个过程: ① 从服务器上下载所需页面 ② 解析这个页面,得到自己需要有用的内容
有的人可能会利用 urllib 模块实现网络抓取功能。但在 Python 中,有一个更好地替代者——Requests。Requests 简化了 urllib 的诸多冗杂且无意义的操作,并提供了更强大的功能。 所以在这里我们使用 Requests 模块的 get() 方法从服务器上来下载这个页面。
import requests
url = "https://movie.douban.com/top250"
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0"
}
res = requests.get(url,headers=headers)
res就是我们需要的这个页面的资源,我们不妨打开来看看是不是
with open("豆瓣电影.txt",'w',encoding='utf-8') as f:
f.write(res.text)
打开文本如下图
我们可以看出这确实是当前网页的资源,所以我们就抓取成功了。
解析网页内容推荐使用 BeautifulSoup 模块,它可以化腐朽为神奇,将一个复杂的网页结构转化为书籍目录的形式供你浏览。 例如,我们现在需要解析提取出当前页面的电影名字
import bs4
soup = bs4.BeautifulSoup(res.text,"html.parser")
targets = soup.find_all("div",class_="hd")
for each in targets:
print(each.a.span.text)
可以得到如下结果
肖申克的救赎 霸王别姬 阿甘正传 这个杀手不太冷 美丽人生 泰坦尼克号 千与千寻 辛德勒的名单 盗梦空间 忠犬八公的故事 海上钢琴师 机器人总动员 三傻大闹宝莱坞 楚门的世界 放牛班的春天 星际穿越 大话西游之大圣娶亲 熔炉 疯狂动物城 无间道 龙猫 教父 当幸福来敲门 怦然心动 触不可及
这里你可能就会有疑问,这些数据是怎么得来的呢? 我们先来看下 HTML 源代码:
发现每个电影的标题都是位于 <div class="hd">...</div>
标签中的,它的从属关系是:div -> a -> span
。
所以我们先调用 find_all() 方法,找到所有 class=”hd” 的 div 标签,然后按照从属关系即可直接取出电影名。 同理,我们借用此发方法来解析提取出电影的评分、介绍等需要的信息。
我们刚才解析提取的仅仅是第一页的页面,那么还有第二、第三、第四页……呢?
其实,解决起来也很简单,我们可以使用for循环来对每一页进行上述的两个过程。
但,我们此时又有新的问题,我们不可能每抓取一次,就重新输入下一网页的链接地址,这样很麻烦,效率也不高。
我们可以分析每一页的链接:
第一页:https://movie.douban.com/top250 第二页:https://movie.douban.com/top250?start=25 第三页:https://movie.douban.com/top250?start=50 第四页:https://movie.douban.com/top250?start=75 第五页:https://movie.douban.com/top250?start=100 … … …. …
我们可以发现这样的规律:
每一次的更新的 url = https://movie.douban.com/top250 + '/?start=' + str(25*i)
其中i
可以表示为页数-1
咦,这个时候,你可能会有疑问,我们怎么知道一共有多少页呢,不能一直for循环无穷吧。
那当然不可能的了,我们可以按第二步解析网页方式来获取页数
depth = soup.find('span',class_='next').previous_sibling.previous_sibling.text
注意,这个返回的depth
是给字符串形式,需要int()
这样结合刚才的过程,就可以迭代每一页了
感兴趣的话,可以试一试哦
import requests
import bs4
#抓取网页
def open_url(url):
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0"}
res = requests.get(url,headers=headers)
return res
#得到总页数
def find_depth(res):
soup = bs4.BeautifulSoup(res.text,'html.parser')
depth = soup.find('span',class_='next').previous_sibling.previous_sibling.text
return int(depth)
#解析网页,提取内容
def find_movies(res):
soup = bs4.BeautifulSoup(res.text,'html.parser')
names = []
target = soup.find_all('div',class_='hd')
for i in target:
names.append(i.a.span.text)
ranks = []
target = soup.find_all('span',class_='rating_num')
for i in target:
ranks.append(i.text)
messages = []
target = soup.find_all('div',class_='bd')
for i in target:
try:
messages.append(i.p.text.split('\n')[1].strip() + i.p.text.split('\n')[2].strip())
except:
continue
result = []
length = len(names)
for i in range(length):
result.append(names[i] + ',' +ranks[i] + ',' + messages[i] + '\n')
return result
def main():
host = "https://movie.douban.com/top250"
res = open_url(host)
depth = find_depth(res)
result = []
for i in range(depth):
url = host + '/?start=' + str(25*i)
res = open_url(url)
result.extend(find_movies(res))
with open("豆瓣TOP250电影.txt",'w',encoding='utf-8') as f:
for each in result:
f.write(each)
if __name__=="__main__":
main()
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3c2lknoq16w4k