Python爬虫实战 _豆瓣电影TOP250(2)爬取详细数据,保存为CSV文件【urllib、request、bs4、error、CSV】

Celeste ·
更新时间:2024-11-13
· 733 次阅读

爬取时间:2020-03-12 爬取难度:★★☆☆☆☆ 请求链接:https://movie.douban.com/top250 以及每部电影详情页,图片 爬取目标:爬取榜单上每一部电影详情页的数据,保存为 CSV 文件 涉及知识:request、urllib、bs4、CSV 和二进制数据储存、列表操作 爬取豆瓣Top250一、循环爬取网页模板二、解析与处理模块1、BeautifulSoup解析电影名称,评分信息和评论人数2、BeautifulSoup解析其他详细信息3、整合三、保存文本内容以及图片四、数据存储五、完整代码六、数据截图七、程序的不足之处 一、循环爬取网页模板

打开豆瓣电影top榜单,请求地址为:https://movie.douban.com/top250
通关观察,我们可以发现每页展示25条电影信息,多次翻页我们可以观察到url的变化:
第一页:https://movie.douban.com/top250
第二页:https://movie.douban.com/top250?start=25&filter=
第三页:https://movie.douban.com/top250?start=50&filter=
通过以上我们可以看到每一页的“start= ”后面的数字跟随每一页的具体数值而改变。 电影总共有250部,以此类推,我们可以知道共10页。那么这10页要如何跳转呢?我么可以看下面的代码:

url = 'https://movie.douban.com/top250?start=%d&filter=' ...... # 页面的跳转 def start_download(self): while self.dstart < self.dtotal: durl = self.durl%self.dstart # print(durl) self.load_page(durl) self.dstart += self.dstep break # 如果爬取全部可以直接放开

根据上面的url链接,再通过下面的自定义函数,实现页面跳转的功能。

二、解析与处理模块

再定义解析函数之前,我们需要添加一个并定义一个报错函数:

from urllib import error def req_page(self,url): # 请求异常处理 pass

详细看下这个函数req_page(),首先我们打开网页,如果出现错误,会打印出来,好让你可以根据错误修改程序,如果正常,就会跳转至下面的自定义功能函数:

def req_page(self,url): try: req=urllib.request.Request(url=url,headers=headers) req = urlopen(req) except error.HTTPError as e: print('catch e:',e) return None except: print('url request error:',url) return None if req.code!=200: return pageinfo = req.read().decode('utf-8') return pageinfo 1、BeautifulSoup解析电影名称,评分信息和评论人数

首先我们需要再网页中查看电影名称,评分信息和评论人数等信息:
①电影名称
电影名称
通过上面的图片我们知道,爬取的内容很简单,只需爬取span标签下的title就行了,代码如下:

listdiv = obj.find_all('div',class_='hd') for div in listdiv: # print(div) murl = div.find('a').get('href') mname = div.find('span',class_='title').get_text() print(mname)

②评分信息
评分信息
分析页面我们知道评分在9.7这个标签内,我们只需查找标签为property="v:average"就可以了,代码如下:

mscore = obj.find('div',class_="rating_self clearfix") score = mscore.find(property="v:average").get_text()

③评论人数
评论人数
分析页面我们知道评分在1921019这个标签内,我们只需查找标签为property="v:votes"就可以了,代码如下:

votes = mscore.find(property="v:votes").get_text() 2、BeautifulSoup解析其他详细信息

详细信息
通过上面的网页分析,我们可以发现,这些详细信息都存放在

中,下面我们要做的就是将这些文字提取出来:

def parse_minfo(self,url,mname): pinfo = self.req_page(url) if not pinfo: return obj = BeautifulSoup(pinfo,'html5lib') minfo = obj.find('div',id='info') tinfo = minfo.get_text() 3、整合

上面的工作做完以后,我们需要把爬取的列表切割成字典,代码如下:

# 把列表切割成字典 def parse_text(self,minfo): # listt = minfo.split('\n') # 切分 listt = [item.strip()for item in minfo.split('\n') if item.strip(' ')] # # print(listt) # 此种打印有点问题 listt = [item.split(':') for item in listt] listt = [items for items in listt if len(items) == 2 and items[0].strip() and items[1].strip()] print(listt) dinfo = dict(listt) return dinfo

这是我们就可以把这部分的代码完整实现:

