赞
踩
所需第三方库:requests,re,time,pymongo,random
爬取网站需联网
网址: https://movie.douban.com/subject/26266893/reviews?start=0
网址: https://movie.douban.com/subject/26266893/reviews?start=20
由此分析,每页评论有20个,而网址的改变也只是在最后,网址的前半部分https://movie.douban.com/subject/26266893/reviews?start=都一样,而后面变的参数代表了第多少条评论,所以要爬取所以影评的动态网址的格式为:https://movie.douban.com/subject/26266893/reviews?start=加上20的倍数。
#前半部分固定的内容
url = r'https://movie.douban.com/subject/26266893/reviews?start='
page=0#表示评论页数
k = 0#表示评论条数
while page<1073:#检查网页可知流浪地球共有1073页影评
urls = url+str(k)#获得一个完整的网址
page = page+1
k = k+20#每一页有20条评论
动态网页网址已经分析清楚后就可以直接用requests库抓取网页代码
import requests
while page<1073:
urls = url+str(k)#拼接成完整的网址
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0'}#根据HTTP协议,设置请求头
response = requests.get(urls, headers=header)#获取源代码
print(response.text)#打印网页源代码
影评格式:
网页中需要提取的目标字段包括作者,评价星级,发布时间及影评内容,所以我采用的是正则匹配来获取
#将提取目标字段的功能封装成函数 import re def re_select(string): # 取出作者字段 pat_author = r'class="name">(.*?)</a>' re_au = re.compile(pat_author, re.S) auList = re_au.findall(string)#获取目标文档中的所有符合条件的字段 # 取出评价等级字段 pat_level = r'main-title-rating" title="(.*?)"></span>' re_level = re.compile(pat_level, re.S) levelList = re_level.findall(string) # 取出影评内容 pat_story = r'<div class="short-content">(.*?) ' re_story = re.compile(pat_story, re.S) storyList = re_story.findall(string) for i in range(len(storyList)):#将获取的字段进行格式调整 storyList[i] = storyList[i].strip()#去掉字段中的空白字符 storyList[i] = re.sub(r"[a-z\s\"\<\>\/\=\-]", "", storyList[i])#删除多余的字符 # 取出评价时间 pat_time = r'class="main-meta">(.*?)</span>' re_time = re.compile(pat_time, re.S) timeList = re_time.findall(string) #返回所有的目标字段列表 return auList,levelList,storyList,timeList
获取数据后可以选择保存为文件格式,也可以保存到本地数据库中。我选择的是保存到本地mongodb数据库中,也将其封装为函数:
#连接数据库
def linkmongo():
# 连接mongodb数据库
myclient = pymongo.MongoClient("mongodb://localhost:27017")
# 查看数据库名
dblist = myclient.list_database_names()
# 指定mydb数据库
mydb = myclient.mydb
# 指定mydb数据库里豆瓣集合
collection = mydb.douban
# 返回游标
return collection
#调用函数,只调用一次,不要加在后面的循环中
collection = linkmongo()
最后就是把所有功能整合就行了,但是还需要提高代码健壮性,本应该加一个异常处理,但是不想搞了,所有直接将出现异常的数据不要,直接请求下一页,总共几万条数据,少几条也没啥事,就这样吧!!!
总代码:
#author mjz #data 2019/12/4/16:19 import requests import re import time import pymongo import random url = r'https://movie.douban.com/subject/26266893/reviews?start=' #定义一个内核列表 u_a = [ 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0', 'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0'] def linkmongo(): # 连接mongodb数据库 myclient = pymongo.MongoClient("mongodb://localhost:27017") # 查看数据库名 dblist = myclient.list_database_names() # 指定mydb数据库 mydb = myclient.mydb # 指定mydb数据库里user集合 collection = mydb.douban return collection collection = linkmongo() def re_select(string): # 取出作者字段 pat_author = r'class="name">(.*?)</a>' re_au = re.compile(pat_author, re.S) auList = re_au.findall(string) # 取出评价等级字段 pat_level = r'main-title-rating" title="(.*?)"></span>' re_level = re.compile(pat_level, re.S) levelList = re_level.findall(string) # 取出影评内容 pat_story = r'<div class="short-content">(.*?) ' re_story = re.compile(pat_story, re.S) storyList = re_story.findall(string) for i in range(len(storyList)): storyList[i] = storyList[i].strip() storyList[i] = re.sub(r"[a-z\s\"\<\>\/\=\-]", "", storyList[i]) # 取出评价时间 pat_time = r'class="main-meta">(.*?)</span>' re_time = re.compile(pat_time, re.S) timeList = re_time.findall(string) return auList,levelList,storyList,timeList page=0 k = 0 while page<1073: urls = url+str(k) user = random.randint(0, 2) header = {'User-Agent': u_a[user]} response = requests.get(urls, headers=header) page = page + 1 #防止短时间内请求次数过多被网站封IP,所有爬一个页面后休息5秒,爬100个后休息一分钟 if page % 100 == 0: time.sleep(60) else: time.sleep(5) k = k + 20 if response.status_code == 200: print("成功爬取评论第%d页......" % page) auList, levelList, storyList, timeList = re_select(response.text) length = len(len(auList)) #如果提取的页面信息有缺失,则不要当前页面的信息,直接跳转到下一页面继续爬取 if(len(levelList)!=length or len(levelList)!=length or len(timeList)!=length): continue for i in range(len(auList)): di = {'作者': auList[i], '评级': levelList[i], '评论': storyList[i], '评论时间': timeList[i]} collection.insert(di) else: print("爬取失败.......")
因为这次用的是单线程,要爬完得很久,没有等到运行结束,但是也运行了两百多个页面,代码还是稳定的,应该没问题的。。。
运行截图:
所有的东西就是这样了,算是敏捷开发吧,只实现了功能,要修改也有许多地方可以修改,就当开源吧,希望有人能帮我完善哈。。。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。