【项目整理】安卓应用商店评论监控平台

目标:对常见手机应用商店评论进行爬取,对第三方数据网上的评论数据进行爬取

1. 爬虫

爬虫过程:获取评论数据→写入数据库

1.1 静态网页爬虫:requests库
以某应用商店的outlook页面为例:
  1. 在打开的应用页面右键点击检查,然后刷新一下页面,选中network,再选中Fetch/XHR,发现有一个网页评论数据url(下图3)
    在这里插入图片描述
  2. 复制该url在新的页面打开,发现就是我们需要的评论数据,只是被编码过(没关系)
    在这里插入图片描述
  3. 继续分析该url:http://app.so.com/message/index?page=1&requestType=ajax&_t=1661237476289&name=Outlook+Android_com.microsoft.office.outlook,经过测试发现:
    &_t=1661237476289是时间戳,去掉后不影响返回结果;
    page=1是我们请求的评论页数,可以自行更换(后面爬虫就是对page进行遍历)
    &name=Outlook+Android_com.microsoft.office.outlook是应用的名字。
  4. 然后我们就可以使用requests.get(url)方法,获取该url返回的评论数据,利用json.loads(res.text)方法读取返回的文字内容。
    import requests
    import json
    
    headers = {
      'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
    }
    res = requests.get('http://app.so.com/message/index?page=1&requestType=ajax&_t=1661237476289&name=Outlook+Android_com.microsoft.office.outlook', headers=headers)
    # 加载评论内容
    json_str = json.loads(res.text) 
    

完整代码如下

import requests
import json
import pymysql

headers = {
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}
# 要爬取的应用url
appname = {
    'Outlook':'Outlook+Android_com.microsoft.office.outlook',
    'Office':'Office+Mobile+for+Office+365+Android_com.microsoft.office.officehub',
}

 # 连接数据库
 db = pymysql.connect(
 host="127.0.0.1", # 本机地址
 port=3306, # 默认端口
 user='root',    #在这里输入用户名
 password='root',     #在这里输入密码
 database='comments', # 数据库名
 charset='utf8mb4'
 )
 cursor = db.cursor() #创建游标对象,用于数据库操作

 for k,v in appname.items():# 遍历应用列表
     for i in range(100): # 爬取前100页
         # 获取请求网页内容
         res = requests.get('http://app.so.com/message/index?page='+str(i)+'&requestType=ajax&name='+str(v), headers=headers)
         # 如果获取页面为[],表明无内容
         if res.content == b'\n\n\n[]':
             break
         # 加载评论内容
         json_str = json.loads(res.text) 
         for item in json_str:
             # 通过msgid判断是否在数据库已存在此条数据
             querysql = "select msgid from database_comments_360 where msgid="+pymysql.converters.escape_string(str(item.get('msgid')))
             if(cursor.execute(querysql)==0):# execute(querysql)返回的是操作影响的行数
                 sql = 'insert into database_comments_360(date,user,content,score,version,name, msgid) values(%s,%s,%s,%s,%s,%s,%s);'

                 date = pymysql.converters.escape_string(item.get('create_time')) # 获取数据
                 user = pymysql.converters.escape_string(item.get('username'))
                 content = pymysql.converters.escape_string(item.get('content'))
                 score = pymysql.converters.escape_string(str(item.get('score')))
                 version = pymysql.converters.escape_string(str(item.get('version_name')))
                 msgid = pymysql.converters.escape_string(str(item.get('msgid')))
                 name = pymysql.converters.escape_string(str(k))

                 data = [date,user,content,score,version,name,msgid]
                 cursor.execute(sql,data)     # 插入数据
                 db.commit() # 提交命令 (如果不提交,不会进行数据库操作)

     print('爬取'+str(k)+'应用评论'+str(i)+'页')
 cursor.close() 
 db.close()  #关闭数据库连接

1.2 动态网页爬虫:selenium库

selenium是一个web测试工具,可以模拟浏览器操作,如打开网页,点击元素等。

