项目的结构:

项目的结构一定要熟悉

  • HMSol 是和项目同名的文件夹,它里面有setting和总的路由
  • commonhomeworklessonmyadminstudentteacher 使我们创建的app,看到上面有一个小点了不,表示他是Python Package,不是一般的文件夹
  • static 文件夹存放一些资源,比如我们用到的框架的代码,图片之类的
  • media 存放用户上传的文件,比如课程资料,作业等等
  • templates 存放模板的文件夹

重点:

记得MVT不?在每个app里面都能体现

  • M:模型层,models.py,存放我们定义的数据库
  • V:视图层,views.py,存放我们定义的具体实现功能的函数
  • T:模板层,就是位于 templates 文件夹里面的html文件
  • 路由:urls.py,就是沟通V和T的桥梁

Ajax的部分

Ajax是什么?标准的Ajax是什么样的

后端如何获取前端传来的数据?

如果前端的ajax是 post 请求:

// 提交作业
$.ajax({url: '/homework/send-homework',   // 路由type: 'POST',    // 类型contentType: "application/json",async: false,data: JSON.stringify({要传到后端的数据,键值对,例如'id': '001'}),success: function (data) {成功之后要做的事},error: function (data) {失败之后要做的事}
});

后端接收:

# 获取前端传的参数 data的形式{'id': '001'}
data = json.loads(request.body)my_id = data['id']

如果前端的ajax是 get 请求:

$.ajax({url: '/get-user-info',type: 'get',contentType: "application/json",async: false,data: JSON.stringify({要传到后端的数据,键值对,例如'id': '001'}),success: function (user_data) {成功后要做的事},error: function (ret) {失败后做的事}
});

后端接收:

# 获取前端传的参数 data的形式{'id': '001'}
my_id = request.GET.get('id', '')

从session中获取数据

id = request.session.get('id')

定时发送邮件功能:

定时发送邮件的功能位于 homeworkviews.py
使用的模块是 apscheduler (我读作ap司改就)

准备的部分:(了解即可)

安装好 django-apscheduler 后,在 setting.py 中添加:

INSTALLED_APPS = (# ..."django_apscheduler",
)