def parse_minfo(self,url,mname): pinfo = self.req_page(url) if not pinfo: return obj = BeautifulSoup(pinfo,'html5lib') minfo = obj.find('div',id='info') tinfo = minfo.get_text() dinfo = self.parse_text(tinfo) mscore = obj.find('div',class_="rating_self clearfix") score = mscore.find(property="v:average").get_text() votes = mscore.find(property="v:votes").get_text() dinfo['评分'] = score dinfo['评论人数'] = votes dinfo['片名'] = mname print(dinfo.keys()) for item in dinfo.items(): print(item) 三、保存文本内容以及图片

如果完成上面的操作,这时候我们就需要对他们进行保存了。保存之前我们还要对这些进行一些规范与调试,这些暂时不做细讲,直接上代码:

# 保存文本内容 def load_page(self,url): pinfo = self.req_page(url) if not pinfo: return obj = BeautifulSoup(pinfo,'html5lib') listdiv = obj.find_all('div',class_='hd') for div in listdiv: # print(div) murl = div.find('a').get('href') mname = div.find('span',class_='title').get_text() print(murl,mname) minfo = self.parse_minfo(murl,mname) if minfo: keys = [ '片名','导演','编剧', '主演', '类型', '制片国家/地区', '语言', '上映日期', '片长', '又名', '评分', '评论人数'] self.infohd.write(keys,minfo) break # 保存图片 def load_img(self,info): print("callhere load img:",info) req=urllib.request.Request(url=info[1],headers=headers) imgreq = urlopen(req) img_c = imgreq.read() path = r'D:\\test\\'+ info[0]+'.jpg' print('path:', path) imgf = open(path,'wb') imgf.write(img_c) imgf.close() 四、数据存储

数据存储一般情况下,单独写在一起比较好,这是我们创建一个minfo_save的文件,并定义一个 csvHandler的类型:

import csv class csvHandler(object): def __init__(): pass def write(): pass def close(): pass

在这个函数中,我们我们指定编码类型,以及key和所爬取内容的对应关系。
代码如下:

def __init__(self,path): print('path:',path) self.f = open(path,'w',encoding='utf-8') self.fw = csv.writer(self.f) self.head = False def write(self,keys,info): # print(keys) rowinfo = [info.get(key, ' ') for key in keys] print(rowinfo) if self.head: self.fw.writerow(rowinfo) else: self.fw.writerow(keys) self.fw.writerow(rowinfo) self.head = True def close(self): self.f.close() 五、完整代码

①minfo_spider