在使用selenium前需要安装浏览器版本对应的chromedriver,解压后把chromedriver.exe的路径加入用户变量Path中

  1. 对某第三方网站的评论页面url进行分析:
    某发烧厂商店edge评论:https://app.diandian.com/app/6njqcmu6xj0rip4/android-review?system=4&market=3&summer=
    某蓝厂商店edge评论:https://app.diandian.com/app/rn6vtpukqjzvhw4/android-review?system=4&market=4&summer=
    发现只有两部分不一样,分别是应用名字和market,而&summer=经过测试可以删掉,不影响返回结果。这样我们就获得了需要的url。
  2. 在页面点击右键,选中检查,发现该网站使用js动态加载数据,无法像静态网页一样通过url获取评论数据。
  3. 点击element,用鼠标选中评论数据数据所在的位置,也就是左侧的表格,可以看到右侧有评论数据内容,我们就可以通过css选择器来定位到该数据:
    在这里插入图片描述
  4. 同样也可以定位到用户评分和用户名等内容,如下图所示:
    在这里插入图片描述
  5. 我们可以使用selenium中By.CSS_SELECTOR来定位到我们需要的元素,css样式通过下图中copy selector来获取。
  6. 分析完成后,使用selenium中的webdriver模拟对浏览器进行操作
    from selenium import webdriver # 模拟浏览器操作
    from selenium.webdriver.common.by import By # 后面用By里面的css选择器定位网页元素
    
    driver = webdriver.Chrome()# 初始化一个浏览器对象
    driver.get("https://app.diandian.com/app/4q3uzuzpe5x8es7/ios-review?system=4") # 打开网页
    
    table=driver.find_element(By.CSS_SELECTOR,'#commentContent > div.loading-wrap > div > div > div > table')#使用css定位网页表格位置
    #获取表格包含的行,并将行数赋值
    time.sleep(2)
    table_rows=table.find_elements(By.CSS_SELECTOR,'#commentContent > div.loading-wrap > div > div > div > table > tbody > tr')# table包含行数的集合,包含标题                                        
    print('table_rows',table_rows)
    
    vrows=len(table_rows)#将总行数赋给变量vrows,后面对每一行进行遍历
    print('vrows',vrows)
    
    # 对表格进行按行遍历
    for table_num in range(1, vrows):
        time.sleep(2)
    	 # 爬取评论内容
        content = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > table > tbody > tr:nth-child("+str(table_num)+") > td:nth-child(2) > div > div > div.dd-word-wrap > div > p > span").text
       
    	 # 爬取评论分数
    	 time.sleep(2)
    	 score = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > table > tbody > tr:nth-child("+str(table_num)+") > td:nth-child(1) > div.dd-table-cell > div > div").get_attribute("aria-valuenow")
    

完整代码如下:

# diandian.py 文件
from selenium import webdriver # 模拟浏览器操作
from selenium.webdriver.common.by import By # 后面用By里面的css选择器定位网页元素
import pymysql # 对MySQL数据库进行连接、插入数据等操作