然后通过迁移命令(还记得吗,数据库迁移的两条命令:python manage.py makemigrationspython manage.py migrate
就会在数据库中生成两张表:(你可以点进去看看是什么子)

  • django_apscheduler_djangojob 表保存注册的任务以及下次执行的时间
  • django_apscheduler_djangojobexecution 保存每次任务执行的时间和结果和任务状态

具体用到的部分:(重要)


# 定时任务相关的包
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore, register_job'''
开启定时任务,实现自动发送邮件提醒
'''# 1.实例化调度器
scheduler = BackgroundScheduler()# 2.调度器使用DjangoJobStore()
scheduler.add_jobstore(DjangoJobStore(), "default")
try:# 3.设置定时任务# 另一种方式为每天固定时间执行任务,对应代码为@register_job(scheduler,  "cron", id='test2', hour=0)# @register_job(scheduler, "interval", id='test1', minutes=1)def my_job():# 发送邮件提醒send_email()# 4.注册定时任务(0.4.0版本之后不需要注册)# register_events(scheduler)# 5.开启定时任务scheduler.start()
except Exception as e:print(e)# 有错误就停止定时器# scheduler.shutdown()# 获取应当被提醒的学生的邮箱列表
def send_email():today = datetime.date.today()print('今天的日期是:', today)# 替换用字典var = {'ENDDATE': "AND d.end_date = '" + str(today + datetime.timedelta(days=1)) + " 00:00:00'",}sql_raw = '''SELECTa.id,a.name,b.email,c.name AS lesson_name,d.name AS homework_name,d.end_dateFROMstudent aLEFT JOIN user b ON a.id = b.id LEFT JOIN lesson c ON a.class_id = c.classesLEFT JOIN homework d ON c.id = d.lesson_idWHERE1=1[ENDDATE]'''sql = sql_fmt(sql_raw, var)print(sql)res = do_sql(sql)print(res)for r in res:if r is not None:send_email1(r)print('给', r['name'], '发送邮件提醒成功!')

用到了我们写在 common 下的 APIsend_email.py 里的函数:
发送邮件的功能是由django提供的(你看那个 django.core.mail ),因此我们只需要准备好需要的东西(数据),按照格式填进去就好了。
发送邮件需要QQ邮箱的SMTP服务,毕竟是Django以你的身份(用你的QQ邮箱)给别人发邮件对不对,所以就需要这个密码。

from django.core.mail import get_connection, send_maildef send_email1(info_dict):print('发送邮件提醒')password = 'XXXXXXXXXX'conn = get_connection(host='smtp.qq.com', username='XXXXXXXXX@qq.com', password=password)student_name = info_dict['name']email = info_dict['email']lesson_name = info_dict['lesson_name']homework_name = info_dict['homework_name']end_date = str(info_dict['end_date'])msg = student_name + ',您好!\n 您参与的课程[' + lesson_name + ']的作业[' + homework_name + ']即将在 ' + end_date + ' 截止\n请尽快提交作业\n From: 在线作业管理系统'send_mail(subject='作业截止提醒', message=msg, from_email='XXXXXXXXX@qq.com', recipient_list=[email], connection=conn)

补充:

重点在这个装饰器 @register_job()

  • 注册后的任务会根据 id 以及相关的配置进行定时任务
  • 设置触发器:'date’为单次任务,比如指定5月12日执行;'interval’为间隔,比如每隔一分钟执行一次;'cron’为定时执行,比如每天的8点半

代码查重功能:

代码查重的功能也位于 homeworkviews.py

大致思路:(重点)

  1. 前端我们有两个上传文件的按钮还有两个框,选择完文件后,会浏览本地文件,将文件名放入框框里
  2. 当文件框内的字发生了改变且两个框里都有文件的时候,就会触发上传,将两个文件传到我们的media文件夹下的code_comparison文件夹中
  3. 然后完成上传后,继续发ajax,将两个文件名传到后端,那么后端拿到文件名后,就可以打开文件,然后进行代码查重的处理(这个你看下面)
  4. 完成处理之后呢,将结果返回,结果显示到页面上(结果显示的部分先是隐藏的,有结果后再显示,html里有个 hide()show() 你看看)。

代码查重的思路:(重点)

  1. 有文件名了就有路径了,有路径了就可以读取文件,就是下面的 Code() 的部分
  2. 他通过这个 PythonLexer (我读作莱克色) 来分析每个字段,区分他是关键字呀还是变量呀还是标点呀还是换行呀
  3. 每个字段就是对应一个block,那么整个代码就能换成一个二维的block数组,每个block通过下面的方法计算相似度
  4. 根据设定的阈值来判断块是否重复的,比如阈值是60分,这个块的相似度是80分,那他是不是就是重复了
  5. 总的代码的重复度就是 超过相似度阈值的block数除以总block数 (例如:宿舍4个人,其中有3个人超过了60分,那么总体的及格率是不是就是 3 / 4?就是这个道理)

代码查重的代码:(了解,知道函数是干什么的就可以了)

是不是比较简单?清除文件的没有用上就先不管,然后热力图没实现的也不管,就下面这俩:

# 展示 代码查重页面
def show_code_compare(request):return render(request, 'code_compare.html')# 代码查重
def compare_code(request):print('进行代码查重')data = json.loads(request.body)print(data)f1_name = data['f1_name']f2_name = data['f2_name']Similarity_threshold = data['Similarity_threshold']kgrams = data['kgrams']window_size = data['window_size']print(kgrams, window_size, Similarity_threshold)# 代码查重winnowing_rate = code_compare(f1_name, f2_name, kgrams, window_size, Similarity_threshold)return JsonResponse({'code': 0, 'winnowing_rate': winnowing_rate, 'message': '查重成功'})

code_compare() 函数呢是我们写在common.API.code_hander里面的:
code_compare() 开始看奥,有些用不到的我在底下删掉了,比如热力图这种的,这样子看应该会清晰一点

import osfrom pygments.lexers import PythonLexer
from pygments import token
from re import search
import io
from hashlib import sha1
from difflib import SequenceMatcher
from operator import itemgetter
import plotly.graph_objects as go
from plotly.subplots import make_subplotsfrom HMSol.settings import MEDIA_ROOTcategories = {'Call': ['A', 'rgb(80, 138, 44)'],'Builtin': ['B', 'rgb(212, 212, 102)'],'Comparison': ['C', 'rgb(176, 176, 176)'],'FunctionDef': ['D', 'rgb(4, 163, 199)'],'Function': ['F', 'rgb(199, 199, 72)'],'Indent': ['I', 'rgb(237, 237, 237)'],'Keyword': ['K', 'rgb(161, 53, 219)'],'Linefeed': ['L', 'rgb(255, 255, 255)'],'Namespace': ['M', 'rgb(232, 232, 209)'],'Number': ['N', 'rgb(192, 237, 145)'],'Operator': ['O', 'rgb(212, 212, 212)'],'Punctuation': ['P', 'rgb(214, 216, 216)'],'Pseudo': ['Q', 'rgb(14, 3, 163)'],'String': ['S', 'rgb(194, 126, 0)'],'Variable': ['V', 'rgb(184, 184, 176)'],'WordOp': ['W', 'rgb(8, 170, 207)'],'NamespaceKw': ['X', 'rgb(161, 53, 219)']
}# 定义要使用的哈希函数的种类
def hash_fun(text):hs = sha1(text.encode("utf-8"))hs = hs.hexdigest()[-4:]hs = int(hs, 16)return hs# 将字符串转化为kgrams
def kgrams(text, n):text = list(text)return zip(*[text[i:] for i in range(n)])# 获取每个grams的哈希值
def do_hashing(kgrams):hashlist = []for i, kg in enumerate(list(kgrams)):ngram_text = "".join(kg)hashvalue = hash_fun(ngram_text)hashlist.append((hashvalue, i))return hashlistdef computeCode(f1, f2):# 若文件为空,则返回0, 0if None in [f1, f2]:return 0, 0c1 = Code(f1, f1.name)c2 = Code(f2, f2.name)# c1.calculate_similarity(c2)  # Calculate similarityreturn c1, c2# 给token分类
def get_category(t):category = ''  # 用单个大写字母代表类别if t[0] == token.Keyword.Namespace:category = categories['NamespaceKw'][0]elif t[0] == token.Name.Namespace:category = categories['Namespace'][0]elif t[0] == token.Name.Function:category = categories['Function'][0]elif t[0] == token.Name:category = categories['Variable'][0]elif t[0] == token.Name.Builtin.Pseudo:category = categories['Pseudo'][0]elif t[0] == token.Name.Builtin:category = categories['Builtin'][0]elif t[0] in token.Literal.Number:category = categories['Number'][0]elif t[0] in token.Literal.String and t[1] not in ['\'', '\"'] and t[0] not in token.Literal.String.Doc:category = categories['String'][0]elif t[0] == token.Keyword and t[1] == 'def':category = categories['FunctionDef'][0]elif t[0] == token.Keyword:category = categories['Keyword'][0]elif t[0] == token.Text and (search(r'\s{2,}\S', t[1]) is not None):category = categories['Indent'][0]elif t[0] == token.Operator.Word:category = categories['WordOp'][0]elif t[0] == token.Operator and (t[1] == '==' or t[1] == '!='):category = categories['Comparison'][0]elif t[0] == token.Punctuation:category = categories['Punctuation'][0]elif t[0] == token.Operator:category = categories['Operator'][0]# elif t[0] == token.Text and t[1] == '\n':#     category = categories['Linefeed'][0]elif t[0] == token.Whitespace and t[1] == '\n':  # 换行category = categories['Linefeed'][0]else:category = None  # 若为注释或其他未分配的token,则忽略return category# 为定义的类别创建cmap, 用于在plot中显示颜色
def get_cmap():clist = []prev_c = [0, "rgb(255, 255, 255)"]for key in categories:clist.append(prev_c)clist.append([ord(categories[key][0]), prev_c[1]])prev_c = [ord(categories[key][0]), categories[key][1]]# Normalize keys of colour representation# Needed for plotlymax_ = max(clist, key=itemgetter(0))[0]min_ = min(clist, key=itemgetter(0))[0]converted = []for element in clist:converted.append([(element[0] - min_) / (max_ - min_), element[1]])# Return cmap for categoriesreturn converted# 展示使用winnowing算法的相似度
def show_winnowing(c1, c2):# 设置参数# k_size 'KGrams大小', 2, 15, 5# win_size '滑动窗口大小', 2, 15, 4k_size = 5win_size = 4# 'Winnowing-相似度: **{:.0f}%**'.format(c1.winnowing_similarity(c2, k_size, win_size) * 100))print('Winnowing-相似度: **{:.0f}%**'.format(c1.winnowing_similarity(c2, k_size, win_size) * 100))# [论文](https://theory.stanford.edu/~aiken/publications/papers/sigmod03.pdf)以获得更多信息"# 返回交集的长度
def intersection(lst1, lst2):temp = set(lst2)lst3 = [value for value in lst1 if value in temp]return len(lst3)# 将滑动窗口应用于哈希列表
def sl_window(hashes, n):return zip(*[hashes[i:] for i in range(n)])# 获取滑动窗口的最小值
def get_min(windows):result = []prev_min = ()for w in windows:# 找到最小散列并取最右边的出现min_h = min(w, key=lambda x: (x[0], -x[1]))# 仅在与前一个最小值不同时才使用散列if min_h != prev_min:result.append(min_h)prev_min = min_hreturn result# winnowing算法
def winnowing(text, size_k, window_size):hashes = (do_hashing(kgrams(text, size_k)))return set(get_min(sl_window(hashes, window_size)))# 使用winnowing算法和jaccard距离得到相似度
def winnowing_similarity(text_a, text_b, size_k=5, window_size=4):# Get fingerprints using winnowingw1 = winnowing(text_a, size_k, window_size)w2 = winnowing(text_b, size_k, window_size)# print('w1:', w1)# Do use list instead of set to also consider number of occurece of copied contenthash_list_a = [x[0] for x in w1]hash_list_b = [x[0] for x in w2]# 交集intersect = intersection(hash_list_a, hash_list_b) + intersection(hash_list_b, hash_list_a)# 全集union = len(hash_list_a) + len(hash_list_b)# print('intersect:', intersect)# print('union:', union)return intersect / union# 为一个block创建tokens
class Block(object):def __init__(self, tokens, similarity=0, compared=False):self._similarity = similarity# self._compared = comparedself._tokens = tokens@propertydef similarity(self):return self._similarity@similarity.setterdef similarity(self, s):self._similarity = s@propertydef tokens(self):return self._tokens# 运算符重载def __len__(self):return len(self.tokens)def __str__(self):return ''.join(str(t[0]) for t in self.tokens)# 对象方法:# 使用token比较两个blockdef compare(self, other):if isinstance(other, Block):# 使用DIFFLIB计算相似度return SequenceMatcher(None, str(self), str(other)).ratio()# 使用原始字符串比较两个blockdef compare_str(self, other):if isinstance(other, Block):return SequenceMatcher(None, self.clnstr(), other.clnstr()).ratio()def clnstr(self):return ''.join(str(t[3].lower()) for t in self.tokens)def max_row(self):return max(self.tokens, key=itemgetter(1))[1]def max_col(self):return max(self.tokens, key=itemgetter(2))[2]# 将源代码用色块tokens表示, 便于实现相似性比较
class Code:# 初始化def __init__(self, text, name="", similarity_threshold=0.9):self._blocks = []self._max_row = 0self._max_col = 0self._name = nameself._similarity_threshold = similarity_thresholdself._lvs_blocksize = 8self.__tokenizeFromText(text)# 分析代码def __tokenizeFromText(self, text):lexer = PythonLexer()  # 使用lexer做词法分析, 判断属于哪种编程语言tokens = lexer.get_tokens(text)tokens = list(tokens)  # 转化为listresult = []prev_c = ''  # 前一个的类别row = 0col = 0# 使用category简化tokensfor token in tokens:c = get_category(token)if c is not None:# 换行检测,更新坐标但不加入resultif c == 'L':row = row + 1col = 0# 检测到新的block, prev_c为\n且不为缩进elif prev_c == 'L' and c != 'I' and result:self.blocks.append(Block(result))result = []# 不为空行if c != 'L':# 区分函数调用和变量if prev_c == 'V' and token[1] == '(':result[-1] = 'A', result[-1][1], result[-1][2], result[-1][3]result.append((c, row, col, token[1]))col += 1if col > self._max_col:self._max_col = colprev_c = cself._max_row = row  # 依照代码的行数更新最大行数# 结果不为空则追加最后一个blockif result:self.blocks.append(Block(result))@propertydef blocks(self):return self._blocks@blocks.setterdef blocks(self, b):self._blocks = b# 重置所有block的相似度def resetSimilarity(self):for block in self.blocks:block.similarity = 0# 使用string compare和annotate方法查找能匹配的blockdef __pre_process(self, other):other_blocks = other.blocksfor block_a in self.blocks:for block_b in other_blocks:if block_a.similarity == 1:breakif block_a.clnstr() == block_b.clnstr():block_a.similarity = 1.0block_b.similarity = 1.0# 处理代码的相似度def __process_similarity(self, other):for block_a in self.blocks:if block_a.similarity == 0:best_score = 0  # 记录最佳匹配分数for block_b in other.blocks:if len(block_a) > self._lvs_blocksize:score = block_a.compare(block_b)else:score = block_a.compare_str(block_b)# 找到最大的匹配分数if score >= best_score:best_score = score# 也为代码b设置最佳匹配数if block_b.similarity < best_score:block_b.similarity = best_scoreblock_a.similarity = best_score# 计算每个块的相似度分数(使用levensthein计算方法)def calculate_similarity(self, other):# 将所有block的相似度置为空self.resetSimilarity()other.resetSimilarity()self.__pre_process(other)self.__process_similarity(other)other.__process_similarity(self)# 返回计算的相似度,为超过相似度阈值的block数除以总block数def getSimScore(self):total_len = 0len_plagiat = 0for block in self.blocks:total_len += len(block)if block.similarity >= self._similarity_threshold:len_plagiat += len(block) * block.similarityreturn len_plagiat / total_len# 计算每个块的相似度分数(使用使用winnowing方法)def winnowing_similarity(self, other, size_k=5, window_size=4):score = winnowing_similarity(str(self), str(other), size_k, window_size)return score# 打印结果 也是返回一个列表
def printResult(c1, c2, threshold):threshold /= 100 # 除以100 比如我们设置的是60 实际用到的是0.6# 这就是下面用到的 判断中等、高的门槛plagrism_threshold_high = 90plagrism_threshold_medium = 60# 两个代码的相似度阈值都设为我们传入的这个thresholdc1._similarity_threshold = thresholdc2._similarity_threshold = thresholdans = []if (c1.getSimScore() * 100) >= plagrism_threshold_high:ans.append('\'{}\' 的相似度为 {:.0f}% 相似度高, 可以认为是抄袭'.format(c1._name, c1.getSimScore() * 100))elif (c1.getSimScore() * 100) >= plagrism_threshold_medium:ans.append('\'{}\' 的相似度为 {:.0f}% 相似度中等, 可以认为不是抄袭'.format(c1._name, c1.getSimScore() * 100))else:ans.append('\'{}\' 的相似度为 {:.0f}% 相似度低, 可以认为不是抄袭'.format(c1._name, c1.getSimScore() * 100))if (c2.getSimScore() * 100) >= plagrism_threshold_high:ans.append('\'{}\' 的相似度为 {:.0f}% 相似度高, 可以认为是抄袭'.format(c2._name, c2.getSimScore() * 100))elif (c2.getSimScore() * 100) >= plagrism_threshold_medium:ans.append('\'{}\' 的相似度为 {:.0f}% 相似度中等, 可以认为不是抄袭'.format(c2._name, c2.getSimScore() * 100))else:ans.append('\'{}\' 的相似度为 {:.0f}% 相似度低, 可以认为不是抄袭'.format(c2._name, c2.getSimScore() * 100))return ans# 代码查重 传入两个文件的名称 还有滑块的三个参数
def code_compare(file_1_name, file_2_name, k_size, win_size, Similarity_threshold):# 文件所在路径file_1_url = MEDIA_ROOT + '/code_comparison/' + file_1_namefile_2_url = MEDIA_ROOT + '/code_comparison/' + file_2_name# 读取文件部分file_1 = open(file_1_url, 'br')  # 使用二进制file_2 = open(file_2_url, 'br')io_file_1 = io.BytesIO(file_1.read())  # 使用BytesIO读取io_file_2 = io.BytesIO(file_2.read())file1 = io_file_1.read().decode(errors='忽略')file2 = io_file_2.read().decode(errors='忽略')c1 = Code(file1, file_1_name)   # Code()是上面写的类 你可以点过去看c2 = Code(file2, file_2_name)# 计算相似度c1.calculate_similarity(c2)# 若文件不为空,则将结果加到这个ans列表里,返回if 0 not in [c1, c2]:ans = []w = 'Winnowing-相似度: {:.0f}%'.format(c1.winnowing_similarity(c2, k_size, win_size) * 100)ans.append(w)res = printResult(c1, c2, Similarity_threshold)for r in res:ans.append(r)return ans

有些函数你就看注释就好了 知道他是干什么的,比如这个
就记他是处理代码的相似度的

# 处理代码的相似度
def __process_similarity(self, other):for block_a in self.blocks:if block_a.similarity == 0:best_score = 0  # 记录最佳匹配分数for block_b in other.blocks:if len(block_a) > self._lvs_blocksize:score = block_a.compare(block_b)else:score = block_a.compare_str(block_b)# 找到最大的匹配分数if score >= best_score:best_score = score# 也为代码b设置最佳匹配数if block_b.similarity < best_score:block_b.similarity = best_scoreblock_a.similarity = best_score

批量导入部分:

位于 myadmin 模块
用到 xlrd 模块,我读作xl read

思路:

  1. xlrd 读取文件
  2. 遍历每个单元格,对需要进行字典转换的进行转换,一行读完,就作为字典存入列表
  3. 遍历列表,对于每一个字典,如果数据库中存在该用户信息就更新,不存在就新增
# 上传文件 批量生成用户
@csrf_exempt # 取消当前View视图函数防御
def upload_xlsx(request):# 读文件file = request.FILES.get('file')wb = xlrd.open_workbook(filename=None, file_contents=file.read())table = wb.sheets()[0]# 转换用字典my_dict = {'ID': 'id', '姓名': 'name', '性别': 'sex', '类型': 'type', '邮箱': 'email'}my_dict_2 = {'教师': 1, '学生': 2, '男': 1, '女': 0,}# 行数 列数rows_count = table.nrowscols_count = table.ncolsprint('行数:', rows_count, '列数: ', cols_count)flag = 0message = ''if rows_count > 1:list_data = []# 从第二行开始for i in range(1, rows_count):dict_row = {}for j in range(0, cols_count):cell = table.cell_value(i, j)# 浮点转成整型if table.cell(i, j).ctype == 2 and cell % 1 == 0.0:cell = int(cell)# 字典转换if cell in my_dict_2:dict_row[my_dict.get(table.cell_value(0, j))] = my_dict_2[cell]else:dict_row[my_dict.get(table.cell_value(0, j))] = celllist_data.append(dict_row)print(list_data)# 对数据库更新  {}success_count = 0for tmp in list_data:this_id = tmp['id']# 选择数据doctor_data = User.objects.filter(id=this_id)if doctor_data:doctor_data.update(**tmp)else:User.objects.create(id=this_id,pwd=this_id,name=tmp['name'],sex=tmp['sex'],type=tmp['type'],email=tmp['email'],)success_count = success_count + 1message += '成功创建了' + str(success_count) + '个用户! 失败了' + str(rows_count - success_count - 1) + '条记录!'return JsonResponse({'ret': flag, 'msg': message})

关于CSRF:(了解)

CSRF(跨站请求伪造漏洞),是网站的一种漏洞
系统为了避免CSRF,所以在跨域的时候需要加上验证
就是那个在html里面经常看到的那一串:

// ajax 头部增加csrf_token
let token = "{{ csrf_token }}";
$.ajaxSetup({headers: { 'X-CSRFTOKEN': `${token}` }, // 这里是headers,不是data,  CSRF
});

在有发送ajax的情况下就需要加上这么一块,否则会报错

给XZZ准备的小攻略(私人向)相关推荐

  1. java手机 上网_Java也懂智能! 中低端手机上网小攻略

    Java也懂智能! 中低端手机上网小攻略 2009年04月20日 04:18作者:Ken编辑:李博文章出处:泡泡网原创 分享 价格适中.操作快捷.功能合理,还带有一些如上网.游戏等便利的功能拓展,中低 ...

  2. 爬虫手机App——数据采集小攻略

    爬虫手机App数据采集小攻略 最近帮朋友研究爬虫软件,抓取手机App的资源,想总结一下爬虫手机App资源的小套路,翻到这哥们总结的不错,就不重复造轮子了!下面请看?↓ 手机App的采集其实跟web采集 ...

  3. 麦芒6计算机记录,最简单易懂的HUAWEI 麦芒6实用小攻略

    原标题:最简单易懂的HUAWEI 麦芒6实用小攻略 "为啥" 是一个大型综合类问答平台. 主要提供-- 产品咨询.使用帮助.心得分享 梦想互助.情感困惑 等问题答疑服务. 在微博( ...

  4. 怎样调节计算机安全度,如何调整鼠标灵敏度调节小攻略

    如何调整鼠标灵敏度调节小攻略 导读:有时候鼠标精确度和移动速度很不灵活,这样就大大地影响了系统操作效率.如果调节鼠标适合自己的使用速度呢?就跟随百分网小编一起去了解下吧,想了解更多相关信息请持续关注我 ...

  5. 威洛特:狗狗洁齿磨牙小攻略

    有些女孩纸表面光鲜亮丽,背后却在家偷偷给狗狗抠牙结石!坊间盛传狗狗得了牙石就用挖耳勺抠,还表示好用到飞起?那么,这样做真的适合做吗?很多铲屎官都是等到狗狗牙齿黄了.蛀牙了.有牙菌斑了.牙结石了,才想起 ...

  6. 「旅游小攻略」广东河源

    Hello 小伙伴们好呀,我是爱折腾的 jsliang~ 今天主要安利的,是「广东省/河源市/源城区」附近的逛吃逛吃. 特别适合 2 天 1 夜.3 天 2 夜的,自驾游或者随心走的小伙伴,随着本篇攻 ...

  7. vue.js 密码加密_软件操作与排错专题_第五期文档及操作系统加密小攻略

    今天我将给大家演示一下如何加密文件.文档.以及设置操作系统账户密码. 一.文件加密 1.Word文档密码的设置 (1)打开Word文件, 选择菜单栏的"文件". (2)点击&quo ...

  8. 【Java小攻略】时间API全解析

    文章目录 时间API全解析 一.艰苦岁月 (一)简述 (二)时间与计算机 (三)常见的API操作 1.Calendar介绍 2.TimeStamp介绍 3.Date介绍 二.新气象 (一)简述 (二) ...

  9. Thindpad T430u折腾小攻略

    前言: 对于这机子,先说两句吧.性能还ok,i5-3337U,双核心四线程(模拟四核):3芯锂离子电池(续航不太够,3小时左右吧):电池位于键盘右下,还有点热,很不爽:usb只有两个,严重不爽. 1. ...

最新文章

  1. 【Python自学】万文字,学习框架+思维整理,入门就是这么简单
  2. AIX HA模拟宕机--维护磁带机
  3. 只允许指定IP远程桌面连接_使用IP安全策略
  4. idea 设置项目跑在tomcat上
  5. Bag-of-words model
  6. 数据库复习之规范化理论
  7. JAVA进阶day04多态(向上转化,向下转化)
  8. vue router传参_新手使用vue-router传参时注意事项
  9. java用NIO实现文件传输_Java Nio 实现文件的传输
  10. lsoci mysql_flask项目从sqlite3升级的mysql数据库
  11. Verilog语法之generate语句
  12. 如何卸载CAD 2019 ?怎么把AutoCAD 2019彻底卸载删除干净重新安装的方法【转载】
  13. Excel表格中两列数据对比,找出异同的数据
  14. 基于SpringBoot-上传照片保存到本地,且回显照片
  15. 深入table之collapse
  16. 大学计算机音乐一起学,和学生一起学音乐
  17. Step1我学习ros2的一些经历(从ubuntu安装到ros2中的位姿转换)
  18. python语言程序设计项目_《Python语言程序设计》项目报告书Word版
  19. RGB三原色的简单理解
  20. Opencv Mat数据类型操作

热门文章

  1. Java动态代理(以现实中我们熟悉的中介代理租房、卖房模式理解)(附案例详解)
  2. 360OS的财产隔离系统
  3. python下载包的时候,如何选择是win32,还是amd64的,其中的cp又是什么意思?
  4. 今日头条的面试题:LRU原理和Redis实现
  5. 美国大学生数学建模竞赛数据常用网站-数学建模(十九)
  6. 离散数学-函数-定义和性质-13
  7. final 评论 I
  8. uni-app 项目引入第三方js插件,单个js文件引入成功,调用该插件方法
  9. C语言isspace()函数:判断字符是否为空白字符
  10. 如何拍照识别花名?手机拍照识花的软件分享