python爬取豆瓣影评

爬取准备

所需第三方库: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">(.*?) &nbsp;'
    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">(.*?) &nbsp;'
    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("爬取失败.......")


因为这次用的是单线程,要爬完得很久,没有等到运行结束,但是也运行了两百多个页面,代码还是稳定的,应该没问题的。。。

运行截图:

在这里插入图片描述所有的东西就是这样了,算是敏捷开发吧,只实现了功能,要修改也有许多地方可以修改,就当开源吧,希望有人能帮我完善哈。。。

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值