def myscrapy(urls,market,appname): # 这里封装了函数,需要传入三个参数,分别是应用的url,market哪个应用市场,appname是数据库表名,指示要写入哪个数据库表
    driver = webdriver.Chrome()# 初始化一个浏览器对象

    # 连接数据库
    db = pymysql.connect(
    host="127.0.0.1", 
    port=3306,
    user='root',    #在这里输入用户名
    password='root',     #在这里输入密码
    database='comments', #数据库名
    charset='utf8mb4'
    )
    cursor = db.cursor() #创建游标对象

    # 删除表中数据 (非必需模块,每次爬取都先删除数据库中已有部分,重新爬取)
    sql='truncate table database_comments_'+str(appname)+';'
    cursor.execute(sql)   
    db.commit()

    for k,v in urls.items():# 遍历要爬取的应用urls
        driver.get("https://app.diandian.com/app/"+str(v)+"/android-review?market="+str(market)+"&summer=") # 打开网页
        # 爬前10页
        for i in range(10):
            print("正在爬取第",i,"页")
            # 点击下一页
            if (i!=0): # 在第一页之后的页面,爬取之前需要点击当前页面的‘下一页’按钮,刷新后再进行爬取
                try:#try处理部分应用只有一页评论,没有nextbutton按钮
                    next_button = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > div > div > button.btn-next")
                    disable = next_button.get_attribute("disabled")
                    # 如果下一页按钮可用再点击,否则跳出此次循环
                    if(disable!='true'):
                        next_button.click()
                        print("next page")
                    else: 
                        print("没有点击下一页")
                        break
                except:
                    break
            # 开始爬表格数据 sleep是为了防止刷新过快,页面元素失效无法获取
            time.sleep(2) 
            table=driver.find_element(By.CSS_SELECTOR,'#commentContent > div.loading-wrap > div > div > div > table')#使用css定位网页表格位置
            #获取表格包含的行,并将行数赋值
            time.sleep(2)
            table_rows=table.find_elements(By.CSS_SELECTOR,'#commentContent > div.loading-wrap > div > div > div > table > tbody > tr')# table包含行数的集合,包含标题                                        
            print('table_rows',table_rows)

            vrows=len(table_rows)#将总行数赋给变量vrows,后面进行循环
            print('vrows',vrows)
            for table_num in range(1, vrows):
                time.sleep(2)
                # 爬取评论内容
                try:
                    content = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > table > tbody > tr:nth-child("+str(table_num)+") > td:nth-child(2) > div > div > div.dd-word-wrap > div > p > span").text
                    print('content:',content)                             
                except:
                    continue  
                # 爬取评论分数
                time.sleep(2)
                score = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > table > tbody > tr:nth-child("+str(table_num)+") > td:nth-child(1) > div.dd-table-cell > div > div").get_attribute("aria-valuenow")
                print('score:',score)                            
                # 爬取评论时间
                time.sleep(2)
                date = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > table > tbody > tr:nth-child("+str(table_num)+") > td:nth-child(3) > div > div").text
                print('date:',date)
                # 爬取评论应用版本
                try:
                    version = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > table > tbody > tr:nth-child("+str(table_num)+") > td:nth-child(2) > div > div > div.comment-info.dd-flex.dd-flex-warp > span:nth-child(6)").text
                    print('version:',version)
                except:
                    version = " "
                    print("not found version!")

                time.sleep(2)
                try:
                    user = driver.find_element(by=By.CSS_SELECTOR, value="#commentContent > div.loading-wrap > div > div > div > table > tbody > tr:nth-child("+str(table_num)+") > td:nth-child(2) > div > div > div.comment-info.dd-flex.dd-flex-warp > div > a").text
                    print('user:',user)
                except:
                    user = "anoy"
                    print("not found user!")
                
                time.sleep(2)
                name = driver.find_element(by=By.CSS_SELECTOR, value=" #appinfo-content > div.container > div:nth-child(2) > div.content-side > div.container-head > div.dd-flex-1.dd-flex.dd-flex-column.dd-flex-space.dd-overflow-hidden > div.logo-wrap > div.max-width-80 > div.app-name > h1").text
                print('name:',name)

                data = [date,user,content,score,version,name]
                print(data)

                sql = 'insert into database_comments_'+str(appname) +'(date,user,content,score,version,name) values(%s,%s,%s,%s,%s,%s);'
                cursor.execute(sql,data)     # 插入数据
                db.commit() # 提交sql语句

    cursor.close() 
    db.close()  # 关闭数据库连接
    driver.quit() # 退出浏览器

下面是调用部分,以某第三方数据网的vivo商店评论为例:

# all.py文件
import diandian
# 爬取第三方数据网蓝厂商店的下面几个应用评论数据
vivo_urls={
    'Outlook':'jn79t9ueolpbqy3',
    'Office':'3403tku16vp5t14',
    'Edge':'rn6vtpukqjzvhw4',
    
}

diandian.myscrapy(vivo_urls,4,'vivo')

2. 定时任务 sched库

参考链接:Python事件调度器定时任务sched

  1. 利用内置模块 sched 实现定时任务
    sched 模块实现了一个通用事件调度器,在调度器类中使用一个延迟函数,等待特定的时间,执行任务。但该方法会阻塞线程,直到所有被调度的任务都执行完成。

  2. class sched.scheduler(timefunc, delayfunc) 这个类定义了调度事件的通用接口,需要外部传入两个参数,
    timefunc:无参数,返回时间戳的函数。常用的有time模块里面的 time。
    delayfunc :需要一个参数,与timefunc的输出兼容,常用的有time模块的 sleep。

    '''
    初始化scheduler类对象
    time.time 返回时间戳
    time.sleep 在定时未到达之前阻塞
    用time模块的这两个函数来实例化scheduler对象
    '''
    schedule = sched.scheduler(time.time, time.sleep)
    
  3. scheduler 对象主要方法:

    1. enter(delay, priority, action, argument),延迟 delay 个时间单位安排一个事件。
    2. cancel(event):从队列中删除事件。如果事件不是当前队列中的事件,则该方法将抛出一个 ValueError。
    3. run():运行所有预定的事件。这个函数将等待(使用delayfunc() 函数),然后执行事件,直到不再有预定的事件。

代码示例:

import sched # 定时任务模块
import time # 时间模块
import datetime # 日期时间模块

s = sched.scheduler(time.time, time.sleep) # 实例化scheduler对象

def print_time(a='default'): # 要调用的函数
    print("From print_time", time.time(), a)

