目标:对常见手机应用商店评论进行爬取,对第三方数据网上的评论数据进行爬取
爬虫过程:获取评论数据→写入数据库
检查,然后刷新一下页面,选中network,再选中Fetch/XHR,发现有一个网页评论数据url(下图3)

&_t=1661237476289是时间戳,去掉后不影响返回结果;page=1是我们请求的评论页数,可以自行更换(后面爬虫就是对page进行遍历)&name=Outlook+Android_com.microsoft.office.outlook是应用的名字。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() #关闭数据库连接
selenium是一个web测试工具,可以模拟浏览器操作,如打开网页,点击元素等。
在使用selenium前需要安装浏览器版本对应的chromedriver,解压后把chromedriver.exe的路径加入用户变量Path中
&summer=经过测试可以删掉,不影响返回结果。这样我们就获得了需要的url。检查,发现该网站使用js动态加载数据,无法像静态网页一样通过url获取评论数据。element,用鼠标选中评论数据数据所在的位置,也就是左侧的表格,可以看到右侧有评论数据内容,我们就可以通过css选择器来定位到该数据:

By.CSS_SELECTOR来定位到我们需要的元素,css样式通过下图中copy selector来获取。
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')
参考链接:Python事件调度器定时任务sched
利用内置模块 sched 实现定时任务
sched 模块实现了一个通用事件调度器,在调度器类中使用一个延迟函数,等待特定的时间,执行任务。但该方法会阻塞线程,直到所有被调度的任务都执行完成。
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)
scheduler 对象主要方法:
enter(delay, priority, action, argument),延迟 delay 个时间单位安排一个事件。cancel(event):从队列中删除事件。如果事件不是当前队列中的事件,则该方法将抛出一个 ValueError。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
参考链接:
菜鸟教程-django
Django官方教程 (推荐)
pip install Django==x.x,安装自己需要的版本import django
print(django.get_version()) # 如果可用的话会打印出版本号,我这里是3.2.5
django-admin startproject mysite # mysite是项目名称。(这句是示例,后面图中我实际创建的项目名是django_app)

我们主要修改urls.py和settings.py文件
先设置好本地服务器环境,我使用的是phpstudy pro,启动mysql服务

使用上图中右上角的数据库工具——SQL_Front,新建一个数据库,数据库名为comments
修改Django项目settings.py中数据库部分:

上图中NAME是数据库名,USER是数据库用户名,PASSWORD是数据库密码
使用 python manage.py startapp database新建一个app,名字叫database,用于数据库操作。
进入database目录,修改models.py,如下图设置好数据表类:

然后修改settings.py的INSTALLED_APPS,添加上面新建的app,也就是database,如下图所示:

使用python manage.py makemigrations database和python manage.py migrate database对该数据表进行初始化,也就是创建这些表
makemigrations的官方解释如下:

migrate命令的官方解释如下:

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

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

get_360实现了以json格式返回Comments_360表中的所有数据
修改urls.py,也就是网站目录。

在终端输入python manage.py runserver回车,运行django项目,实现了在http://127.0.0.1:8000/360/中返回数据库中数据,如下图所示:

至此,后端部分完成。
参考链接:【Django】宝塔面板部署Django+MySQL项目实战 这篇文章写的很详细,可以直接去看这篇文章
部署前准备
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") # 生产模式
pip freeze > requirements.txtpython manage.py collectstatic # 必须调到生产模式才能成功登录腾讯云控制台,设置云服务器系统,我这里选择的系统是腾讯云专享版宝塔Linux面板

登录宝塔面板,软件商店 — 下载安装MySQL、Nginx、Python项目管理器;在Python项目管理器中安装Python 3.8


添加站点
上传本地项目至站点文件夹下
编辑uwsgi.ini文件
配置数据库,修改项目配置文件 settings.py 中 MySQL配置
Python项目管理器添加项目
配置网站的配置文件