# ============================================= # --*-- coding: utf-8 --*-- # @Time : 2020-03-15 # @Author : 李华鑫 # @CSDN : https://blog.csdn.net/qq_16146103 # @FileName: douban250.py # @Software: PyCharm # ============================================= from urllib.request import urlopen import urllib from bs4 import BeautifulSoup from urllib import error from minfo_save import csvHandler headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36', 'Cookie':'bid=wjbgW95-3Po; douban-fav-remind=1; __gads=ID=f44317af32574b60:T=1563323120:S=ALNI_Mb4JL8QlSQPmt0MdlZqPmwzWxVvnw; __yadk_uid=hwbnNUvhSSk1g7uvfCrKmCPDbPTclx9b; ll="108288"; _vwo_uuid_v2=D5473510F988F78E248AD90E6B29E476A|f4279380144650467e3ec3c0f649921e; trc_cookie_storage=taboola%2520global%253Auser-id%3Dff1b4d9b-cc03-4cbd-bd8e-1f56bb076864-tuct427f071; viewed="26437066"; gr_user_id=7281cfee-c4d0-4c28-b233-5fc175fee92a; dbcl2="158217797:78albFFVRw4"; ck=4CNe; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1583798461%2C%22https%3A%2F%2Faccounts.douban.com%2Fpassport%2Flogin%3Fredir%3Dhttps%253A%252F%252Fmovie.douban.com%252Ftop250%22%5D; _pk_ses.100001.4cf6=*; __utma=30149280.1583974348.1563323123.1572242065.1583798461.8; __utmb=30149280.0.10.1583798461; __utmc=30149280; __utmz=30149280.1583798461.8.7.utmcsr=accounts.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/passport/login; __utma=223695111.424744929.1563344208.1572242065.1583798461.4; __utmb=223695111.0.10.1583798461; __utmc=223695111; __utmz=223695111.1583798461.4.4.utmcsr=accounts.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/passport/login; push_noty_num=0; push_doumail_num=0; _pk_id.100001.4cf6=06303e97d36c6c15.1563344208.4.1583798687.1572242284.'} url = 'https://movie.douban.com/top250?start=%d&filter=' class spider_douban250(object): # 页面初始化 def __init__(self,url = None, start = 0, step = 25 , total = 250,savehd=None): self.durl = url self.dstart = start self.dstep =step self.dtotal = total self.infohd = savehd # 页面的跳转 def start_download(self): while self.dstart < self.dtotal: durl = self.durl%self.dstart # print(durl) self.load_page(durl) self.dstart += self.dstep break # 如果爬取全部可以直接放开 def req_page(self,url): # 请求异常处理 try: req=urllib.request.Request(url=url,headers=headers) req = urlopen(req) except error.HTTPError as e: print('catch e:',e) return None except: print('url request error:',url) return None if req.code!=200: return pageinfo = req.read().decode('utf-8') return pageinfo # 把列表切割成字典 def parse_text(self,minfo): # listt = minfo.split('\n') # 切分 listt = [item.strip()for item in minfo.split('\n') if item.strip(' ')] # # print(listt) # 此种打印有点问题 listt = [item.split(':') for item in listt] listt = [items for items in listt if len(items) == 2 and items[0].strip() and items[1].strip()] print(listt) dinfo = dict(listt) return dinfo # 对返回值进行处理 def parse_minfo(self,url,mname): pinfo = self.req_page(url) if not pinfo: return obj = BeautifulSoup(pinfo,'html5lib') minfo = obj.find('div',id='info') tinfo = minfo.get_text() dinfo = self.parse_text(tinfo) mscore = obj.find('div',class_="rating_self clearfix") score = mscore.find(property="v:average").get_text() votes = mscore.find(property="v:votes").get_text() dinfo['评分'] = score dinfo['评论人数'] = votes dinfo['片名'] = mname print(dinfo.keys()) for item in dinfo.items(): print(item) return dinfo # 保存文本内容 def load_page(self,url): pinfo = self.req_page(url) if not pinfo: return obj = BeautifulSoup(pinfo,'html5lib') listdiv = obj.find_all('div',class_='hd') for div in listdiv: # print(div) murl = div.find('a').get('href') mname = div.find('span',class_='title').get_text() print(murl,mname) minfo = self.parse_minfo(murl,mname) if minfo: keys = [ '片名','导演','编剧', '主演', '类型', '制片国家/地区', '语言', '上映日期', '片长', '又名', '评分', '评论人数'] self.infohd.write(keys,minfo) break # 保存图片 def load_img(self,info): print("callhere load img:",info) req=urllib.request.Request(url=info[1],headers=headers) imgreq = urlopen(req) img_c = imgreq.read() path = r'D:\\test\\'+ info[0]+'.jpg' print('path:', path) imgf = open(path,'wb') imgf.write(img_c) imgf.close() fcsv = csvHandler('minfo.csv') spider = spider_douban250(url,start=0,step=25,total=25,savehd=fcsv) spider.start_download() fcsv.close()

②minfo_save

# ============================================= # --*-- coding: utf-8 --*-- # @Time : 2020-03-15 # @Author : 李华鑫 # @CSDN : https://blog.csdn.net/qq_16146103 # @FileName: douban250.py # @Software: PyCharm # ============================================= import csv class csvHandler(object): def __init__(self,path): print('path:',path) self.f = open(path,'w',encoding='utf-8') self.fw = csv.writer(self.f) self.head = False def write(self,keys,info): # print(keys) rowinfo = [info.get(key, ' ') for key in keys] print(rowinfo) if self.head: self.fw.writerow(rowinfo) else: self.fw.writerow(keys) self.fw.writerow(rowinfo) self.head = True def close(self): self.f.close() if __name__ == '__main__': fcsv = csvHandler('minfo.csv') fcsv.write([],[]) fcsv.close() 六、数据截图

爬取内容
爬取内容

七、程序的不足之处

程序不足的地方:豆瓣电影有反爬机制,由于没有添加时间间隔,以及IP代理池没有构建以及多线程的使用,在爬取一百多条数据的时候,IP会被封禁,第二天才会解封。如果有能力的可以添加多个User—Agent、添加时间间隔以及使用多个代理IP进行完善代码。
除此之外,由于此代码没有用较为常用的requests库,可以考虑使用此库。


作者:不温卜火



top250 Python爬虫实战 csv文件 实战 数据 urllib request 豆瓣 error python爬虫 csv top Python

需要 登录 后方可回复, 如果你还没有账号请 注册新账号