def print_some_times():
    print(time.time())
    s.enter(10, 1, print_time) # 加入队列
    s.enter(5, 2, print_time, argument=('positional',))# 加入队列
    s.enter(5, 1, print_time, kwargs={'a': 'keyword'})# 加入队列
    print(s.queue)
    s.run()
    print(time.time())

print_some_times()
# 1607676900.9483116
# From print_time 1607676905.9483757 keyword
# From print_time 1607676905.9483757 positional
# From print_time 1607676910.9485233 default
# 1607676910.9485233

3. django后端框架 版本3.2.5

参考链接:
菜鸟教程-django
Django官方教程 (推荐)

3.1 安装Django
  1. 使用pip install Django==x.x,安装自己需要的版本
  2. 测试是否可用:
    import django
    print(django.get_version()) # 如果可用的话会打印出版本号,我这里是3.2.5
    
  3. 创建Django项目
    django-admin startproject mysite # mysite是项目名称。(这句是示例,后面图中我实际创建的项目名是django_app)
    

在这里插入图片描述
我们主要修改urls.pysettings.py文件

3.2 数据库
  1. 先设置好本地服务器环境,我使用的是phpstudy pro,启动mysql服务
    在这里插入图片描述
    使用上图中右上角的数据库工具——SQL_Front,新建一个数据库,数据库名为comments

  2. 修改Django项目settings.py中数据库部分:
    在这里插入图片描述
    上图中NAME是数据库名,USER是数据库用户名,PASSWORD是数据库密码

  3. 使用 python manage.py startapp database新建一个app,名字叫database,用于数据库操作。
    进入database目录,修改models.py,如下图设置好数据表类:
    在这里插入图片描述

  4. 然后修改settings.pyINSTALLED_APPS,添加上面新建的app,也就是database,如下图所示:
    在这里插入图片描述

  5. 使用python manage.py makemigrations databasepython manage.py migrate database对该数据表进行初始化,也就是创建这些表

    1. makemigrations的官方解释如下:
      在这里插入图片描述

    2. migrate命令的官方解释如下:
      在这里插入图片描述

    3. 运行上面两个命令之后,使用phpstudy pro的SQL_Front查看数据库,可以看到已经创建了我们刚才在model.py里定义的数据表
      在这里插入图片描述

    总之,模型更改的三步指南:
    1. 在models.py中更改模型。
    2. 运行python manage.py makemigrations app名字,存储这些更改。
    3. 运行python manage.py migrate,将这些更改应用到数据库。

  6. 修改views.py,编写返回数据表内容的函数
    urls
    get_360实现了以json格式返回Comments_360表中的所有数据

  7. 修改urls.py,也就是网站目录。
    在这里插入图片描述
    在终端输入python manage.py runserver回车,运行django项目,实现了在http://127.0.0.1:8000/360/中返回数据库中数据,如下图所示:
    在这里插入图片描述

至此,后端部分完成。


4. 将后端部署到腾讯云和阿里云服务器上,进行生产环境部署

参考链接:【Django】宝塔面板部署Django+MySQL项目实战 这篇文章写的很详细,可以直接去看这篇文章

  1. 部署前准备

    1. 调开发模式为生产模式
      Django配置文件settings.py修改,调开发模式为生产模式:
      DEBUG = False  # 开发模式为True,上线时设置为False
      ALLOWED_HOSTS = ['8.130.98.214']  # 开发模式为*,实际生产为真实IP或域名
      STATIC_URL = '/static/' #上线模式
      # STATICFILES_DIRS = [
      #     os.path.join(BASE_DIR, "static"),    # 开发模式
      # ]
      STATIC_ROOT = os.path.join(BASE_DIR, "static")    # 生产模式
      
    2. 导出项目依赖包
      pip freeze > requirements.txt
    3. 收集静态文件
      python manage.py collectstatic # 必须调到生产模式才能成功
      其中带有后台的项目必须要收集静态文件,部署时才能显示后台。
  2. 登录腾讯云控制台,设置云服务器系统,我这里选择的系统是腾讯云专享版宝塔Linux面板
    在这里插入图片描述

  3. 登录宝塔面板,软件商店 — 下载安装MySQL、Nginx、Python项目管理器;在Python项目管理器中安装Python 3.8
    在这里插入图片描述
    在这里插入图片描述

  4. 添加站点

  5. 上传本地项目至站点文件夹下

  6. 编辑uwsgi.ini文件

  7. 配置数据库,修改项目配置文件 settings.py 中 MySQL配置

  8. Python项目管理器添加项目

  9. 配置网站的配置文件

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岁月漫长_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值