分类

课内:
不限
类型:
不限 游戏 项目 竞赛 个人研究 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
年份:
不限 2018 2019

资源列表

  • 基于SSH框架的电影订票系统网站的设计与实现

    1 总体描述1.1 产品前景目前国内市场上的电影购票网站很多,各个网站都有一定的用户量。用户还处于一种培养习惯的阶段。鉴于目前各个购票网站的质量参差不齐,许多网站都把大部分内容堆积在其首页,我们如果能够做一款用户体验极佳的购票网站,用户有好的体验过程,再加上我们网站特有的功能,用户就会慢慢习惯使用我们的网站,长远看来这有很大的商业机遇。
    在我们购票网站中,我们可以通过与影片制片方合作,在我们网页中推荐其即将上映的影片,只要我们的网站流量足够,这无疑会是一个很大的商机。
    1.2 产品功能
    记录销售交易与实时票务统计 支付交易(使用第三方交易网站进行交易)用户账号的安全性管理电影的介绍以及基本了解提供喜欢电影的推荐以及提醒功能针对不同使用场景下的自适应界面基于行业标准,与第三方库进行实时交易,包括电影票务、支付、院线、选座系统等
    1.3 用户类及其特征
    普通用户:以消遣为主,内容消费较少,对网站的使用体验比较在意,对网 站能够提供的附加功能比较敏感电影爱好者:对于电影有内容和题材偏好,有喜欢的导演和演员。在意网站 推荐的的电影,在意网站提供关于影片的评价专业影评人:很在意网站上的评论功能,提供好的评论交互方式会吸引这样 的用户
    1.4 运行环境支持PC端网页访问,移动端暂不支持
    1.5 开发环境和工具
    终端支持:PC
    开发语言框架:HTML5,CSS3,JavaScript
    服务器端支持
    语言:Java,Python
    Web框架:Struts MVC + Spring Boot,Hibernate
    关系数据库:MySQL
    负载均衡机制:Nginx
    开发平台与工具
    IDE:eclipse
    集成与测试:Travis
    源代码管理:Github
    项目管理与自动构建:maven

    1.6 开发规范
    WEB前端
    语言:Javascript,html,CSS
    代码风格:JS ES5代码风格;ES6代码风格;CSS代码风格;HTML/CSS代码风格
    自动化检测工具:ESLint
    WEB后端
    语言:Java
    代码风格 Google Java Style(科学上网),中文翻译
    自动化检测工具:Checkstyle(Ecplise 插件安装教程)
    爬虫脚本
    语言:Python 3.6+
    代码风格:Python风格规范

    1.7 设计和实现上的约束
    设计约束:改变现有购票网站较为杂乱的整体局面,剔除购票流程的冗 余过程,符合现代年轻人审美,尽量做到简洁、美观、大方
    可用性:系统需要提供较为完整的第三方接口,以供院线使用。功能完 备,贴合用户要求,能够提供较好的电影购票体验
    可靠性:使用行业标准,以便于部不同的第三方接口进行信息交换。有 一定的防差错功能,能保证高峰时期的购票正常
    可支持性:标准的接口,在进行信息交换的时候流畅无差错。

    1.8 假设和依赖本平台依赖于PC端运行环境,后台是使用了JAVA进行编写,使用了MySQL数据库管理系统对用户信息、电影及影院信息进行管理,前端页面显示使用了MVC框架。
    2 系统功能


    ID
    Name
    Imp
    Est
    How to demo
    Notes




    0
    搜索框
    7
    8
    可搜索影片、影院



    1
    电影 (按钮)
    8
    12
    点击主页面上方“电影”按钮,根据当前电影热度,票房,评分等列出现在上映的所有电影,每个电影的小项里包括名字,影片时长,类型,主演,评分以及选座购票按钮
    需要用户授权定位,也可点击定位,手动选择定位


    2
    影片详情
    9
    12
    页面包括名字,影片时长,上映日期,影片简介,影片评分,影片海报,可选影院部分(推荐)列表,导演编剧等各项信息及选座购票按钮
    影片评分我们提供豆瓣,烂番茄,时光网等不同影评网站的评分,使用户参考更多样


    3
    选座购票
    10
    9
    从影片列表或者影片详情页都可点击选座购票
    会有影院列表,点击进去就会看到场次票价,用户可根据需求选择场次,座次


    4
    即将上映
    5
    6
    在主页面中,给出近期即将上映的所有电影
    点击每个分项会进入影片详情页


    5
    登录
    3
    11
    点击主页面“登录”,进入登录界面,输入用户名密码即登陆成功
    在登录界面包含注册按钮,账户可与社交账号绑定或绑定邮箱,要与手机绑定(用于短信提醒)


    6
    我的主页
    6
    8
    登陆成功后,点击主页面“我的”,进入我的主页
    含有电影票订单、优惠方式、收藏三大类,其中优惠方式包含折扣卡、红包、现金券等;收藏分为电影收藏、影院收藏、活动收藏



    3 数据库实体关系图

    实体定义

    user 表:用户表,记录用户的信息,用户名,密码的 MD5,电话,邮箱movie 表:记录电影的信息,包括中文名,英文名,电影类型,电影时长,上映日期,电影简介,电影海报的 URL,参演人员名单 person 表:记录电影人的信息,通过 type 列区别是导演还是演员,包括名字,照片 URL,type 电影人的类型(导演/演员) cinema 表:订单编号,电影 id、影院 id、场次 id、座位 id screen 表:荧屏 id,语言,价格,房间 id,时间,影院 id,电影名字,座位 id admin 表:id,名字,密码,email,电话号码movie 表和 person 表是一对多的关联映射关系
    四、总体设计4.1 概念术语描述(后端)4.1.1 Java
    java是纯面向对象编程的语言平台无关性 (一次编译,到处运行;Write Once,Run Anywhere)java提供了许多内置的类库,通过这些类库,简化了开发人员的设计工作,同时缩短了项目开发时间提供了对Web应用开发的支持,例如,Applet,Servlet,和JSP可以用来开发Web应用程序,,Socket,RMI可以用来开发分布式应用程序的类库去除了c++中难以理解,容易混淆的特性(如c++中的多继承,头文件,指针,结构,单元,运算符重载,虚拟基础类,使得程序更加严谨,整洁具有较好的安全性和健壮性。java语言经常会被用在网络环境中,为了增强程序的安全性
    4.1.2 SpringSpring Framework(简称Spring)是根据Rod Johnson著名的《Expert One-on-One J2EE Design and Development》而开发的J2EE应用程序框架。目前主要根据Rod Johnson和Juergen Hoeller而进行开发的,目前发布的最新版为1.1.4。 Spring是J2EE应用程序框架,不过,更严格地讲它是针对Bean的生命周期进行管理的轻量级容器(Lightweight container),可以单独利用Spring构筑应用程序,也可以和Struts,Webwork,Tapestry等众多Web应用程序框架组合使用,并且可以与Swing等桌面应用程序API组合。所以Spring并不仅仅只能应用在J2EE中,也可以应用在桌面应用及小应用程序中。针对Spring开发的组件不需要任何外部库。
    优点:

    Spring能有效地组织你的中间层对象Spring能消除在许多工程中常见的对Singleton的过多使用Spring能消除各种各样自定义格式的属性文件的需要,使配置信息一元化Spring能够帮助我们真正意义上实现针对接口编程在Spring应用中的大多数业务对象没有依赖于Spring使用Spring构建的应用程序易于单元测试Spring支持JDBC和O/R Mapping产品(Hibernate)MVC Web框架,提供一种清晰,无侵略性的MVC实现方式JNDI抽象层,便于改变实现细节,可以方便地在远程服务和本地服务间切换简化访问数据库时的例外处理Spring能使用AOP提供声明性事务管理,可以不直接操作JTA也能够对事务进行管理提供了JavaMail或其他邮件系统的支持
    4.2 概念术语描述(前端)5.2.1 Vue.jsVue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。
    优点:

    响应式编程:mvvm框架,实现数据的双向绑定组件化:一切都是组件,组件可以套其他组件,增强了可复用性模块化:我们用一个模块打包工具来配合 Vue.js,比如Webpack或者Browserify,然后再加上 ES2015。每一个 Vue 组件都可以看做一个独立的模块动画:Vue 自带简洁易用的过渡动画系统。有很多获奖的互动类网站是用 Vue 开发的。Vue 的反应式系统也使得它可以用来开发高效的数据驱动的逐帧动画路由:Vue 本身是不带路由功能的。但是,有vue-router这个可选的库来配合。vue-router 可以将嵌套的路径映射到嵌套的组件,并且提供了细致的路径跳转控制文档和配套设施:文档和配套设施完善,社区活跃,生态系统完备,易于上手
    4.2.2 ES6(ECMAScript 6)新一代的javascript也被称为ECMAScript 6(也称为 ES6 or Harmony)。
    优点:

    糖语法:首先,语法糖是一种语法,使得语言更容易理解和更具有可读性,它使语言相对我们来说变得更”甜”。这也意味着ES6的一些”新”的特点并不是真的新,只是试图简化语法而已,让我们编程更容易。这样就无需使用老式的取巧的方法编写你的代码,而是可以一种更简单的方式来编写代码,那就是使用糖语法模块Module:如果你想将所有js放在一个文件中,或者你要在应用的不同地方使用相同的功能,你就要使用模块,关键词是exportlet和const:在一段代码块中用let或者const声明的变量会限制它们只在这个块中可见。这叫做块级作用域
    4.3 基本设计描述4.3.1 系统总体逻辑结构图
    4.3.2 系统部署结构图
    4.4 主要界面流程描述
    4.5 模块列表4.5.1 模块划分


    后端
    前端









    4.5.2 前端结构
    4.6 Web服务器4.6.1 返回的状态码


    类型
    stateCode
    info




    成功
    200
    NULL


    错误
    500
    错误信息



    4.6.2 用户登录/注册


    路由
    方法
    说明
    提交格式
    测试




    /api/login
    POST
    提交用户登录表单 username, password, 允许邮箱/手机/用户名登录

    OK


    /api/signup
    POST
    提交用户注册表单 username, password, email, phone

    OK


    /api/logout
    PUT
    登出

    OK


    /api/user
    GET
    获取当前用户信息

    OK


    /api/user
    PUT
    修改当前用户信息,填写需要修改的项,username,email,phone,oldPassword,newPassword

    OK


    /api/user/order
    GET
    查看该用户的所有订单
    OrderModel
    OK


    /api/user/screen/{id}
    PUT
    锁定/购买座位,需要上传 需要用户登录后
    eat={88长字符串, 锁定的位置用1表示,购买位置用2表示,其他用0填充}



    // 购票例子:// 表单格式:seat=1100000000000000000000000000000000000000000000000000000000000000000000000000000000000000// 表示锁定第1,2个位置seat=0022000000000000000000000000000000000000000000000000000000000000000000000000000000000000// 表示购买第1,2个位置// 购买前需要先锁定public class OrderModel { private List<FilmOrder> filmOrderModelList;}public class FilmOrder { private Integer id; private User user; private Integer screenId; private String seat;}
    4.6.3 管理员账号


    路由
    方法
    说明
    提交格式




    /api/admin/login
    POST
    adminname, password 登录



    /api/admin/logout
    PUT
    登出



    /api/admin/newMovie
    POST
    需要填写的域chineseName,englishName,pictureUrl,type,length,releaseDate,introduction



    /api/admin/{id}
    DELETE
    当初对应id的电影



    /api/admin/{id}
    PUT
    更新电影信息,只需要填写需要更新的域,和创建电影的域名字相同



    /api/admin/cinema/{id}
    DELETE
    参数对应id的影院



    /api/admin/cinema/create
    POST
    创建一个影院
    CinemaModel


    /api/admin/cinema/{id}
    PUT
    修改一个影院信息
    CinemaModel



    CinemaModel { private String name; private String address; private String phone; private List<Screen> screens;}
    4.6.4 获取电影信息


    路由
    方法
    说明
    返回值
    测试




    /api/movie/name/{查询电影名}
    GET
    返回电影名对应信息,允许查询中英文电影名,返回一条记录或空
    SimpMovie
    OK


    /api/movie/type/{type}?id=ID
    GET
    返回电影类型列表, 数目为从id开始往后20条,默认id = 0
    List
    OK


    /api/movie/date/day/20170501
    GET
    返回2017-05-01上映的电影列表,如果输入非法日期,返回当天上映列表
    List
    OK


    /api/movie/date/month/201705
    GET
    返回2017-05上映的电影列表,如果输入非法日期,返回当月上映列表
    List
    OK


    /api/movie/date/year/2017
    GET
    返回2017上映的电影列表,如果输入非法日期,返回当年上映列表
    List
    OK


    /api/movie/{id}
    GET
    返回ID=id的电影详细信息
    Movie
    OK


    /api/movie/showing/{number}
    GET
    返回最近一个月上映的电影列表,number条
    List
    OK


    /api/movie/query/count?type={}&area={}&year={}
    GET
    year=2007, 允许type,area,year字段为”all”
    Integer
    OK


    /api/movie/query?type={}&area={}&year={}&page={}&step={}
    GET
    返回 [pagestap, pagestep+step]的数据,允许type,area,year字段为”all”
    List
    OK



    SimpMovie { private String name; private Integer id; private String url;}
    4.6.5 获取演员/导演信息


    路由
    方法
    说明
    返回值
    测试




    /api/person/{id}
    GET
    通过演员/导演的ID获取
    Person
    OK


    /api/person/movie/{id}
    GET
    获取电影ID的演员/导演名单
    List
    OK



    Person { private Integer id; // 名字 private String name; // 照片的URL private String url; // 表示是导演还是演员 private String type; // "actor", "director"}
    4.6.6 获取影院信息


    路由
    方法
    说明
    接受内容
    返回值
    测试




    /api/cinema?number={}&address={}
    GET
    number选填默认10,address必填

    List



    /api/cinema/{id}
    GET
    返回影院详细信息

    Cinema
    OK


    /api/cinema/showing?id={id}
    GET
    返回正在该影院上映的电影简要信息列表

    List
    OK



    SimpCinema { private Integer id; private String name;}Cinema { private Integer id; private String name; private String address; private String phone; private List<Screen> screens;}
    4.6.7 获取排片信息


    路由
    方法
    说明
    接受内容
    返回值
    测试




    /api/screen?cinemaid={}&movieid={}&date={}&time={}
    GET
    获取对应影院对应电影的排片情况列表

    List
    OK


    /api/screen/{id}
    GET
    获取对应id的排片情况

    Screen
    OK



    Seat { private List<Integer> vacancy; private List<Integer> soldOut; private List<Integer> locking;}Screen { private Integer id; private Date time; private String language; private String room; private Double price; private Cinema cinema; private String movieName; private String seats; // '0'->空位,'1'->被锁定,'2'->已售出 8x11 列优先, 比如2行1列下标为8}
    4.6.8 搜索功能


    路由
    方法
    说明
    返回值
    测试




    /api/search?query={}






    五、软件设计技术5.1 前后端分离5.1.1 理解MVCMVC是一种经典的设计模式,全名为Model-View-Controller,即模型-视图-控制器。
    其中,模型是用于封装数据的载体,例如,在Java中一般通过一个简单的POJO(Plain Ordinary Java Object)来表示,其本质是一个普通的java Bean,包含一系列的成员变量及其getter/setter方法。对于视图而言,它更加偏重于展现,也就是说,视图决定了界面到底长什么样子,在Java中可通过JSP来充当视图,或者通过纯HTML的方式进行展现,而后者才是目前的主流。模型和视图需要通过控制器来进行粘合,例如,用户发送一个HTTP请求,此时该请求首先会进入控制器,然后控制器去获取数据并将其封装为模型,最后将模型传递到视图中进行展现。
    综上所述,MVC的交互过程如图1所示。

    5.1.2 MVC模式的优点与不足MVC模式早在上个世纪70年代就诞生了,直到今天它依然存在,可见生命力相当之强。MVC模式最早用于Smalltalk语言中,最后在其它许多开发语言中都得到了很好的应用,例如,Java中的Struts、spring MVC等框架。正是因为这些MVC框架的出现,才让MVC模式真正落地,让开发更加高效,让代码耦合度尽量减小,让应用程序各部分的职责更加清晰。
    既然MVC模式这么好,难道它就没有不足的地方吗?我认为MVC至少有以下三点不足:

    每次请求必须经过“控制器->模型->视图”这个流程,用户才能看到最终的展现的界面,这个过程似乎有些复杂。
    实际上视图是依赖于模型的,换句话说,如果没有模型,视图也无法呈现出最终的效果。
    渲染视图的过程是在服务端来完成的,最终呈现给浏览器的是带有模型的视图页面,性能无法得到很好的优化。

    为了使数据展现过程更加直接,并且提供更好的用户体验,我们有必要对MVC模式进行改进。不妨这样来尝试,首先从浏览器发送AJAX请求,然后服务端接受该请求并返回JSON数据返回给浏览器,最后在浏览器中进行界面渲染。
    改进后的MVC模式如图2所示。

    也就是说,我们输入的是AJAX请求,输出的是JSON数据,市面上有这样的技术来实现这个功能吗?答案是REST。
    REST全称是Representational State Transfer(表述性状态转移),它是RoyFielding博士在2000年写的一篇关于软件架构风格的论文,此文一出,威震四方!国内外许多知名互联网公司纷纷开始采用这种轻量级的Web服务,大家习惯将其称为RESTful Web Services,或简称REST服务。]
    如果将浏览器这一端视为前端,而服务器那一端视为后端的话,可以将以上改进后的MVC模式简化为以下前后端分离模式,如图3所示。

    可见,有了REST服务,前端关注界面展现,后端关注业务逻辑,分工明确,职责清晰。那么,如何使用REST服务将应用程序进行前后端分离呢?我们接下来继续探讨,首先我们需要认识REST。
    5.1.3 认识RESTREST本质上是使用URL来访问资源种方式。众所周知,URL就是我们平常使用的请求地址了,其中包括两部分:请求方式与请求路径,比较常见的请求方式是GET与POST,但在REST中又提出了几种其它类型的请求方式,汇总起来有六种:GET、POST、PUT、DELETE、HEAD、OPTIONS。尤其是前四种,正好与CRUD(Create-Retrieve-Update-Delete,增删改查)四种操作相对应,例如,GET(查)、POST(增)、PUT(改)、DELETE(删),这正是REST与CRUD的异曲同工之妙!需要强调的是,REST是“面向资源”的,这里提到的资源,实际上就是我们常说的领域对象,在系统设计过程中,我们经常通过领域对象来进行数据建模。
    REST是一个“无状态”的架构模式,因为在任何时候都可以由客户端发出请求到服务端,最终返回自己想要的数据,当前请求不会受到上次请求的影响。也就是说,服务端将内部资源发布REST服务,客户端通过URL来访问这些资源,这不就是SOA所提倡的“面向服务”的思想吗?所以,REST也被人们看做是一种“轻量级”的SOA实现技术,因此在企业级应用与互联网应用中都得到了广泛应用。
    下面我们举几个例子对REST请求进行简单描述:可以查看API来更好地理解。
    可见,请求路径相同,但请求方式不同,所代表的业务操作也不同,例如,/advertiser/1这个请求,带有GET、PUT、DELETE三种不同的请求方式,对应三种不同的业务操作。
    虽然REST看起来还是很简单的,实际上我们往往需要提供一个REST框架,让其实现前后端分离架构,让开发人员将精力集中在业务上,而并非那些具体的技术细节。下面我们将使用Java技术来实现这个REST框架,整体框架会基于Spring进行开发。
    5.2 Vue渐进式框架5.2.1 为什么要有框架框架的存在是为了帮助我们应对复杂度
    前端框架特别多,那么为什么要有框架呢?框架的存在是为了帮助我们应对复杂度。当我们需要解决一些前端上工程问题的时候,这些问题会有不同的复杂度。如果你用太简陋的工具应对非常复杂的需求,就会极大地影响你的生产力。所以,框架本身是帮我们把一些重复的并且已经受过验证的模式,抽象到一个已经帮你设计好的API封装当中,帮助我们去应对这些复杂的问题。
    框架自身也有复杂度
    框架本身也会带来复杂度。相信大家在调研各种框架或学习各种框架时,会遇到学习曲线问题——有些框架会让人一时不知如何上手。
    工具复杂度是为了处理内在复杂度所做的投资
    工具的复杂度是可以理解为是我们为了处理问题内在复杂度所做的投资。为什么叫投资?那是因为如果投的太少,就起不到规模的效应,不会有合理的回报。这就像创业公司拿风投,投多少是很重要的问题。如果要解决的问题本身是非常复杂的,那么你用一个过于简陋的工具应付它,就会遇到工具太弱而使得生产力受影响的问题。
    反之,是如果所要解决的问题并不复杂,但你却用了很复杂的框架,那么就相当于杀鸡用牛刀,会遇到工具复杂度所带来的副作用,不仅会失去工具本身所带来优势,还会增加各种问题,例如培训成本、上手成本,以及实际开发效率等。
    Pick the right tool for the job
    “Pick theright tool for the job”——在国外,跟开发者讨论一些框架选型问题时,大家都会说这句话——一切都要看场景。因为,前端开发原生开发或者桌面开发模式相比,有自己的独特之处,它跟其实并不那么固定。在Web上面,应用可以有非常多的形态,不同形态的Web应用可能有完全不同程度的复杂度。这也是为什么要谈工具复杂度和所要做的应用复杂度的问题。
    怎么看前端框架的复杂度
    目前的前端开发已经越来越工程化,而我们需要解决的实际问题也是不同的。我们就下图进行分析。

    我们可能在任何情况下都需要声明式的渲染功能 ,并希望尽可能避免手动操作,或者说是可变的命令式操 ,希望尽可能地让DOM的更新操作是自动的,状态变化的时候它就应该自动更新到正确的状态;我们需要组件系统,将一个大型的界面切分成一个一个更小的可控单元; 客户端路由 ——这是针对单页应用而言,不做就不需要,如果需要做单页应用,那么就需要有一个URL对应到一个应用的状态,就需要有路由解决方案; 大规模的状态管理 ——当应用简单的时候,可能一个很基础的状态和界面映射可以解决问题,但是当应用变得很大,涉及多人协作的时候,就会涉及多个组件之间的共享、多个组件需要去改动同一份状态,以及如何使得这样大规模应用依然能够高效运行,这就涉及大规模状态管理的问题,当然也涉及到可维护性,还有构建工具。现在,如果放眼前端的未来,当HTTP2普及后,可能会带来构建工具的一次革命。但就目前而言,尤其是在中国的网络环境下,打包和工程构建依然是非常重要且不可避免的一个环节。
    5.2.2 渐进式框架Vue.jsVue.js现状
    以下数据可以体现出Vue.js的现状。

    前一段时间突破了三万星(如下图所示),总下载量过百万。

    官网上每个月的用户量为26万,这个应该是不包含中国区数据。官方开发者插件的周活跃用户数在5万5左右。这个数据是我觉得最有说服力的数据。安装并且使用开发者插件的Vue用户,应该会在实际生产中真正频繁使用Vue。
    Google搜索趋势的相关数据如下图所示。图中,绿色的是Backbone的数据,黄色是Ember,红色是React,蓝色是Vue。可以看出React和Vue近两年发展势头都比较迅猛。可以看出,Vue的曲线开始的是很早,2013年已经开始,但是有很长一段时间的增长是比较低的。因为在那一段时间我还在谷歌工作,Vue基本上是作为个人项目在运营。在过去一两年中,Vue获得了非常大的突破性发展。这个图里没有Angular,因为Angular的量还是非常大的,如果放进去就破表了。

    这些数据并不能绝对地代表框架当前的热度,但有一定的参考价值。可以看到React的势头很足。而由Vue的曲线还可以看出它的增长速度还在不停上扬。
    Vue的定位
    它与其他框架的区别就是渐进式的想法,也就是“Progressive”——这个词在英文中定义是渐进,一步一步,不是说你必须一竿子把所有的东西都用上。
    Vue的设计
    接下来我们回到之前看的图:

    Vue从设计角度来讲,虽然能够涵盖这张图上所有的东西,但是并不需要一上手就把所有东西全用上 ,因为没有必要。无论从学习角度,还是实际情况,这都是可选的。声明式渲染和组建系统是Vue的核心库所包含内容,而客户端路由、状态管理、构建工具都有专门解决方案。这些解决方案相互独立,你可以在核心的基础上任意选用其他的部件,不一定要全部整合在一起。
    6 系统演示电影订票网站首页

    会员注册

    会员登录

    查找电影
    6 评论 106 下载 2018-10-05 23:12:23 下载需要12点积分
  • 基于JAVA实现的游戏大厅

    一、概述服务器端分为五大部分:服务器的管理,用户信息管理和储存,游戏服务器的实现,大厅信息的更新与转发以及针对用户的线程管理。
    服务器的整体架构图

    游戏大厅可完成三人斗地主,双人五子棋对战,大厅内聊天等功能。
    二、服务器的管理首先进入服务器登陆界面,初始的账号和密码分别为admin和123456。文件存储在文件目录下的“log/administrator”,以“admin&123456”进行AES加密后进行base64加密储存。此文件必须保证存在且正确,若文件不存在或被修改,系统将不能登陆。
    2.1 菜单栏四个选项:

    开启服务器
    关闭服务器
    退出,以正常方式退出,在退出时会将内存中的用户数据放入磁盘中
    强制退出,有风险,可能会未正常保存用户数据,并且使客户端报错,用于紧急情况

    2.2 在线人员查看开启服务器后,客户端可进行连接,连接成功后,会更新表中数据。断开连接功能可单选或多选恶意人员进行强制中断连接。
    2.3 按钮功能共有五个按钮:

    刷新数据库:可让服务器强制进行一次数据刷新,将磁盘中的用户信息刷新
    显示/关闭游戏大厅:可打开大厅的监控界面
    与客户端不同的是,服务器会广播人员的上线信息,并显示各大厅的在线人数
    刷新公告: 公告的信息在“log/notice.html”进行修改,每次修改保存后可刷新服务器中的公告,随后改变后来新登陆的人的公告栏信息
    查询账号信息:可查询某个账号的个人信息
    显示/关闭监视器:系统运行的每个步骤都将记录到文档中,可查看系统正在运行中的日志系统,日志系统将保存到“log/activity”中

    其中监视器,用来显示系统进行中的操作,以及可以显示用户选择大厅信息,以及状态信息的更新,均会记录到文档中。磁盘中文件日志的储存,以单位日为时间轴进行。因为是实时更新的,可以防止系统因崩溃找不到日志文件,方便管理员进行对服务器的维护以及管理。
    以上为服务器主框架的大致介绍,服务器的功能还包括有定时刷新数据库,定时刷新重置次数,黑白名单(当客户端进行对客户端的非法操作时加入黑名单,白名单方便管理人员测试管理)等。
    三、用户信息管理和储存用户的信息储存在一颗B+树上,树的阶数视用户的估计数量而定,在测试中测试用户有十万个,所以采用的是万阶B+树,查找速度均在毫秒之内。B+树的插入与删除速度也均在毫秒之内。B+树的序列化采用将树和树上每一个叶节点通过ObjectOutputStream写入文本文档中。B+树上的每个叶节点有一个键值对存储着每一个Account信息。在找到用户对应的哈希值范围时,再对叶节点的键值对取出用户对象。在未来版本中,可以实现在服务器端修改用户的个人信息,volatile保证树是最新的树,将修改的数据马上刷新到主存。
    在存储用户信息的过程中,对用户的密码、密保问题、密保答案均进行AES加密,加密密钥为“log/aes.key”,只有密钥正确才能在初始化时读取到用户的正确信息。用户的管理和存储包括:

    注册:会修改树的信息,所以采用线程同步锁;每个人注册都会由系统分配一个对应的Id给用户,类似于QQ
    登陆:在登陆过程中因涉及敏感操作,所以在传输过程中,采用服务器发送公钥给客户端,加密后由服务器解密,防止用户信息的暴露在网络中
    注销:即设置在线状态为false,关闭连接与线程
    重置密码(将重置失败次数超过三次的用户禁掉,并每24小时自动刷新重置的次数)
    修改昵称头像等

    在用户使用此客户端时,能捕捉用户的异常输入与操作,并对用户发出信息,如收到的信息格式不符合要求,或者收到非法信息等。登陆成功后,即可进入大厅进行操作。
    四、大厅信息的更新大厅信息包括斗地主游戏大厅和五子棋游戏大厅的桌子状态以及更新、大厅聊天信息的更新、大厅公告栏的更新。
    桌子状态的更新采用TCP协议与每个用户建立连接,收取用户的动作状态,如坐下、准备、站起来,存储在对应的容器Vector中,并通过函数对在相同大厅的人进行转发,其他大厅或未选择大厅的用户将无法收取动作信息。每次用户选择大厅点击确认后将会收到全部桌子状态进行刷新。
    实现此功能的是Gobang_Tables.java和Landlord_Tables.java两个文件。当发送桌子的状态信息失败的时候(即网络阻塞或客户端异常断开时),程序会捕捉异常并及时把异常用户移出列表并关闭用户,防止二次异常。
    大厅聊天信息采用UDP协议接收用户发来的聊天信息,并可以显示在服务器的游戏大厅上并对所有在线的用户在进行转发。
    大厅公告栏通过发送html文本给客户端进行更新,html文件可在“log/notice.html”修改,不再重复叙述。
    五、针对用户的线程管理因为对每一个建立了TCP连接的客户,都会分配一个线程进行处理,所以将进行对用户的线程管理。
    首先设置对用户操作时间超时,测试设置了20分钟,当用户超过这个时间没有任何动作时,服务器便会自动断开用户的连接,防止内存过度消耗。
    同时,考虑到大用户量,便必然会引发并发问题,所以在服务器中,设置了阻塞队列,当服务器并发处理数量达到一定值,服务器便拒绝接收客户端的登陆,将优先处理前面的客户端。
    最后,对用户操作有异常超过两次的,会加入服务器黑名单列表。
    六、测试截图6.1 服务器测试截图





    6.2 客户端测试截图





    1 评论 4 下载 2019-05-12 17:53:20 下载需要13点积分
  • 基于Qt和Mysql的学生信息管理与收发系统

    哈哈哈,小白在学校期间的练手作品,很粗糙,很简陋,bug也有,但是对于新手来说还是很具有参考价值的,不喜勿喷,指出问题,共同进步。
    项目简介
    项目名称:学生信息管理与收发系统(客户端+服务器)-(学生端-服务器-教师端)
    使用工具:QT Creator 5.6 + Mysql5.6;
    使用技术:C/S(客户端-服务器)、TCP/IP(协议)、socket、多线程、数据库;
    项目描述:

    服务器:服务器监听一个IP地址,用来连接教师端和学生端,用于数据转发(eg:教师端发消息到服务器,在由服务器发消息到学生端)教师端:教师端的主要功能是选择需要发送的学生(可以发送给不在线学生),输入将要发送给一部分学生的表格名(标题),和1-8个字段名(不能重复,因为数据库中的字段名不能重复),在点击发送后由服务器转发给学生端。在学生端收到消息并且提交消息后可以查询学生的信息和提交的信息,还可以将数据表导出成xls文件。文件发送还没有完成0.0…..学生端:学生端可以编辑个人信息。学生端可以查询收到的并未提交的数据表并且提交信息。(可以收到离线信息)(在线学生收到消息提示后从数据库中查找教师端所发出的数据)(不在线学生在上线后从数据库中查找数据)。文件发送还没有完成0.0……
    注意事项:本系统只能用于局域网中的数据传输,并且由于本项目是在学校完成后并没有改动,所以服务器所监听的地址为我本身的地址,在下载后本系统是不可用的。还有就是数据库的问题,数据库是我在花钱买的一个远程服务器上搭建的,所以数据库也是不可用的。因此

    在拿到本系统的代码时应该修改IP地址(服务器-教师端-学生端)改为你所需要的在拿到本系统的代码时应该把我所发的数据库加入到你的数据库中,并且修改代码中跟数据库有关的代码

    服务器

    教师端

    学生端

    程序代码我就不贴了,自己下载看吧~
    0 评论 4 下载 2019-05-08 13:30:46 下载需要6点积分
  • 基于C#的对WORD文件修改的小系统

    一、背景亲戚在家乡的一家电影放映场工作,由于不太先进的设备,导致每一次打印机打出都号码都与座位号对应不上,所以就跟我聊,问能不能写一个方便一点的小系统。
    二、抽象设计2.1 选择编程语言这个小代码的难点是如何操作(WORD文本)以及如何方便安装使用,至于性能,可能不需要考虑的太多。由于之前写了两个JAVA与PHP的WEB网站,就想用WEB网站的形式。但是由于需要稳定服务器就被迫放弃了,因为没有必要。简单调研了一下,决定用C#编写一个单机版的客户端小程序。一是因为简单,二是因为c#对word的操作应该更好支持。
    2.2 是否选择数据库如上面所言,使用数据库还要安装,使用线上资源没必要,而且就算使用本地数据,由于操作可能需要一定的技术功底,所以也暂时放弃。
    2.3 程序设想一个单机版的可以通过word模板去生成对应的电影票信息的word文本的软件。

    并且要在每一步对应打上log,方便纠错与统计。
    三、难点解析c#直接使用net框架很方面的完成图形界面的编写以及按钮的颜色改变,按钮信息获取等,这些就不再阐述了,接下来我主要写一下关于如何使用c#操作word以及如何编写一个简单的日志类。

    已售出自动修改为红色且不再可选,点击座位号,确认可以生成对应word。
    3.1 操作word模板c#操作word的两种方式:
    3.1.1 使用 office.Core.dll 引用
    直接添加引用,搜索office就可以,它使用的是你电脑office上的dll引用,不过感觉上使用起来比较复杂。推荐第二种。
    3.1.2 使用 Aspose.WORD for net 进行操作Aspose 官网下载 此处需要google CSDN下载(破解版)。
    操作WORD:使用上面两种方式操作一个word文件都是很麻烦的,因为word和txt不同,它有很多编码,字号缩进等设置,要找到编辑的地方很麻烦。在查了一些资料以后,发现微软word有一个很好的功能书签,它可以理解为一个key,对应的value即为所需要编辑的word的地址位置。查看详细

    office 操作代码
    //赋值书签名oBookMark[0] = "movieName";oBookMark[1] = "pai";oBookMark[2] = "pai1";oBookMark[3] = "hao";oBookMark[4] = "hao1";doc = wordApp.Documents.Open("D://test//test.doc");//插入对应书签 对应值doc.Bookmarks.get_Item(ref oBookMark[0]).Range.Text = movieName;doc.Bookmarks.get_Item(ref oBookMark[1]).Range.Text = pai;doc.Bookmarks.get_Item(ref oBookMark[2]).Range.Text = pai;doc.Bookmarks.get_Item(ref oBookMark[3]).Range.Text = hao;doc.Bookmarks.get_Item(ref oBookMark[4]).Range.Text = hao;//另存为 savePath 路径doc.SaveAs2(savePath);TraceHelper.GetInstance().Info("已保存至" + savePath, "WordRW Function");doc.Close(ref unknow, ref unknow, ref unknow);wordApp.Documents.Save(ref unknow, ref unknow);wordApp.Quit(ref unknow, ref unknow, ref unknow);
    Aspose 操作代码
    //初始化信息doc = new Aspose.Words.Document("test.doc");DocumentBuilder bulider = new DocumentBuilder(doc);//移动并写对应信息bulider.MoveToBookmark(oBookMark[0]);bulider.Write(movieName);bulider.MoveToBookmark(oBookMark[1]);bulider.Write(pai);bulider.MoveToBookmark(oBookMark[2]);bulider.Write(pai);bulider.MoveToBookmark(oBookMark[3]);bulider.Write(hao);bulider.MoveToBookmark(oBookMark[4]);bulider.Write(hao);//doc 另存doc.Save(savePath);TraceHelper.GetInstance().Info("已保存至" + savePath, "WordRW Function");
    上述两种方式都可以做到我们想要的功能。

    3.2 日志类为了更好的调试代码,也为了保存一些有用的信息,所有日志是必不可少的。 已调研日志引用(EventLog Log4Net)但他们用起来都比较麻烦,因为他们都是为大型系统设计的,所以就用网上例子自己写了一个简单日志类(利用DEBUG)。
    TraceHelper.GetInstance().Info("程序开始...", "Main Function");
    1 评论 2 下载 2019-05-07 16:35:26 下载需要12点积分
  • X509证书读取与解析实验

    一、X509证书结构描述1.1 X509证书类型X.509证书有多种常用的文件扩展名,代表着不同形式的数据编码以及内容
    其中常见的有(来自 Wikipedia ):

    .pem
    隐私增强型电子邮件 ,DER编码的证书再进行Base64编码的数据存放在”——-BEGIN CERTIFICATE——-“和”——-END CERTIFICATE——-“之中。

    .cer, .crt, .der – 通常是DER二进制格式的,但Base64编码后也很常见。
    .p7b, .p7c – PKCS#7 SignedData structure without data, just certificate(s) or CRL(s)
    .p12 – PKCS#12格式,包含证书的同时可能还有带密码保护的私钥
    .pfx – PFX,PKCS#12之前的格式(通常用PKCS#12格式,比如那些由IIS产生的PFX文件)

    1.2 X509证书的结构整体数据结构如下图所示:

    1.2.1 X509证书基本部分
    版本号:
    标识证书的版本(版本1、版本2或是版本3)
    序列号:
    标识证书的唯一整数,由证书颁发者分配的本证书的唯一标识符
    签名算法:
    用于签证书的算法标识,由对象标识符加上相关的参数组成,用于说明本证书所用的数字签名算法
    颁发者:
    证书颁发者的可识别名(DN)
    证书有效期 :
    证书有效期的时间段。本字段由”Not Before”和”Not After”两项组成,意义为:此日期前无效 - 此日期后无效。它们分别由UTC时间或一般的时间表示(在RFC2459中有详细的时间表示规则)。
    主体:
    证书拥有者的可识别名,这个字段必须是非空的,除非你在证书扩展中有别名。
    主体公钥信息

    公钥算法主体公钥
    颁发者唯一标识符(可选项)
    标识符—证书颁发者的唯一标识符,仅在版本2和版本3中有要求,属于可选项。
    主体唯一身份信息(可选项)
    证书拥有者的唯一标识符,仅在版本2和版本3中有要求,属于可选项。

    1.2.2 X509证书拓展部分
    发行者密钥标识符
    密钥使用
    CRL分布点
    私钥的使用期
    证书策略
    策略映射
    主体别名
    颁发者别名
    主体目录属性

    二、数据结构这次我们主要进行解析的是X509证书的基本部分。包括版本号、序列号、颁发者详情、证书有效期、证书主体、签名算法、签名信息和公钥。
    利用FileInputStream 读取文件流,
    然后使用 java.security 的库对文件流进行解析,生成证书。
    import java.security.*;import java.io.*;import java.security.cert.*;import java.security.cert.Certificate;CertificateFactory cf;// 获取工厂实例CertificateFactory cf = CertificateFactory.getInstance("X.509");// 用文件流读入证书FileInputStream fis = new FileInputStream(fileLocation);// 生成证书Certificate c = cf.generateCertificate(fis);X509Certificate t = (X509Certificate)c;fis.close();
    解析证书后,获取库中 X509Certificate 类的关键成员信息,其中包括版本号、序列号、颁发者详情、证书有效期、证书主体、签名算法、签名信息和公钥等,然后在终端上输出结果。
    System.out.println("版本号: " + t.getVersion());System.out.println("序列号: " + t.getSerialNumber().toString(16));System.out.println("颁发者部分: ");String issuerDN = t.getIssuerDN().toString();String[] issuerInfo = issuerDN.split(",");judge = false;for(int i = 0; i < issuerInfo.length; i++) { if(issuerInfo[i].contains("\"")) { if(judge) System.out.println("," + issuerInfo[i]); else { int index = issuerInfo[i].indexOf("="); String key = issuerInfo[i].substring(0, index).replaceAll(" ", ""); String value = issuerInfo[i].substring(index+1); System.out.print(" [" + key + "]: " + value); } judge = !judge; continue; } int index = issuerInfo[i].indexOf("="); String key = issuerInfo[i].substring(0, index).replaceAll(" ", ""); String value = issuerInfo[i].substring(index+1); System.out.println(" [" + key + "]: " + value);}System.out.println("有效起始日期: " + t.getNotBefore());System.out.println("有效终止日期: " + t.getNotAfter());System.out.println("主体部分: ");String[] subjectInfo = t.getSubjectDN().toString().split(",");judge =false;for(int i = 0; i < subjectInfo.length; i++) { if(subjectInfo[i].contains("\"")) { if(judge) System.out.println("," + subjectInfo[i]); else { int index = subjectInfo[i].indexOf("="); String key = subjectInfo[i].substring(0, index).replaceAll(" ", ""); String value = subjectInfo[i].substring(index+1); System.out.print(" [" + key + "]: " + value); } judge = !judge; continue; } int index = subjectInfo[i].indexOf("="); String key = subjectInfo[i].substring(0, index).replaceAll(" ", ""); String value = subjectInfo[i].substring(index+1); System.out.println(" [" + key + "]: " + value);}System.out.println("签名算法: " + t.getSigAlgName());System.out.println("签名: " + t.getSignature().toString());System.out.println("公钥: ");PublicKey pk = t.getPublicKey();String pkStr = pk.toString();String[] pkInfo = pkStr.split("\n");for(int i = 0; i < pkInfo.length; i++) System.out.println(" " + pkInfo[i].trim());
    三、java语言源代码源码已经在上传作业包下的 src 文件夹内
    四、编译运行结果运行环境:Win 10
    示例:github.com.cer
    $ javac X509.java$ java X509 ../resource/github.com.cer../resource/github.com.cer版本号: 3序列号: a0630427f5bbced6957396593b6451f颁发者部分: [CN]: DigiCert SHA2 Extended Validation Server CA [OU]: www.digicert.com [O]: DigiCert Inc [C]: US有效起始日期: Tue May 08 08:00:00 CST 2018有效终止日期: Wed Jun 03 20:00:00 CST 2020主体部分: [CN]: github.com [O]: "GitHub, Inc." [L]: San Francisco [ST]: California [C]: US [SERIALNUMBER]: 5157550 [OID.1.3.6.1.4.1.311.60.2.1.2]: Delaware [OID.1.3.6.1.4.1.311.60.2.1.3]: US [OID.2.5.4.15]: Private Organization签名算法: SHA256withRSA签名: [B@42d80b78公钥: Sun RSA public key, 2048 bits modulus: 25025100770112519133826373044337089322469791879220152213643221754976969243477927257227415181039366015609149001175458675631697702239034823716334509809784926406937227125890521894087124165874208928008511527244368706849310092475511259401776633076671238008575313180508596720476568749022900129891932143823778833404532554658303977351639532131107111874168053266560861447299063764771943313867349795002140249378509492410727023509926138655327290063258841194245159501288231930813126290727910076185376418441777313922434226388044466254908262848472806237246586907086347793775219421137443851512766147228992395134669954845000049168203 public exponent: 65537
    截图结果:

    其余证书实验截图:

    google.com.cer


    microsoft.com.cer


    csdn.net.cer


    baidu.com.cer
    0 评论 1 下载 2019-05-06 15:44:45 下载需要8点积分
  • 基于java的SM3算法

    一、SM3算法介绍SM3是国家密码管理局编制的商用算法,它是一种杂凑算法,可以应用于数字签名、验证等密码应用中。其计算方法、计算步骤和运算实例可以在国家商用密码管理办公室官网查看。
    该算法的输入是一个长度 L 比特的消息m,其中 L < 2^64 ,经过填充、迭代压缩后,生成一个256比特的输出。
    二、算法步骤2.1 填充长度假设消息m 的长度为 L 比特。首先将比特“1”添加到消息的末尾,再添加k 个“0”, k是满足L + 1 + k ≡ 448 mod 512 的最小的非负整数。然后再添加一个64位比特串,该比特串是长度L的二进制表示。填充后的消息m′的比特长度为512的倍数。
    在具体的实现过程中,首先获取消息超过512比特整数倍部分的长度L。由于在最后一个分组分组中,要将1个比特位“1”添加到消息的末尾,并且要添加64比特来存储消息的长度。
    当 L <= 512-(64+1)时,可以直接填充比特位“1”、 512-(64+1)个比特位“0”、64位的消息长度,;当 L > 512-(64+1)时,最后一个512比特的分组不够填充,需要再添加一个512位的分组,此时填充的“0”的个数为k=512-L-1+(512-64)。
    2.2 迭代压缩在迭代的过程中,首先对填充后的消息m′按512比特进行分组。然后对每一个分组进行迭代压缩。迭代方式如下:
    FOR i=0 TO n-1 V (i+1) = CF (V (i) , B (i) )ENDFOR上述算法中,n是填充后消息分组的个数,即有多少个消息分组,就迭代多少次。Vi是256位的向量,V0为初始值IV,即前一个分组计算完后的结果Vi会当作下一个分组的参数传入CF函数中,此即是密码学中扩散原则,即原始消息的任意比特位的变化都会造成结果产生大的改变。
    在CF压缩函数中,需要用到的参数有向量V (i)、B(i)、常量Tj、Wj和Wj′。其中Wj和Wj′是对512比特的消息分组进行扩展后产生的132个字。由于消息分组有多个,Wj和Wj′也对应有多个。在具体实现时,要在CF函数中对每一个消息分组进行消息扩展计算。
    在迭代完最后一个消息分组后,CF函数返回的值Vn就是最终的计算结果。
    三、实现过程3.1 创建项目打开Eclipse创建项目SM3,在项目SM3中创建类SM3。创建完成后目录结构如下所示:

    3.2 定义算法中的常量、函数算法中需要用到函数FFj、GGj、P0、P1、常量Tj等,以及原始消息、填充后的消息定义如下:
    // 字符集private String charset = "ISO-8859-1";// 要哈希的字符串private String message = "abc";// 填充后的字符串private String PaddingMessage;// 获取常量T0和T1private int T(int j){ if(j <= 15){ return 0x79cc4519; }else{ return 0x7a879d8a; }}// 布尔函数 FFprivate int FF(int X, int Y, int Z, int j){ int result = 0; if(j >= 0 &&j <= 15) { result = X ^ Y ^ Z; }else if(j >= 16 && j <= 63) { result = (X & Y) | (X & Z) | (Y & Z); } return result; }// 布尔函数GGprivate int GG(int X, int Y, int Z, int j){ int result = 0; if(j >= 0 &&j <= 15) { result = X ^ Y ^ Z; }else { result = (X & Y) | (~X & Z); } return result;}// 置换函数P0private int P0(int X){ return X ^ (CircleLeftShift(X, 9)) ^ CircleLeftShift(X, 17);}// 置换函数P1private int P1(int X){ return X ^ (CircleLeftShift(X, 15)) ^ CircleLeftShift(X, 23);}
    在上述函数定义中,用到的CircleLeftShift函数用于实现循环左移,它的两个参数分别是要移位的32位int型数据和循环左移的位数。在循环左移中,循环左移k位,相当于将二进制位最左边的k位移动到最右边。
    3.2 调试方法编写在课本的运算示例中,每一步运算的中间结果都有。在编写算法时,每写一步都要与课本上的中间结果对照,以确定当前得到的中间结果是否正确。由于算法运行的中间结果都是二进制形式,为方便查看,编写了dump方法用于将中间结果显示为16进制的形式。如将填充后的消息打印出来的dump方法如下:
    private void dump() { System.out.println("========开始打印========"); try{ byte bts[] = this.PaddingMessage.getBytes(this.charset); for(int i = 0; i < bts.length; i ++) { if(i%16 != 0 && i%2 == 0 && i != 0){ System.out.print(" "); } if(i%16 == 0 && i != 0){ System.out.println(); } System.out.printf("%02x", bts[i]); } }catch(Exception e){ System.out.println("Error Catch"); } System.out.println("\n========结束打印========");}
    在输入消息为“abc”的情况下,打印填充后的消息的十六进制形式如下所示:

    其中开头的61、62、63是字母a、b、c对应的ASCII码,80是填充消息时附加的比特位1,该比特位与后面填充的比特位0,构成了二进制1000 0000,所以对应的十六进制是80。最后的18也是十六进制形式,对应的十进制是24,表示消息的长度是24位。
    由于在使用Java编写SM3算法时,计算的中间结果有字符串、整型数组等多种类型,为方便查看对应数据的十六进制形式,编写了多个dump方法,用于打印各种类型的数据。
    /* 将字符串输出为16进制形式 */private static void dump(String str)/* 将整型数组输出为16进制形式 */private static void dump(int nums[])
    3.3 遇到的错误及解决方案3.3.1 循环左移计算结果偶尔不正确在Java中,只有按位左移<<操作符,按位左移溢出的比特位直接丢弃,而SM3算法需要的循环左移需要将溢出的比特位存储到操作数最右边。
    在实现循环左移时,假设要移位的32位比特位的数据为Y,则循环左移位可以分为三步:

    把Y按位左移k位的值赋值为l,此时l最右边的k位为0;
    把Y按位右移(32-k)位的值赋值为r,此时r左边的(32-k)位为0;
    将l和r进行按位或运算,即得到循环左移后的结果。

    例如将0x1234 5678按位左移8位,则Y左移8位得到 l=0x3456 7800,Y右移32-8=24位得到r=0x0000 0012,最后将l和r进行按位或运算得到最终结果0x34567812。
    循环左移的实现过程如下:
    // 将x循环左移N位 private static int CircleLeftShift(int x, int N) { return (x << N) | (x >> (32 - N)); }
    在使用此方法进行按位左移时,发现偶尔计算出来的结果与预期不符合。经过调试,发现是在按位右移时没有得到预期的结果,导致最终循环左移结果出错。具体原因及分析如下:
    按位左移是直接在右边补0,而按位右移分为两种情况,一种是逻辑右移(有符号移位),一种是算术右移(无符号移位)。
    逻辑右移是当最高位为0是,说明这是一个正数,右移时在最左边补0;当最高位为1时,说明这是一个负数,负数在计算机中以补码形式存储,所以逻辑右移时在最左边补1。
    而算术左移在移位时忽略符号位,即无论最高位是0还是1,都往最左边补0。
    在SM3算法中,需要的是算术右移。而在Java的语法中,>>是逻辑右移,>>>是算术右移。最初使用逻辑右移,导致循环左移最高位为1的数时运算结果与期望值不符。修改后的循环左移方法如下:
    // 将x循环左移N位 private static int CircleLeftShift(int x, int N) { return (x << N) | (x >>> (32 - N)); }
    3.3.2 填充消息时附加比特位1结果不对根据算法的计算步骤,填充消息时,首先在消息后面附上一个比特位1。在实现算法时,由于用户输入的都是以字节为单位的字符串,所以1之后填充的0的个数k肯定是符合7+8*Z的,其中Z为非负整数。所以可以附加一个比特位“1”的操作可以转化为附加二进制1000 0000。实现代码如下:
    padding += (byte)0x80; // 先填充一个“1000 0000”其中padding是一个字符串类型的数据,用于存储附加的数据。填充完比特位1、k个0以及消息长度后,将填充后的消息打印出来,如下:

    测试时输入的消息依旧是abc,理论上得到的是6162 6380 0000 0000 .....,将消息c即63后的十六进制位2d 3132 38与ASCII表对照,发现是-128。原因很明确,byte类型的0x80表示的数正是-128。而将-128与字符串padding进行 +=操作时,byte类型的数据被转换成字符串-128,所以得到上图的结果。
    既然不能直接将byte类型的数据与字符串相连接,那可以尝试使用new String(byte[] bytes[])方法将一个byte数组转换成字符串。修改代码如下:
    byte a[] = { (byte) 0x80 }; padding += new String(a); // 先填充一个“1000 0000”
    再次运行后结果还是不正确,如下图所示:

    结果显示原本的0x80变成了0x3f。经过测试发现,0x01最后会得到0x01,0x02会得到0x02,0x7f也会得到0x7f,只有当大于0x7f是结果才会不正确,而且得到的都是0x3f。
    在网上搜索之后,得出错误的原因:ASCII是每个字节对应一个字符,一个字节的表示范围是-128~127,而ASCII只对0~127这个范围进行了编码。也就是每个字节最大值是0x7f,用二进制表示就是最高位为0。上面的0x80的二进制位是1000 0000,最高位是1,不在ASCII编码的范围之内。Java使用的是Unicode字符集,当进行将0x80转换成字符时,Java在Unicode代码页中查询不到对应的字符,Java会默认返回一个0x3f。所以上面试验中,小于0x80的byte可以正确转换成字符串,而大于等于0x80的byte数据将会返回0x3f.
    解决方法是将byte数组转化成字符串时设置编码为“ISO-8859-1”。ISO-8859-1是按字节编码的,并且它对0~255的空间都进行了编码,所在在转换时它能够正确的将0x80转换为字符串。实现代码如下:
    byte a[] = {(byte)0x80};padding += new String(a, charset);
    其中第二个参数charset是在最前面定义的字符集,它是一个字符串“ISO-8859-1”。再次运行并打印填充后的消息,发现结果跟预期一致:

    3.3.3 迭代时结果出错在进行迭代的时候,导出了迭代前的数据,包括Wj,Wj′等,都与课本上的示例一样。说明迭代前的步骤已经正确的完成。迭代后的结果却不正确,说明错误出现在迭代这里。将中间结果ABCDEFGH导出后与课本上的对照,发现最开始出错的位置是G0,而G0之前的A-F都是正确的。课本上的:

    实验中的:

    查看课本上的算法G赋值的位置,算法如下:
    G ← F <<< 9此时怀疑移位算法是否编写正确,经过手算移位算法后,发现结果和实验中的显示的一样。对照国家密码管理局发布《SM3密码杂凑算法》发现,课本上的算法不对,正确的移位数应该是19,即:
    G ← F <<< 19修改为正确的移位数后再次运行,发现结果与课本上的一致,SM3密码算法完成。哈希结果如下:
    66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0
    1 评论 1 下载 2019-05-04 16:08:18 下载需要8点积分
  • 基于python的自动续借图书集


    Python 2.7
    IDE Pycharm 5.0.3
    Firefox浏览器:47.0.1

    目的自动实现图书馆借书籍的书单截图,并一键续约全部书籍,我登录校图书馆的目的无非就这两个咯,我才不去预约没有的书呢—反正没有一次预约成功过0.0
    实现方法Selenium+PhantonJS自动化脚本执行
    实现方案
    采用Firefox浏览器进行模拟登录,这个比较酷炫把,可以看着浏览器自己在那边跑,欢快的停不下来。。。
    调用PhantomJS.exe,不展现浏览器的运作,直接在cmd窗口跑(用pyinstaller打包成exe后有cmd窗)

    方案实现过程采用Selenium+Firefox方式:
    先来个最后成品动图:

    然后来程序代码—主模块(被调用模块,也可单独执行)
    # -*- coding: utf-8 -*-from selenium import webdriverimport time#shift-tab多行缩进(左)print 'please wait...system loading...'#reload(sys)PostUrl = "http://lib.hrbeu.edu.cn/#"driver=webdriver.Firefox()#用浏览器实现访问#driver = webdriver.PhantomJS(executable_path="phantomjs.exe")#没用浏览器driver.get(PostUrl)elem_user = driver.find_element_by_name('number')elem_psw = driver.find_element_by_name('passwd')#选择我的图书馆,点击后才能看到输入账号密码click_first = driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]")click_first.click()elem_user.send_keys('S315080092')elem_psw.send_keys('xxxxxxxxx')#点击登录click_second = driver.find_element_by_name('submit')click_second.click()print 'log in...'time.sleep(1)#定位新页面元素,将handle重定位即可driver.switch_to_window(driver.window_handles[1])#定位弹出的第一个页面,也就是当前页面#sreach_window = driver.current_window_handle #此行代码用来定位当前页面#不可行driver.find_element_by_xpath("/html/body/div[4]/div/div/ul/li[3]/a").click()driver.save_screenshot('image_booklist_firefox.jpg')print 'turning to the mylib...'time.sleep(1)#搜索结果页面停留片刻#driver.switch_to_window(driver.window_handles[1])#没有跳出新窗口就是在同一页面的!for i in range(2,30):#这里限定是29本书,一般我们都不会借那么多书的 try: #driver.find_element_by_xpath("/html/body/div[4]/div/div[2]/table/tbody/%s/td[8]/div/input"%('tr[%s]'%i)).click()#下面的比较好理解 driver.find_element_by_xpath("/html/body/div[4]/div/div[2]/table/tbody/tr[%s]/td[8]/div/input"%i).click() print 'renewing...the %d\'th book renewed '%(i-1) except: print '%d books have been renewed !'%(i-2) a=i-2 time.sleep(4) driver.save_screenshot('image_done_firefox.jpg') print 'the picture is saving...' print 'done!' breaktime.sleep(1)driver.close()driver.quit()
    调用上述模块的主执行函数(其实就是为了封装上述模块而已,封装成gui界面,为后续的打包做准备)
    # -*- coding: utf-8 -*-from Tkinter import *import tkMessageBox#执行gui窗import timedef check_renew(): print 'checking and renewing...' tkMessageBox.showinfo('提示','即将开启装逼模式,请确认已安装Firefox浏览器') #time.sleep(4) import Selenium_PhantomJS_lib_firefox tkMessageBox.showinfo('提示','已执行成功!\n(截图已保存于程序目录)')#主框架部分root = Tk()root.title('图书馆查询续约(哈尔滨工程大学专版))label=Label(root,text=' 图书馆一键查询与续约Firefox版本 (✪ω✪) ')button_check=Button(root,text='查询书单并续期━Σ(゚Д゚|||)━开启Firefox有形装逼模式 ',background='green',command=check_renew)label.pack()button_check.pack()root.mainloop()
    实现效果如图所示:

    程序中的注释相信可以把程序解释的差不多了把。。。。
    遇到问题和解决方案
    selenium对新页面元素无法定位抛出NoSuchElementException: Message: Unable to locate element
    错误,导致无法进行对新的界面进行点击操作。
    解决方案:专门写了一篇博客,请见
    解决Selenium弹出新页面无法定位元素问题(Unable to locate element)

    对打包后的版本无法运行,抛出如图错误Errno 10054

    解决方案:暂未找到解决方案,exe文件不可用,程序执行可用

    对未知书籍数目重复点击操作,代码冗余
    解决方案:因为点击续借按钮的元素每个都不一样,通过观察可知其中的规律,之后就知道在那进行修改,但是,光修改的话,十本书就有十个相似的代码串,很不pythontic,所以,采用格式化字符串的方式进行for循环带入,方便又漂亮!

    使用了1中的解决方案还是不能定位元素
    可能查找元素的方式出现错误,我现在的使用方法是采用xpath的方式来找,比如说这样
    driver.find_element_by_xpath("/html/body/div[4]/div/div/ul/li[3]/a")虽然看起来有点长,但是元素相当好找,而且定位很准,如果采用类似这种driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]"),我现在还不能很好地驾驭,出错可能性有点大,下次要多进行尝试。
    接下来实现方案二的构思:调用PhantomJS.exe,不展现浏览器的运作,直接在cmd窗口跑(用pyinstaller打包成exe后有cmd窗)
    方案实现过程1. 效果
    2. 代码
    被调模块(可单独执行)

    # -*- coding: utf-8 -*-from selenium import webdriverimport timeimport sysfrom PIL import Image#shift-tab多行缩进(左)print 'please wait...system loading...'reload(sys)PostUrl = "http://lib.hrbeu.edu.cn/#"driver = webdriver.PhantomJS(executable_path="phantomjs.exe")#没用浏览器driver.get(PostUrl)elem_user = driver.find_element_by_name('number')elem_psw = driver.find_element_by_name('passwd')#选择我的图书馆,点击后才能看到输入账号密码click_first = driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]")click_first.click()elem_user.send_keys('S315080092')elem_psw.send_keys('xxxxxxxx')#点击登录click_second = driver.find_element_by_name('submit')click_second.click()print 'log in...'time.sleep(1)#定位新页面元素,将handle重定位即可driver.switch_to_window(driver.window_handles[1])#定位弹出的第一个页面,也就是当前页面driver.find_element_by_xpath("/html/body/div[4]/div/div/ul/li[3]/a").click()driver.save_screenshot('image_booklist.jpg')print 'turning to the mylib...'time.sleep(1)#搜索结果页面停留片刻#driver.switch_to_window(driver.window_handles[1])#没有跳出新窗口就是在同一页面的!for i in range(2,30):#这里限定是29本书,一般我们都不会借那么多书的 try: driver.find_element_by_xpath("/html/body/div[4]/div/div[2]/table/tbody/%s/td[8]/div/input"%('tr[%s]'%i)).click() print 'renewing...the %d\'th book renewed '%(i-1) except: print '%d books have been renewed !'%(i-2) a=i-2 time.sleep(4) driver.save_screenshot('image_done.jpg') print 'the picture is opening...please wait...' breaktime.sleep(2)driver.close()driver.quit()def show_img(): im_check=Image.open('image_booklist.jpg') im_check.show() im_done =Image.open('image_done.jpg') im_done.show()

    然后是程序入口

    # -*- coding: utf-8 -*-from Tkinter import *import tkMessageBoxdef check_renew(): print 'checking and renewing...' tkMessageBox.showinfo('提示','执行速度取决于网速和电脑,能等着就按"确定"\n(请允许phantomjs.exe访问网络)\nBTW 你现在按啥都不好使,程序照样执行(*゜Д゜)σ凸') from Selenium_PhantomJS_lib import show_img show_img()#show一下预约前和预约后截图,好确认 tkMessageBox.showinfo('提示','已执行成功!\n(若没有弹出图片则请自行打开程序目录)')#主框架部分root = Tk()root.title('图书馆查询续约(哈尔滨工程大学专版)--by 哈士奇说喵')label=Label(root,text=' 图书馆一键查询与续约cmd版本 (✪ω✪) ')button_check=Button(root,text='查询书单并续期━Σ(゚Д゚|||)━开启cmd无形装逼模式 ',background='green',command=check_renew)label.pack()button_check.pack()root.mainloop()

    之后启动的画面应该是这样的



    最后完成的画面应该是这样的,截图,确认框,cmd窗口,一个都不少;


    原理和上面并没有什么区别,只是调用了一个phantomjs.exe文件而已,实际上的处理都是这个exe在进行处理的,所以,在进行打包的时候,打包出来的exe需要和此文件在一个文件夹下才可以,就像这样

    遇到问题和解决方案
    找不到执行文件,phantomjs.exe
    解决方案:把phantomjs.exe添加到工作路径下,最方便的方法就是,你的工程在哪,直接添加到工程文件夹下就可以了

    截图的图片没有显示出来,或者提示”在禁用UAC时无法激活此应用“
    解决方案:图片有没有显示,可以看有没有调用show方法,如果调用了,那在自己电脑测试肯定是没有问题的,我在测试别的电脑的时候遇到UAC问题,直接启用就可以了,一般没有问题的,如果不想麻烦启动,那就直接去工作文件夹下手动打开看,截图已保存在本地的工作路径下的。
    最后这个程序是可以打包在别的电脑进行运行的,不过账号和密码我都直接输在程序里面了,而且也只是我自己学校的专版,主要还是自己用,如果有哈尔滨工程大学的小伙伴想用,你只要自己改个账号密码参数就可以了,前提是你有完整的python开发环境。
    1 评论 2 下载 2019-05-03 20:27:55 下载需要11点积分
  • 基于Python的有道翻译小软件


    Python 2.7.13
    IDE Pycharm 5.0.3
    macOS 10.12.1

    前言
    花了一点时间,半抄半写半修改的写了第一个能用的python小程序,作用是在IDE端模拟有道词典的访问,效果如下图所示,不足之处在于,当输入的中英文字符串超过一定数量,会抛出中间代码,新手并不知道怎么处理,望知道的不吝赐教

    初阶:交互界面
    首先在jupyter或者pycharm中进行交互的操作,核心语句是使用raw_input捕获系统输入

    1. 效果图
    2. 代码# -*- coding: utf-8 -*-import urllib2import urllib # python2.7才需要两个urllibimport jsonwhile True: content = raw_input("请输入需要翻译的内容:") # 系统捕获输入,就是命令框会弹出提示,需要你进行手动输入 if content == 'q': # 输入q退出while循环 break url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" data = {} # 构造data,里面构造参数传入 data['type'] = 'AUTO' data['i']=content data['doctype'] = 'json' data['xmlVersion'] = '1.8' data['keyfrom'] = 'fanyi.web' data['ue'] = 'UTF-8' data['action'] = 'FY_BY_ENTER' data['typoResult'] = 'true' data = urllib.urlencode(data).encode('utf-8') # 将构造的data编码 req = urllib2.Request(url) # 向浏览器发出请求 response = urllib2.urlopen(req, data) # 带参请求,返回执行结果 html = response.read().decode('utf-8') # print(html) # 可以取消print的注释,查看其中效果,这边获取的结果是进行解析 target = json.loads(html) # 以json形式载入获取到的html字符串 print "翻译的内容是:"+target['translateResult'][0][0]['tgt'].encode('utf-8')# 请输入需要翻译的内容:test# 翻译的内容是:测试# 请输入需要翻译的内容:测试# 翻译的内容是:test# 请输入需要翻译的内容:q
    注意:这里的data字典中的数据根据实际网页中数据为准,可能会不一样,具体操作,点击审查元素。
    进阶:做成gui
    离实用还差那么两步,第一步是先做成GUI

    1. 界面效果
    2. 代码# -*- coding: utf-8 -*-from Tkinter import *import difflibimport urllib2import urllib # python2.7才需要两个urllibimport json# ----------------------主框架部分----------------------root = Tk()root.title('翻译GUI&beta1')root.geometry()Label_root=Label(root)#-----------------------定义规则------------------------def translate(content): url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" data = {} # 构造data,里面构造参数传入 data['type'] = 'AUTO' data['i']=content data['doctype'] = 'json' data['xmlVersion'] = '1.8' data['keyfrom'] = 'fanyi.web' data['ue'] = 'UTF-8' data['action'] = 'FY_BY_ENTER' data['typoResult'] = 'true' data = urllib.urlencode(data).encode('utf-8') # 将构造的data编码 req = urllib2.Request(url) # 向浏览器发出请求 response = urllib2.urlopen(req, data) # 带参请求,返回执行结果 html = response.read().decode('utf-8') # print(html) # 可以取消print的注释,查看其中效果,这边获取的结果是进行解析 target = json.loads(html) # 以json形式载入获取到的html字符串 #print u"翻译的内容是:"+target['translateResult'][0][0]['tgt'] return target['translateResult'][0][0]['tgt'].encode('utf-8')#还可以继续增加规则函数,只要是两输入的参数都可以#----------------------触发函数-----------------------def Answ():# 规则函数 Ans.insert(END,"翻译 %s: "%var_first.get().encode('utf-8') + translate(var_first.get().encode('utf-8')))def Clea():#清空函数 input_num_first.delete(0,END)#这里entry的delect用0 Ans.delete(0,END)#text中的用0.0#----------------------输入选择框架--------------------frame_input = Frame(root)Label_input=Label(frame_input, text='请输入需要翻译的内容', font=('',15))var_first = StringVar()input_num_first = Entry(frame_input, textvariable=var_first)#---------------------计算结果框架---------------------frame_output = Frame(root)Label_output=Label(frame_output, font=('',15))Ans = Listbox(frame_output, height=5,width=30) #text也可以,Listbox好处在于换行#-----------------------Button-----------------------calc = Button(frame_output,text='翻译', command=Answ)cle = Button(frame_output,text='清空', command=Clea)Label_root.pack(side=TOP)frame_input.pack(side=TOP)Label_input.pack(side=LEFT)input_num_first.pack(side=LEFT)frame_output.pack(side=TOP)Label_output.pack(side=LEFT)calc.pack(side=LEFT)cle.pack(side=LEFT)Ans.pack(side=LEFT)#-------------------root.mainloop()------------------root.mainloop()
    高阶:发布应用
    如何在小伙伴面前装B才是我学习的动力,哈哈哈

    Pay Attention
    python3的用户注意url包的使用和python2是有区别的,请根据实际需求自行百度
    Python如果操作频率太快或者网页限制机器人对此的访问,则需要修改head了,修改代码后.当然每个电脑的user都不一样,具体去审查元素查看。

    req = urllib2.Request(url) # 生成对象# 添加如下一行代码;req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'),这样就可以伪装成人类啦
    当然也可以添加延时模块, 即可限定访问时间。
    import time #添加延时模块time.sleep(1)#休息1秒钟再进行操作
    python3的同学需要Tkinter改成小写,还有就是注意编码部分的转化。
    mac的同学可能遇到tkinter无法输入中文问题,可能是由tkinter版本过低导致,解决方案参考:MAC 系统中,Tkinter 无法用 中文输入法 输入中文
    1 评论 1 下载 2019-05-02 20:41:30 下载需要9点积分
  • 基于python的验证码自动识别

    Python+Selenium+PIL+Tesseract真正自动识别验证码进行一键登录

    Python 2.7
    IDE Pycharm 5.0.3
    Firefox浏览器:47.0.1
    Selenium:Selenium的介绍及使用,强烈推荐@ Eastmount的博客
    PIL : Pillow-3.3.0-cp27-cp27m-win_amd64.whl PIL第三方库的下载,win下安装whl文件
    Pytesser:依赖于PIL ,Tesseract 了解pytesser及基本使用
    Tesseract:3.0.2 tesseract下载及安装

    前言
    自动登陆时候遇到验证码,采用Tesseract+PIL进行识别和自动填充,不让验证码成为我们自动化登录的阻碍,哈哈哈

    Talk is cheap, Show me the code
    自动识别验证码模拟登陆,注意是自动,一键登录,不是那种扫出验证码,然后手动输入登录!首先来代码实现吧!

    # -*- coding: utf-8 -*-from selenium import webdriverimport osimport pytesserimport sys,timefrom PIL import Image,ImageEnhance#shift+tab多行缩进(左)reload(sys)PostUrl = "http://yjsymis.hrbeu.edu.cn/gsmis/indexAction.do"driver=webdriver.Firefox()driver.get(PostUrl)i=0while 1:#sb登录系统,即使输对所有消息还是登不进去的,需要登录两次及以上 i=i+1 try: elem_user = driver.find_element_by_name('id') elem_psw = driver.find_element_by_name('password') elem_code = driver.find_element_by_name('checkcode') except: break #-------------------对验证码进行区域截图,好吧,这方法有点low------------------ driver.get_screenshot_as_file('C:\Users\MrLevo\image1.jpg')#比较好理解 im =Image.open('C:\Users\MrLevo\image1.jpg') box = (516,417,564,437) #设置要裁剪的区域 region = im.crop(box) #此时,region是一个新的图像对象。 #region.show()#显示的话就会被占用,所以要注释掉 region.save("e:/image_code.jpg") #------------------------------------------------------------------- #--------------ImageGrab.grab()直接可以区域截图,但是有bug,截图不全------- ''' bbox = (780, 0, 1020, 800) img = ImageGrab.grab() img.save("E:\image_code.jpg") img.show() ''' #-------------------------手动输入验证码:适用范围更广,但不够方便------------------------------ ''' response = opener.open(CaptchaUrl) picture = response.read() with open('e:/image.jpg', 'wb') as local: local.write(picture) # 保存验证码到本地 #------------对于不能用pytesser+ocr进行识别,手动打开图片手动输入-------- # 打开保存的验证码图片 输入 #SecretCode = raw_input('please enter the code: ') #---------------------------------------------------------------------- ''' #--------------------图片增强+自动识别简单验证码----------------------------- #time.sleep(3)防止由于网速,可能图片还没保存好,就开始识别 def image_file_to_string(file): cwd = os.getcwd() try : os.chdir("C:\Users\MrLevo\Anaconda2\Lib") return pytesser.image_file_to_string(file) finally: os.chdir(cwd) im=Image.open("E:\\image_code.jpg") imgry = im.convert('L')#图像加强,二值化 sharpness =ImageEnhance.Contrast(imgry)#对比度增强 sharp_img = sharpness.enhance(2.0) sharp_img.save("E:\\image_code.jpg") #http://www.cnblogs.com/txw1958/archive/2012/02/21/2361330.html #imgry.show()#这是分布测试时候用的,整个程序使用需要注释掉 #imgry.save("E:\\image_code.jpg") code= pytesser.image_file_to_string("E:\\image_code.jpg")#code即为识别出的图片数字str类型 print code #打印code观察是否识别正确 #---------------------------------------------------------------------- if i <= 2: # 根据自己登录特性,我这里是验证码失败一次,重填所有,失败两次,重填验证码 elem_user.send_keys('S315080092') elem_psw.send_keys('xxxxxxxxxx') elem_code.send_keys(code) click_login = driver.find_element_by_xpath("//img[@src='main_images/images/loginbutton.gif']") click_login.click()#time.sleep(5)#搜索结果页面停留片刻#driver.save_screenshot('C:\Users\MrLevo\image.jpg')#driver.close()#driver.quit()
    Show Gif ( :
    遇到问题及解决方法
    验证码取得问题,因为每次刷新之后验证码动态刷新,所以如果不采用cookie的话(我还不太会用cookie),根本捉不到元素,这个我在下篇文章中采用cookie来登录的,但不是调用浏览器,这个跑远了,下次说。
    解决方案:用了driver.get_screenshot_as_file方法,机智的进行全截图,然后采用PIL中的crop进行再截图操作,可能有人会说,为什么不采用ImageGrab.grab()函数来做,好吧,因为这个函数在win10上尽然!截不了全图!!自己试了才知道,btw,我的分辨率1920x1080,难道和分辨率有关?反正这个我截了好久都没有成功,到最后才想到,截全部看看,结果,tmd只有一半,我说怎么都找不到要截图的部分!

    验证码验证错误率高问题
    解决方案,采用PIL强大的图像处理功能,我先将图片二值化,本来是蓝色字体的,,然后再进行对比度强化来锐化图片,然后再调用Tesseract.exe进行处理,提高的识别精度不是一点两点:看图比较,左1是用cookie抓的原图,右边是全景截图,再定位截图,再进行二值化和锐化处理的图,本来我想着用matlab做图像识别的,但是想想还要调用,感觉有点麻烦。。。


    调用Tesseract.exe问题
    解决方案因为程序执行图像识别需要调用Tesseract.exe,所以必须把路径切到有这个exe的路径下,刚开始,以为和包依赖,结果根本没有识别出任何图!折腾一个多小时才写好验证码识别的问题——单独测试的确很重要,记一笔!

    登录失败问题—mdzz学校教务系统二次验证
    解决方案,写了一个while循环,把主程序很大部分都扔进去了,目的也很明确,如果第一次登录失败,再重复进行登录,注意采用try试探元素是否仍然存在,except来抛出break结束循环,因为登录成功后,比如说driver.find_element_by_name('id')是不存在的!所以当这个元素在登陆后的界面找不到时,那就说明登录成功,ok,跳出循环,进行下一步操作。

    明明图片已截取,为什么没有识别
    解决方案,这个我真的没想到,我一直以为可能因为save时候还没下载好,导致库中没有这张图,那就不能识别,但是我用time.sleep函数让它停下来缓缓,还是不行,我就很无语了,想了半天,可能是因为图片被占用!因为我有一个img.show()函数,为了检测有没有截取到标准的图,然后show之后这个图像就被占用了!就像你在编辑word时候,是无法删除word文档一样!果然在注释掉show之后,一切可行,真是差错查了小半天啊!!

    元素一切就位,为什么不执行操作
    解决方案,这个有点脑残了,不过的确是我遇到的,还是记上一笔,然后骂自己一遍sb,没有click()你让它怎么处理!!!就像用cookie登录时候还有个ENTRY呢!

    两次验证失败后,用户名重复累加
    解决方案,直接加了个变量,计数循环次数,观察到只要超过两次没有登录上,就会累加登录名和用户密码,直接写了个if进行判断,完事!

    im.crop(box)裁剪区域选择困难症
    解决方案,多试几次,反正我是试出来的。。。。当然,你点击图片进行审查元素时候,可以看到图片大小,那么,你就可以知道横纵坐标差值多少,但是大范围区域还得自己试,如有更好的办法,请告知,以下为我截图实验次数,次数30+


    导入不了Image,ImageEnhance
    解决方案,因为PIL用的是第三方库,所以,采用的导入方式是这样的,多看看官方文档就可以,官方描述如下Usefrom PIL import Imageinstead ofimport Image.

    找不到应该键入的元素
    这个问题,请单击要输入的空白处右键,审查元素,就可以看到,然后根据driver.find_element_by_各种方法来定位元素,如果输入进行了隐藏,在当前页面找不到怎么办,就像如下图,需要先点击我的图书馆,才能看到输入的账户和密码,那么先找我的图书馆的元素,进行click操作,之后再找元素,一句话,把自己想成浏览器,阿不,把python想成浏览器。。。。。

    上图的代码我也放上,大同小异,比有验证码的简单,但是多了一个click操作。
    # -*- coding: utf-8 -*-from selenium import webdriverimport timeimport sys#shift+tab多行缩进(左)reload(sys)PostUrl = "http://lib.hrbeu.edu.cn/#"driver=webdriver.Firefox()driver.get(PostUrl)elem_user = driver.find_element_by_name('number')elem_psw = driver.find_element_by_name('passwd')#选择我的图书馆,点击后才能看到输入账号密码click_first = driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]")click_first.click()elem_user.send_keys('S315080092')elem_psw.send_keys('xxxxxxxx')#点击登录click_second = driver.find_element_by_name('submit')click_second.click()time.sleep(5)#登陆后选择click_third = driver.find_element_by_xpath("//*[@id='mainbox']/div/div/ul/li/a")click_third.click()time.sleep(5)#搜索结果页面停留片刻#driver.save_screenshot('C:\Users\MrLevo\image.jpg')driver.close()driver.quit()
    1 评论 1 下载 2019-05-01 21:32:29 下载需要13点积分
  • 基于python的网易云音乐分析


    MacOS Sierra 10.12.1
    Python 2.7
    selenium 3.4.3
    phantomjs

    前言
    发现自己有时候比挖掘别人来的更加有意义,自己到底喜欢谁的歌,自己真的知道么?习惯不会骗你

    搭建爬虫环境1.安装seleniumpip install selenium# anaconda环境的可用conda install selenium# 网速不好的可用到https://pypi.python.org/pypi/selenium下载压缩包,解压后使用python setup.py install
    2.安装Phantomjs2.1 Mac版本步骤一下载包:去这里下载对应版本http://phantomjs.org/download.html步骤二解压:双击就行,用unzip这都无所谓步骤三切入路径:cd ~/Downloads/phantomjs-2.1.1-macosx/bin # 我下的路径的路径是download,版本不一,注意修改步骤四:chmod +x phantomjs步骤五: 配置环境,因为我装的的zsh,所以文件需要修改的是~/.zshrc这个文件,加上这句话export PATH="/Users/mrlevo/Downloads/phantomjs-2.1.1-macosx/bin/:$PATH",然后source ~/.zshrc 即可生效(没用zsh的同学,直接修改的文件时~/.bash_profile,添加内容和上述一致)查看是否生效:phantomjs -v # 有信息如 2.1.1 则生效mac若遇到问题请参考PhantomJS 安装
    2.2 Win版本官网http://phantomjs.org/下载PhantomJS解压后如下图所示:

    调用时可能会报错“Unable to start phantomjs with ghostdriver”如图:



    此时可以设置下Phantomjs的路径,同时如果你配置了Scripts目录环境变量,可以解压Phantomjs到该文件夹下。可参考Selenium with GhostDriver in Python on Windows - stackoverflow,整个win安装过程可参考在Windows下安装PIP+Phantomjs+Selenium],Mac和Linux/Ubuntu 下可参考[解决:Ubuntu(MacOS)+phantomjs+python的部署问题

    3. 测试安装是否成功# 进入python环境后执行如下操作# win下操作>>> from selenium import webdriver # pip install selenium>>> driver_detail = webdriver.PhantomJS(executable_path="F:\Python\phantomjs-1.9.1-windows\phantomjs.exe")>>> driver_detail.get('https://www.baidu.com')>>> news = driver_detail.find_element_by_xpath("//div[@id='u1']/a")>>> print news.text新闻>>> driver_detail.quit() # 记得关闭,不然耗费内存------------------------------------------------------------------------# mac下操作>>> from selenium import webdriver # pip install selenium>>> driver_detail = webdriver.PhantomJS()>>> driver_detail.get('https://www.baidu.com')>>> news = driver_detail.find_element_by_xpath("//div[@id='u1']/a")>>> print news.text新闻>>> driver_detail.quit() # 记得关闭,不然耗费内存爬取动态数据
    获取自己的id号,这个可以自己登陆自己的网易云音乐后获得,就是id=后面那个值



    构造爬取的id,因为我发现,每个人的id只要被获取到,他的歌单都是公开的!!!这就节省了自动登录的一步,而且,我还有个大胆的想法,哈哈哈,我还要搞个大新闻!这次先不说~

    墙裂推荐先阅读该博客掌握获取元素方法:Python爬虫 Selenium实现自动登录163邮箱和Locating Elements介绍
    # -*- coding: utf-8 -*-import tracebackfrom selenium import webdriverimport selenium.webdriver.support.ui as uifrom selenium.webdriver.common.desired_capabilities import DesiredCapabilitiesimport timeimport random# 存储为文本的子函数def write2txt(data,path): f = open(path,"a") f.write(data) f.write("\n") f.close()# 获取该id喜欢音乐的列表def catchSongs(url_id,url): user = url_id.split('=')[-1].strip() print 'excute user:',user driver = webdriver.PhantomJS()#,executable_path='/Users/mrlevo/phantomjs-2.1.1-macosx/bin/phantomjs') # 注意填上路径 driver.get(url) driver.switch_to_frame('g_iframe') # 网易云的音乐元素都放在框架内!!!!先切换框架 try: wait = ui.WebDriverWait(driver,15) wait.until(lambda driver: driver.find_element_by_xpath('//*[@class="j-flag"]/table/tbody')) # 等待元素渲染出来 try: song_key = 1 wrong_time = 0 while wrong_time < 5: # 不断获取歌信息,假定5次获取不到值,就判无值可获取,跳出循环 try: songs = driver.find_elements_by_xpath('//*[@class="j-flag"]/table/tbody/tr[%s]'%song_key) info_ = songs[0].text.strip().split("\n") if len(info_) == 5: info_.insert(2,'None') # 没有MV选项的进行插入None new_line = '%s|'%user+'|'.join(info_) song_key +=1 #new_line = "%s|%s|%s|%s|%s|%s|%s"%(user,info_[0],info_[1],info_[2],info_[3],info_[4],info_[5]) print new_line write2txt(new_line.encode('utf-8'),user) # mac写入文件需要改变字符,以id命名的文件,存储在执行脚本的当前路径下,在win下请去掉编.endcode('utf-8') except Exception as ex: wrong_time +=1 # print ex except Exception as ex: pass except Exception as ex: traceback.print_exc() finally: driver.quit()# 获取id所喜爱的音乐的urldef catchPlaylist(url): driver = webdriver.PhantomJS()#,executable_path='/Users/mrlevo/phantomjs-2.1.1-macosx/bin/phantomjs') # 注意填上路径 driver.get(url) driver.switch_to_frame('g_iframe') # 网易云的音乐元素都放在框架内!!!!先切换框架 try: wait = ui.WebDriverWait(driver,15) wait.until(lambda driver: driver.find_element_by_xpath('//*[@class="m-cvrlst f-cb"]/li[1]/div/a')) # 根据xpath获取元素 urls = driver.find_elements_by_xpath('//*[@class="m-cvrlst f-cb"]/li[1]/div/a') favourite_url = urls[0].get_attribute("href") except Exception as ex: traceback.print_exc() finally: driver.quit() # print favourite_url return favourite_urlif __name__ == '__main__': for url in ['http://music.163.com/user/home?id=67259702']: # 这里把自己的id替换掉,想爬谁的歌单都可以,只要你有他的id time.sleep(random.randint(2, 4)) # 随机休眠时间2~4秒 url_playlist = catchPlaylist(url) time.sleep(random.randint(1, 2)) catchSongs(url,url_playlist)

    不出意外的话,你的执行脚本的目录下会产生一个以你的id命名的文件,里面打开应该是这样的

    67259702|2|因为了解|None|04:08|汪苏泷|慢慢懂67259702|3|潮鳴り|None|02:37|折戸伸治|CLANNAD ORIGINAL SOUNDTRACK67259702|4|每个人都会|None|02:58|方大同|橙月 Orange Moon67259702|5|Don't Cry (Original)|MV|04:44|Guns N' Roses|Greatest Hits67259702|6|妖孽(Cover:蒋蒋)|None|02:58|醉影An|醉声梦影67259702|7|好好说再见(Cover 陶喆 / 关诗敏)|None|04:06|锦零/疯疯|zero67259702|8|好好说再见(cover陶喆)|None|03:34|AllenRock|WarmCovers ·早# 这边分别爬取的数据结构是: id|歌次序|歌名|是否有MV|时长|歌手|专辑
    Show数据-ROUND1
    接下来就是处理自己下好的自己的歌单了,为了方便起见,我在构造爬取代码的时候,已经构造的比较好了,这也就帮助大家减少了数据预处理的时间了,一般来说,数据不会那么干净的。

    我只是做了最简单的歌手词云的例子,数据比较丰富的情况下,自己处理吧,想做什么统计都可以,或许以后我会补上可视化相关的一些例子
    1. 自定义遮罩层版本# -*- coding: utf-8 -*-# 如果还不清楚词云怎么搞,请参考这里https://mp.weixin.qq.com/s/0Bw8QUo1YfWZR_Boeaxu_Q,或者自行百度,很简单的一个包import numpy as npimport PIL.Image as Imagefrom wordcloud import WordCloud, ImageColorGeneratorimport matplotlib.pyplot as plt# 统计词频# win的用户,把解码去掉即可,因为当时mac写入的文件有编码,所以读出来需要解码def statistics(lst): dic = {} for k in lst: if not k.decode('utf-8') in dic:dic[k.decode('utf-8')] = 0 dic[k.decode('utf-8')] +=1 return dic path = '67259702' # 自己路径自己搞定list_ = []with open(path,'r') as f: for line in f: list_.append(line.strip().split('|')[-2].strip())dict_ = statistics(list_)# the font from github: https://github.com/adobe-fontsfont = r'SimHei.ttf'coloring = np.array(Image.open("screenshot.png")) # 遮罩层自己定义,可选自己的图片wc = WordCloud(background_color="white", collocations=False, font_path=font, width=1400, height=1400, margin=2, mask=np.array(Image.open("screenshot.png"))).generate_from_frequencies(dict_)# 这里采用了generate_from_frequencies(dict_)的方法,里面传入的值是{‘歌手1’:5,‘歌手2’:8,},分别是歌手及出现次数,其实和jieba分词# 之后使用generate(text)是一个效果,只是这里的text已经被jieba封装成字典了image_colors = ImageColorGenerator(np.array(Image.open("screenshot.png")))plt.imshow(wc.recolor(color_func=image_colors))plt.imshow(wc)plt.axis("off")plt.show()wc.to_file('mymusic2.png') # 把词云保存下来

    2. 方块版本# -*- coding: utf-8 -*-# 稍微修改下参数,就是另一幅图,这是没有遮罩层的import numpy as npimport PIL.Image as Imagefrom wordcloud import WordCloud, ImageColorGeneratorimport matplotlib.pyplot as plt# 统计词频def statistics(lst): dic = {} for k in lst: if not k.decode('utf-8') in dic:dic[k.decode('utf-8')] = 0 dic[k.decode('utf-8')] +=1 return dic path = '67259702' # 自己路径自己搞定list_ = []with open(path,'r') as f: for line in f: list_.append(line.strip().split('|')[-2].strip())dict_ = statistics(list_)# the font from github: https://github.com/adobe-fontsfont = r'SimHei.ttf'coloring = np.array(Image.open("screenshot.png"))wc = WordCloud( collocations=False, font_path=font, width=1400, height=1400, margin=2, ).generate_from_frequencies(dict_)# 这里采用了generate_from_frequencies(dict_)的方法,里面传入的值是{‘歌手1’:5,‘歌手2’:8,},分别是歌手及出现次数,其实和jieba分词# 之后使用generate(text)是一个效果,只是这里的text已经被jieba封装成字典了image_colors = ImageColorGenerator(np.array(Image.open("screenshot.png")))plt.imshow(wc)plt.axis("off")plt.show()wc.to_file('mymusic2.png') # 把词云保存下来

    SHOW数据-ROUND2
    刚看到个好玩的,迫不及待的试了下,这是关于语种翻译的API接口,阿里云买的,0.01=1000条,买买买,买来玩玩试试自己歌曲语种

    # -*- coding:utf-8 -*-# 调用的阿里云的API接口实现语种翻译# API官网:https://market.aliyun.com/products/57124001/cmapi010395.html?spm=5176.730005.0.0.UrR9bO#sku=yuncode439500000import urllib, urllib2, sysimport ssldef Lang2Country(text): host = 'https://dm-12.data.aliyun.com' path = '/rest/160601/mt/detect.json' method = 'POST' appcode = 'xxxxx' # 购买后提供的appcode码 querys = '' bodys = {} url = host + path bodys['q'] = text post_data = urllib.urlencode(bodys) request = urllib2.Request(url, post_data) request.add_header('Authorization', 'APPCODE ' + appcode) # 根据API的要求,定义相对应的Content-Type request.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE response = urllib2.urlopen(request, context=ctx) content = response.read() if (content): # print(content) return content else: return None# 67259702|1|Claux - 水之畔(8lope Remix) (feat. 陶心瑶)|None|02:44|8lope|水之畔(feat. 陶心瑶) (8lope Remix)list_songs = []list_songwithsinger = []with open('67259702') as f: # 文件名写上次爬下来的 for line in f: line_split = line.split('|') list_songs.append(line_split[2]) list_songwithsinger.append(line_split[2]+line_split[5])# 调用接口进行语种识别dict_lang = {}for i in range(537): try: content = Lang2Country(list_songwithsinger[i]) lag_ = json.loads(content)['data']['language'] if lag_ not in dict_lang: dict_lang[lag_]=0 dict_lang[lag_] +=1 except: passprint dict_lang # {u'ru': 1, u'fr': 9, u'en': 111, u'zh': 259, u'pt': 21, u'ko': 8, u'de': 7, u'tr': 15, u'it': 47, u'id': 2, u'pl': 7, u'th': 1, u'nl': 10, u'ja': 17, u'es': 20}

    ok,数据准备好了,接下来可视化就好了!这次我用Echarts,换个口味的就不用云词了,来个统计效果好看点的!


    # 进入该网页:http://echarts.baidu.com/demo.html#pie-simple# 然后把里面的内容替换掉就行option = { title : { text: '哈士奇说喵喜欢的音乐', x:'center' }, tooltip : { trigger: 'item', formatter:'{b} : {c} ({d}%)' }, legend: { orient: 'vertical', left: 'left', data:['中文','英文','俄语','法语','葡萄牙语','韩语','德语','土耳其语','意大利语'] }, series : [ { name: '访问来源', type: 'pie', radius : '55%', center: ['50%', '60%'], itemStyle: { normal: {label:{ show:true, formatter:'{b} : {c} ({d}%)' }, }}, data:[ {value:259, name:'中文'}, {value:111,name:'英文'}, {value:1, name:'俄语'}, {value:9, name:'法语'}, {value:21, name:'葡萄牙语'}, {value:8, name:'韩语'}, {value:7, name:'德语'}, {value:15, name:'土耳其语'}, {value:47, name:'意大利语'}, {value:2, name:'印尼语'}, {value:7, name:'波兰语'}, {value:1, name:'泰语'}, {value:10, name:'荷兰语'}, {value:17, name:'日语'}, {value:20, name:'西班牙语'}, ], } ]};
    Pay Attention
    这里遇到的最大问题,就是网易云的网页竟然还iframe框来做!!!不切入那个内联框架连phantomjs都无能为力!!这是最值得注意的一点,即使你找对了元素,也可能获取不到值!
    如果是win的计算机,在 driver = webdriver.PhantomJS()里面填上phantomjs.exe的路径,上面抓取数据的代码里面有两个需要引擎需要填写路径
    如果有打印出字段,但是记录的数据为0KB,那么是文件没有写进去,对于win的用户,把代码写入的部门,编码方式去掉即可
    有些win的小伙伴反应路径都加载对了,但是还是找不到exe,那么请在路径前面加r比如 executable_path=r"F:\Python\phantomjs-1.9.1-windows\phantomjs.exe"

    结论
    果然一下子就看出是上个世纪九十年代的人(:,还有就是,音乐不分国界,就是动感~

    附录
    对照表

    1 评论 3 下载 2019-04-29 21:40:48 下载需要10点积分
  • Python基于Tkinter的二输入规则器


    Python 2.7IDE Pycharm 5.0.3
    起因
    昨天接触了Tkinter框架,之后就迫不及待的想写个计算器出来,结果呢,可想而知了,当初自己犟脾气,掌握几个语法后就想什么都不参考写自己的一段四则运算器出来,结果。。。。。。花了我一天时间,我竟然歪打正着写了个规则器出来窝草。。。。

    对比
    贴个图,别人家的计算器是这样的;而且用了五十行,说的貌似很了不起的样子(老纸的规则器,只要40-就可以!不算上Scrollbar,分割子框架这类的)


    But
    我的规则器是这样的。。。。


    我知道布局排丑了,不要在意这些细节好么0.0
    说说优点
    以计算器角度说,能完美实现计算,而且带标号,记录存储等功能,知道上一步计算结果。
    最大的优点在于二输入,调用各种def的函数,而四则运算只是最简单的函数而已,比如说我又写了字符串连接函数,相似度比较函数等等,做个实例而已,大家可以大开脑洞

    缺点
    需要键盘输入,与普通计算器按键输入不同
    我的代码冗余量比较大,因为自己需要看懂,所以不像别的教程那样直接跟着lambda和pack,一长串的,不利于我们这种小白读。等我水平再高一些,或许我也会采用lambda,这样才够pythontic~

    后续
    本身在做分类聚类方面的课题,结合这个规则器,我完全可以把k-means中的k参数在交互界面上输入,这样就不用每次上程序里面改了!还有DBSCAN里面的Eps和MinPts也可以直接用这个框架!!想想有点小激动呢!(挖的坑不计其数)
    需要优化下布局,尝试用grid来做,感觉pack里面参数略多啊。

    构思框架放代码之前,先来设计思路,我设计了两个框架,输入和输出在两个框架上,这样便于写代码思路清晰,框架大概是这样的;

    代码此代码(就算再烂)绝此一家,别无分店哈哈
    #-------------------二输入规则计算器--------------------# -*- coding: utf-8 -*-from Tkinter import *import difflib#主框架部分root = Tk()root.title('乞丐版规则器0.0')root.geometry()Label_root=Label(root,text='规则运算(根框架)',font=('宋体',15))#-----------------------定义规则------------------------def Plus(a,b): return round(a+b, 2)def Sub(a,b): return round(a-b,2)def Mult(a,b): return round(a*b, 2)def Div(a,b): return round(a/b, 2)def P_str(a,b): return a+bdef Rep(a,b): return difflib.SequenceMatcher(None,a,b).ratio() #difflib可以看看其中的定义,计算匹配率的#还可以继续增加规则函数,只要是两输入的参数都可以#----------------------触发函数-----------------------def Answ():#规则函数 if lb.get(lb.curselection()).encode('utf-8') == '加': Ans.insert(END,'规则:+ ->'+str(Plus(float(var_first.get()),float(var_second.get())))) if lb.get(lb.curselection()).encode('utf-8')=='减': Ans.insert(END,'规则:- ->'+str(Sub(float(var_first.get()),float(var_second.get())))) if lb.get(lb.curselection()).encode('utf-8')=='乘': Ans.insert(END,'规则:x ->'+str(Mult(float(var_first.get()),float(var_second.get())))) if lb.get(lb.curselection()).encode('utf-8')=='除': Ans.insert(END,'规则:/ ->'+str(Div(float(var_first.get()),float(var_second.get())))) if lb.get(lb.curselection()).encode('utf-8')=='字符串连接': Ans.insert(END,'规则:字符串连接 ->' +P_str(var_first.get(),var_second.get()).encode('utf-8')) if lb.get(lb.curselection()).encode('utf-8')=='字符串相似度': Ans.insert(END,'规则:字符串相似度 ->'+str(Rep(var_first.get(),var_second.get()))) #添加规则后定义规则函数def Clea():#清空函数 input_num_first.delete(0,END)#这里entry的delect用0 input_num_second.delete(0,END) Ans.delete(0,END)#text中的用0.0#----------------------输入选择框架--------------------frame_input = Frame(root)Label_input=Label(frame_input, text='(输入和选择框架)', font=('',15))var_first = StringVar()var_second = StringVar()input_num_first = Entry(frame_input, textvariable=var_first)input_num_second = Entry(frame_input, textvariable=var_second)#---------------------选择运算规则---------------------#还可以添加其他规则lb = Listbox(frame_input,height=4)list_item=['加', '减', '乘', '除','字符串连接','字符串相似度']for i in list_item: lb.insert(END,i)#---------------------计算结果框架---------------------frame_output = Frame(root)Label_output=Label(frame_output, text='(计算结果框架)', font=('',15))Ans = Listbox(frame_output, height=5,width=30)#text也可以,Listbox好处在于换行#-----------------------Button-----------------------calc = Button(frame_output,text='计算', command=Answ)cle = Button(frame_output,text='清除', command=Clea)#---------------------滑动Scrollbar-------------------scr1 = Scrollbar(frame_input)lb.configure(yscrollcommand = scr1.set)scr1['command']=lb.yviewscr2 = Scrollbar(frame_output)Ans.configure(yscrollcommand = scr2.set)scr2['command']=Ans.yview#-------------------------布局------------------------#布局写在一块容易排版,可能我low了吧Label_root.pack(side=TOP)frame_input.pack(side=TOP)Label_input.pack(side=LEFT)input_num_first.pack(side=LEFT)lb.pack(side=LEFT)scr1.pack(side=LEFT,fill=Y)input_num_second.pack(side=RIGHT)frame_output.pack(side=TOP)Label_output.pack(side=LEFT)calc.pack(side=LEFT)cle.pack(side=LEFT)Ans.pack(side=LEFT)scr2.pack(side=LEFT,fill=Y)#-------------------root.mainloop()------------------root.mainloop()
    Tkinter还是比较好上手的,知道一些基本语法就可以实现自己想要的效果了,这里我把自己遇到的问题写一下,如果也有人遇到,恰好能帮助的话,我很荣幸。
    问题&解决Q.button或插件不显示A.记得加上pack显示函数!!一般我都定义完了插件直接补上 pack函数
    Q.插件位置显示问题A.这个要看你的pack函数写在哪了,所以我一般直接写在最后,容易排序,比如side都是LEFT的话,就按先后顺序显示的
    Q.刚开始键入的被get之后,直接运算出错。A.结果是str类型,所以记得用float强制转换,不用int是因为int做除法时候不好使,需要float,切记(python2.7)
    Pay Attention
    在自定义规则的时候,主要get抓到的数据类型和你的def里面的数据类型,保持一致。
    清空函数中,text和entry,listbox的delect清空不一样!比如被实例的是Listbox(Entry)的,那么清空是Obj.delect(0,END),而如果是Text的对象,那么就是Obj.delect(0.0,END),这个是我之前没想到的,只有实践过才记得住把。而且,用listbox好处在于计算一个值之后,下一个值自动换行,用text时候\n还不好使
    如果使用python3,会出现,no model name Tkinter,其实py3只是把它改成小写了,所以导入包的时候改成tkinter 小写就行
    出现点击运算符之后无法输出结果或者gui中文乱码问题,一般也是出现在python3的问题上,所以解决方案是吧encode(‘utf-8’)删掉就可以了。
    1 评论 1 下载 2019-04-27 21:04:41 下载需要9点积分
  • Python自定义豆瓣电影种类,排行,点评的爬取与存储(初级)


    Python 2.7
    IDE Pycharm 5.0.3
    Firefox 47.0.1

    起因
    就是想写个豆瓣电影的爬取,给我电影荒的同学。。。。当然自己也练手啦

    目的
    根据用户输入,列出豆瓣高分TOP(用户自定义)的电影,链接,及热评若干。
    制作不需要Python环境可运行的exe,但由于bug未修复,需要火狐浏览器支持

    方案
    使用PhantomJS+Selenium+Firefox实现

    实现过程
    get到首页后,根据选择,点击种类,然后根据输入需求,进行排序
    抓取每个电影及超链接,进入超链接后,抓取当前电影的热评及长评
    当用户所要求TOP数目大于第一页的20个时候,点击加载更多,再出现20个电影,重复2操作


    以豆瓣高分,然后按评分排序的点击过程(其余操作一致,先种类后排序选择,再爬)


    实现代码# -*- coding: utf-8 -*-#Author:哈士奇说喵#爬豆瓣高分电影及hot影评from selenium import webdriverimport selenium.webdriver.support.ui as uiimport timeprint "---------------system loading...please wait...---------------"SUMRESOURCES = 0 #全局变量driver_detail = webdriver.PhantomJS(executable_path="phantomjs.exe")#driver_item=webdriver.PhantomJS(executable_path="phantomjs.exe")driver_item=webdriver.Firefox()url="https://movie.douban.com/"#等待页面加载方法wait = ui.WebDriverWait(driver_item,15)wait1 = ui.WebDriverWait(driver_detail,15)#获取URL和文章标题def getURL_Title(): global SUMRESOURCES###############################################################################需要键入想要获取的信息,比如种类,排序方式,想看多少内容############################################################################## print "please select:" kind=input("1-Hot\n2-Newest\n3-Classics\n4-Playable\n5-High Scores\n6-Wonderful but not popular\n7-Chinese film\n8-Hollywood\n9-Korea\n10-Japan\n11-Action movies\n12-Comedy\n13-Love story\n14-Science fiction\n15-Thriller\n16-Horror film\n17-Cartoon\nplease select:") print "--------------------------------------------------------------------------" sort=input("1-Sort by hot\n2-Sort by time\n3-Sort by score\nplease select:") print "--------------------------------------------------------------------------" number = input("TOP ?:") print "--------------------------------------------------------------------------" ask_long=input("don't need long-comments,enter 0,i like long-comments enter 1:") print "--------------------------------------------------------------------------" global save_name save_name=raw_input("save_name (xx.txt):") print "---------------------crawling...---------------------" driver_item.get(url)###############################################################################进行网页get后,先进行电影种类选择的模拟点击操作,然后再是排序方式的选择#最后等待一会,元素都加载完了,才能开始爬电影,不然元素隐藏起来,不能被获取#wait.until是等待元素加载完成!############################################################################## wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div/div/label[%s]"%kind)) driver_item.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div/div/label[%s]"%kind).click() wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div[3]/div/label[%s]"%sort)) driver_item.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div[3]/div/label[%s]"%sort).click() num=number+1#比如输入想看的TOP22,那需要+1在进行操作,细节问题 time.sleep(2) #打开几次“加载更多” num_time = num/20+1 wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list-wp']/a[@class='more']")) for times in range(1,num_time): time.sleep(1) driver_item.find_element_by_xpath("//div[@class='list-wp']/a[@class='more']").click() time.sleep(1) wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list']/a[%d]"%num)) #print '点击\'加载更多\'一次' #使用wait.until使元素全部加载好能定位之后再操作,相当于try/except再套个while把 for i in range(1,num): wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list']/a[%d]"%num)) list_title=driver_item.find_element_by_xpath("//div[@class='list']/a[%d]"%i) print '----------------------------------------------'+'NO' + str(SUMRESOURCES +1)+'----------------------------------------------' print u'电影名: ' + list_title.text print u'链接: ' + list_title.get_attribute('href') #print unicode码自动转换为utf-8的 #写入txt中部分1 list_title_wr=list_title.text.encode('utf-8')#unicode码,需要重新编码再写入txt list_title_url_wr=list_title.get_attribute('href') Write_txt('\n----------------------------------------------'+'NO' + str(SUMRESOURCES +1)+'----------------------------------------------','',save_name) Write_txt(list_title_wr,list_title_url_wr,save_name) SUMRESOURCES = SUMRESOURCES +1 try:#获取具体内容和评论。href是每个超链接也就是资源单独的url getDetails(str(list_title.get_attribute('href')),ask_long) except: print 'can not get the details!'###############################################################################当选择一部电影后,进入这部电影的超链接,然后才能获取#同时别忽视元素加载的问题#在加载长评论的时候,注意模拟点击一次小三角,不然可能会使内容隐藏##############################################################################def getDetails(url,ask_long): driver_detail.get(url) wait1.until(lambda driver: driver.find_element_by_xpath("//div[@id='link-report']/span")) drama = driver_detail.find_element_by_xpath("//div[@id='link-report']/span") print u"剧情简介:"+drama.text drama_wr=drama.text.encode('utf-8') Write_txt(drama_wr,'',save_name) print "--------------------------------------------Hot comments TOP----------------------------------------------" for i in range(1,5):#四个短评 try: comments_hot = driver_detail.find_element_by_xpath("//div[@id='hot-comments']/div[%s]/div/p"%i) print u"最新热评:"+comments_hot.text comments_hot_wr=comments_hot.text.encode('utf-8') Write_txt("--------------------------------------------Hot comments TOP%d----------------------------------------------"%i,'',save_name) Write_txt(comments_hot_wr,'',save_name) except: print 'can not caught the comments!' #加载长评 if ask_long==1: try: driver_detail.find_element_by_xpath("//img[@class='bn-arrow']").click() #wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='review-bd']/div[2]/div/div")) time.sleep(1) #解决加载长评会提示剧透问题导致无法加载 comments_get = driver_detail.find_element_by_xpath("//div[@class='review-bd']/div[2]/div") if comments_get.text.encode('utf-8')=='提示: 这篇影评可能有剧透': comments_deep=driver_detail.find_element_by_xpath("//div[@class='review-bd']/div[2]/div[2]") else: comments_deep = comments_get print "--------------------------------------------long-comments---------------------------------------------" print u"深度长评:"+comments_deep.text comments_deep_wr=comments_deep.text.encode('utf-8') Write_txt("--------------------------------------------long-comments---------------------------------------------\n",'',save_name) Write_txt(comments_deep_wr,'',save_name) except: print 'can not caught the deep_comments!'###############################################################################将print输出的写入txt中查看,也可以在cmd中查看,换行符是为了美观##############################################################################def Write_txt(text1='',text2='',title='douban.txt'): with open(title,"a") as f: for i in text1: f.write(i) f.write("\n") for j in text2: f.write(j) f.write("\n")def main(): getURL_Title() driver_item.quit()main()
    上面的代码是可以实现的,但需要Firefox的配合,因为我其中一个引擎调用了Firefox,另一个抓评论的用了PhantomJS。
    实现效果

    存入的txt文件



    因为打包成exe必须是中文的键入,所以没办法,我改成英文来着,不然会出现这种情况。。。



    输出内容是没有问题的。。。。。。

    问题及解决方案Q: 使用PhantomJS和Firefox出现不同效果的问题,第21个回到起点。
    A: 解决方案,暂且我也没有找到,只有调用Firefox然后完事后再关闭,分析请见伪解决Selenium中调用PhantomJS无法模拟点击(click)操作
    Q: 在对unicode输出在txt出现的问题,但是在print可以直接中文输出的。
    A: 解决方案:详见Python输出(print)内容写入txt中保存
    Q: 元素无法定位问题
    A: 首先查看是不是隐藏元素,其次再看自己的规则有没有写错,还有就是是不是页面加载未完成,详见解决网页元素无法定位(NoSuchElementException: Unable to locate element)的几种方法
    Q: 只采集自己需要的数据,剔除无用数据,比如说,刚开始我用
    driver_detail.find_elements_by_xpath然后写个取出list中元素的方法,但是这样的话,一个便签下内容未必太多,并不是我想要的如图:

    比如说,我只想要红色的部分,那么,采取elements就不太好处理。
    A: 我采用的方法是格式化字符串!根据元素的特性,可以发现,每个热评的正文标签不一样的,其余标签一样,只要格式化正文标签即可,像这样
    for i in range(1,5):#取了前四条热评 try: comments = driver_detail.find_element_by_xpath("//div[@id='hot-comments']/div[%s]/div/p"%i) print u"最新热评:"+comments.text except: print 'can not caught comments!'
    Q: 一个引擎干有个事!我现在没办法,只有将第一个需要处理的页面用Firefox来处理,之后评论用PhantomJS来抓取,之后可以用quit来关闭浏览器,但是启动浏览器还是会耗费好多资源,而且挺慢,虽然PhantomJS也很慢,我12G内存都跑完了。。。。。。看样子是给我买8x2 16G双通道的借口啊。
    Q: 备注不标准也会导致程序出错,这个是我没想到的,我一直以为在’’’备注’’’之间的都可以随便来,结果影响程序运行了,之后分模块测试才注意到这个问题,也是以前没有遇到过的,切记!需要规范自己代码,特别是像Python这样缩进是灵魂的语言。。。。
    Q: 补充,长评论的抓取

    这是点击之后的图,可以看到元素定位也是不一样的,注意
    1 评论 1 下载 2019-04-26 22:35:17 下载需要9点积分
  • 基于N-gram概括内容

    利用N-Gram模型概括数据(Python描述)

    Python 2.7
    IDE PyCharm 5.0.3

    什么是N-Gram模型?
    在自然语言里有一个模型叫做n-gram,表示文字或语言中的n个连续的单词组成序列。在进行自然语言分析时,使用n-gram或者寻找常用词组,可以很容易的把一句话分解成若干个文字片段。摘自Python网络数据采集[RyanMitchell著]

    简单来说,就是找到核心主题词,那怎么算核心主题词呢,一般而言,重复率也就是提及次数最多的也就是最需要表达的就是核心词。下面的例子也就从这个开始展开
    临时补充在栗子中出现,这里拿出来单独先试一下效果

    string.punctuation获取所有标点符号,和strip搭配使用
    import stringlist = ['a,','b!','cj!/n']item=[]for i in list: i =i.strip(string.punctuation) item.append(i)print item
    ['a', 'b', 'cj!/n']
    operator.itemgetter()
    operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号)
    栗子
    import operatordict_={'name1':'2', 'name2':'1'}print sorted(dict_.items(),key=operator.itemgetter(0),reverse=True)#dict_.items(),键值对
    [('name2', '1'), ('name1', '2')]当然,你可以直接直接使用这个
    dict_={'name1':'2', 'name2':'1'}print sorted(dict_.iteritems(),key=lambda x:x[1],reverse=True)2-gram就以两个关键词来说吧,上个栗子来进行备注讲解
    import urllib2import reimport stringimport operatordef cleanText(input): input = re.sub('\n+', " ", input).lower() # 匹配换行,用空格替换换行符 input = re.sub('\[[0-9]*\]', "", input) # 剔除类似[1]这样的引用标记 input = re.sub(' +', " ", input) # 把连续多个空格替换成一个空格 input = bytes(input)#.encode('utf-8') # 把内容转换成utf-8格式以消除转义字符 #input = input.decode("ascii", "ignore") return inputdef cleanInput(input): input = cleanText(input) cleanInput = [] input = input.split(' ') #以空格为分隔符,返回列表 for item in input: item = item.strip(string.punctuation) # string.punctuation获取所有标点符号 if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'): #找出单词,包括i,a等单个单词 cleanInput.append(item) return cleanInputdef getNgrams(input, n): input = cleanInput(input) output = {} # 构造字典 for i in range(len(input)-n+1): ngramTemp = " ".join(input[i:i+n])#.encode('utf-8') if ngramTemp not in output: #词频统计 output[ngramTemp] = 0 #典型的字典操作 output[ngramTemp] += 1 return output#方法一:对网页直接进行读取content = urllib2.urlopen(urllib2.Request("http://pythonscraping.com/files/inaugurationSpeech.txt")).read()#方法二:对本地文件的读取,测试时候用,因为无需联网#content = open("1.txt").read()ngrams = getNgrams(content, 2)sortedNGrams = sorted(ngrams.items(), key = operator.itemgetter(1), reverse=True) #=True 降序排列print(sortedNGrams)
    [('of the', 213), ('in the', 65), ('to the', 61), ('by the', 41), ('the constitution', 34),,,巴拉巴拉一堆上述栗子作用在于抓到2连接词的频率大小来排序的,但是这并不是我们想要的,你说这出现两百多次的 of the 有个猫用啊,所以,我们要进行对这些连接词啊介词啊的剔除工作。
    Deeper
    完整代码和测试图都在同级目录下的2_gram.ipynb,如要测试请手动下载工程,然后运行jupyter即可,不知道jupyter?百度啊,自己装

    # -*- coding: utf-8 -*-import urllib2import reimport stringimport operator#剔除常用字函数def isCommon(ngram): commonWords = ["the", "be", "and", "of", "a", "in", "to", "have", "it", "i", "that", "for", "you", "he", "with", "on", "do", "say", "this", "they", "is", "an", "at", "but","we", "his", "from", "that", "not", "by", "she", "or", "as", "what", "go", "their","can", "who", "get", "if", "would", "her", "all", "my", "make", "about", "know", "will","as", "up", "one", "time", "has", "been", "there", "year", "so", "think", "when", "which", "them", "some", "me", "people", "take", "out", "into", "just", "see", "him", "your", "come", "could", "now", "than", "like", "other", "how", "then", "its", "our", "two", "more", "these", "want", "way", "look", "first", "also", "new", "because", "day", "more", "use", "no", "man", "find", "here", "thing", "give", "many", "well"] if ngram in commonWords: return True else: return Falsedef cleanText(input): input = re.sub('\n+', " ", input).lower() # 匹配换行用空格替换成空格 input = re.sub('\[[0-9]*\]', "", input) # 剔除类似[1]这样的引用标记 input = re.sub(' +', " ", input) # 把连续多个空格替换成一个空格 input = bytes(input)#.encode('utf-8') # 把内容转换成utf-8格式以消除转义字符 #input = input.decode("ascii", "ignore") return inputdef cleanInput(input): input = cleanText(input) cleanInput = [] input = input.split(' ') #以空格为分隔符,返回列表 for item in input: item = item.strip(string.punctuation) # string.punctuation获取所有标点符号 if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'): #找出单词,包括i,a等单个单词 cleanInput.append(item) return cleanInputdef getNgrams(input, n): input = cleanInput(input) output = {} # 构造字典 for i in range(len(input)-n+1): ngramTemp = " ".join(input[i:i+n])#.encode('utf-8') if isCommon(ngramTemp.split()[0]) or isCommon(ngramTemp.split()[1]): pass else: if ngramTemp not in output: #词频统计 output[ngramTemp] = 0 #典型的字典操作 output[ngramTemp] += 1 return output#获取核心词在的句子def getFirstSentenceContaining(ngram, content): #print(ngram) sentences = content.split(".") for sentence in sentences: if ngram in sentence: return sentence return ""#方法一:对网页直接进行读取content = urllib2.urlopen(urllib2.Request("http://pythonscraping.com/files/inaugurationSpeech.txt")).read()#对本地文件的读取,测试时候用,因为无需联网#content = open("1.txt").read()ngrams = getNgrams(content, 2)sortedNGrams = sorted(ngrams.items(), key = operator.itemgetter(1), reverse=True) # reverse=True 降序排列print(sortedNGrams)for top3 in range(3): print "###"+getFirstSentenceContaining(sortedNGrams[top3][0],content.lower())+"###"
    [('united states', 10), ('general government', 4), ('executive department', 4), ('legisltive bojefferson', 3), ('same causes', 3), ('called upon', 3), ('chief magistrate', 3), ('whole country', 3), ('government should', 3),,,,巴拉巴拉一堆### the constitution of the united states is the instrument containing this grant of power to the several departments composing the government###### the general government has seized upon none of the reserved rights of the states###### such a one was afforded by the executive department constituted by the constitution###从上述栗子我们可以看出,我们对有用词进行了删选,去掉了连接词,取出核心词排序,然后再把包含核心词的句子抓出来,这里我只是抓了前三句,对于有两三百个句子的文章,用三四句话概括起来,我想还是比较神奇的。
    1 评论 0 下载 2019-04-25 23:09:42 下载需要8点积分
  • Python+MySQL用户加密存储验证系统


    Python 2.7
    IDE Pycharm 5.0.3
    PyMySQL 0.7.6
    MySQL 5.7
    MySQL Workbench 6.3

    应用场景
    如果数据库是暂存在第三方,而且存入的数据又不想让第三方数据库管理员看到,消息涉及隐私,只有自己可见,那么怎么办呢,我自己设计了一套用户加密验证系统,对登录密码进行MD5/SHA1可选加密,对明文进行自定义的加密算法进行加密存储。短时间内无法破解(私以为)

    特点
    用户加密存储系统—用于托管第三方数据库,内容进行加密后存储,没有秘钥无法破解


    用户存储,登录,查看,删除操作,存储在数据库中
    用户密码加密存储,密码加密方式可选,目前只可选MD5和SHA1,用户存储内容加密存储,加密方式自定义
    自定义(我自己定义了一个加密解密函数)加密序列,拿到内容没有序列无法解密(自以为)
    可更改用户密码,更改自定义KEY值,更改加密存储内容
    支持任何位数和形式设置密码,甚至可以设置成中文!但是请注意,最好是杂乱无序字母夹杂的,不然被破解第一层密码后,KEY值可能会暴露!

    实现流程框架

    其实上面流程图的最左边如果新建用户和数据库中用户重名,则有两个选择,一个是重新命名,另一个就是对原来用户名进行修改密码,线有点多,太乱了连过去,所以这里省略了,实现效果请看下面的IDE交互章节。

    好了,整体的思路框架就是这样,当然一开始我没有想那么多,只是做着做着,想着不断增加功能,更加完善性考虑,才会加入那么多选择项的,因为自己设计的,所以,难免存在瑕疵,也没有参考实际大的加密工程中如何处理,下次去看看。
    实现代码这里我就不贴详细代码了,太长了,估计三四百行把,我上传资源在同级目录的mysql_encryptWD中,包括源码(带注释)+exe(exe由于打包软件限制只能用于英文字符输入)+README(请先阅读)

    这里只是贴上自己写的加密算法部分。

    #自定义加密、解密算法子函数。结合base64def encrypt(key,content): # key:密钥,content:明文 s1 = base64.encodestring(str(content)) #将内容进行base64加密 len1 = len(key) % 7 #取余数 len1_list = list_key[len1] #取余数对应list_key中伪码 mix_first = str(key)+s1 #将key转化为字符串后拼接第一次加密的内容 mix = len1_list+base64.encodestring(mix_first) #对拼接后字符串再进行加密,再加个伪码 return mix #存入数据库中,不能被反解def decrypt(key,mix): # key:密钥,content:密文 len2 = len(key) % 7 len2_findlist = list_key[len2] if len2_findlist==mix[0]: #先确定伪码 s2_first = base64.decodestring(mix[1:])#反解出第一次的base64编码 s2_second = s2_first[0:len(key)] #获取第一次解出前缀是否为key if s2_second==str(key):#key值对应了 s2_end = base64.decodestring(s2_first[len(key):])#反解出去掉前缀后的真实内容的64位编码 print '-------------------------------Validation Succeed!-------------------------------' return s2_end else: print "Warning!Validation Failed!Can't Get Secret Words!" else: print "Warning!Validation Failed!Can't Get Secret Words!"
    解释就在上面的注释上了,这里说一下实现效果,存入数据库中形式应该是这样的

    对于自己的加密算法:自己尝试写,肯定会有纰漏的地方,如果以后有机会,可以学一下密码学,自己接触的到底还是太少了,只是添加了伪码表,然后两次base64加密,当然,数据库中内容直接拿来base64解码肯定是不会成功的。
    登录密码是用MD5/SHA1进行加密的,而第二层,登陆之后,可以自己选择KEY值,对输入的明文进行加密,如果没有我的伪字典和KEY应该是不能反解出加密的内容的,所以对于数据库管理员来说,根本不能解密存入数据库中的内容,对于有特殊需求的记录也好,项目也好,我想应该有应用的地方。
    交互效果这里显示的是整个IDE交互界面,如何处理这些;

    进行新用户注册(可以选择不设置明文默认KEY为123456)
    -------------------------------Mode Choice-------------------------------------Store&Encrypt-1 Login&View&Update&Delete-2 Quit System-3 Clear Database-4Select Mode:1-------------------------------Store&Encrypt-------------------------------New User:k3Set Password:k3-------------------------------Password Encrypt Algorithm-------------------------------------MD5-1 SHA1-2Select Algorithm:1
    进行设置KEY和明文加密
    -------------------------------What's Next?-------------------------------------Store Encrypt Plaintext-1 Maybe Next Time-2Your Choice:1Please Design Your KEY:k4Plaintext:k4'secret##############################################SHA1-Password&Plaintext Encryption Succeed!##############################################以下是默认KEY与明文设置,选择2即可
    -------------------------------What's Next?-------------------------------------Store Encrypt Plaintext-1 Maybe Next Time-2Your Choice:2Default KEY '123456'Default Plaintext 'Default Storage'#############################################MD5-Password&Plaintext Encryption Succeed!#############################################
    查看加密明文
    (如果没有自己设置KEY等,会有个默认值进行存储)
    以下为自己设置了KEY和明文(没有设置时候,则KEY为123456),查看明文
    -------------------------------k4:What's Next?-------------------------------Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6Your Choice: 2KEY:k4-------------------------------Validation Succeed!-------------------------------Secret Words:k4'secret更加详细的请自己测试使用。代码和上述流程图保持一致。

    遇到新用户重名,解决途径,修改密码或者更换新名字
    -------------------------------Mode Choice-------------------------------------Store&Encrypt-1 Login&View&Update&Delete-2 Quit System-3 Clear Database-4Select Mode:1-------------------------------Store&Encrypt-------------------------------New User:k1Warning!The Name Already Exist!-------------------------------Make Your Choice-------------------------------------Change Password-1 Create New User-2Select Mode:2New User:k2Set Password:k3
    更新登录密码选择,需要有以前密码,才能修改
    -------------------------------Welcome k1--------------------------------------------------------------k1:What's Next?-------------------------------Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6Your Choice: 3Please Enter Original Password:k1Please Enter New Password:k2###########################Update Password Succeed!###########################
    更新KEY值
    -------------------------------Welcome k1--------------------------------------------------------------k1:What's Next?-------------------------------Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6Your Choice: 4Please Enter Original KEY:k1Please Enter New KEY:k2-------------------------------Validation Succeed!-------------------------------######################Update KEY Succeed!######################
    更新明文
    -------------------------------k4:What's Next?-------------------------------Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6Your Choice: 1KEY:k4-------------------------------Validation Succeed!-------------------------------Original Plaintext:k4'secretNew Plaintext:k4's secret2############################Update Plaintext Succeed!############################遇到的问题及解决方案Q: MD5/SHA1加密存储时候的类型不同引起的错误。A: 解决方案,多进行try/except使用抛出错误,定位错误,常用输出语句进行和预期值之间的排错,如下,md5加密后为元组形式,而sha1为str类型
    import hashlib#MD5和SHA1加密算法def md5(str1): md = hashlib.md5() md.update(str1) md_5=md.hexdigest() return md_5,def sha1(str1): sh = hashlib.sha1() sh.update(str1) sha_1 = sh.hexdigest() return sha_1print md5("123")print type(md5("123"))print sha1("123")print type(sha1("123"))运行后
    ('202cb962ac59075b964b07152d234b70',)<type 'tuple'>40bd001563085fc35165329ea1ff5c5ecbdbbeef<type 'str'>知道所出现的形式之后,对症下药就可以了!
    Q: 对数据库进行插入,删除,更新操作,数据库内容不改变问题A: 解决方案,没有进行事务提交!比如,我实现添加操作,最后需要添加语句commit
    cur.execute("insert into store(user_name,passwd,encrypt_words,encrypt_password) VALUES (%s,%s,%s,%s)",(user_name,passwd,encrypt_str,key_content))cur.connection.commit()#commit()提交事物,做出改变后必须提交事务,不然不能更新Q: 数据库出现Lock wait timeout exceeded错误,原因是如图

    A: 解决方案;这里应该运行时候断开以前运行的程序,这点我做的不好,调试的时候,以前的程序还在运行,全部断开连接,只要一个运行就行
    Q: 结构功能问题A: 需要实践积累,怎样实现目的,产生比较清晰的架构,子函数应该怎么写,才能最大程度的调用,这些我都比较弱,需要不断的进行学习和测试,我的框架结构也是改了很多次,都是进行测试之后慢慢修改成型的,考虑到了几乎所有的操作需求,你能信当时我只是想弄个加密写入和读取的玩意就行了么,最后还是写成比较完善的一个小项目了,所以,这个问题,只有不断练习把,不过下次我会先拟构好一个流程图框架再写。
    1 评论 0 下载 2019-04-24 21:42:08 下载需要10点积分
  • 基于QT的考试管理系统设计与实现

    一、项目概要1.1 项目名称
    考试管理系统
    1.2 项目目标
    培养快速学习新的知识,解决问题的能力规划项目的整体功能以及相关需求分析,并设计出合理的数据库,并熟悉整个试题系统的开发流程。
    1.3 软件概要开发一个考试管理系统,考生可以进行练习,和在线考试,管理员负责管理题库以及生成试卷,登陆主界面如图1.3所示。

    1.4 功能描述
    涉及到两个模块:学生登录和管理员登陆。
    基于学生的功能有:
    练习试题(此试题为题库中所有试题类型的所有题,考生可以任意答题且参考标准答案)
    在线考试(试卷从后台试卷库里面随机挑选,考生必须在指定时间内答完试题,交卷后显示考生成绩以及所用时间等信息)
    基于管理员的功能有:
    试题管理(管理题库中所有题,可以进行增删改查,支持关键字、难度等级查询)
    试卷管理(按照要求从题库随机挑选试题生成试卷,对已生成的试卷进行增删改查,支持成批生成试卷,成批删除试卷,根据试卷名,试卷内容,难度等级查询试卷,以及查看试卷内容)

    1.5 开发环境
    操作系统:Microsoft Windows 10开发环境:Qt Creator 8.1数据库:MySql Server 5.5
    1.6 关键技术
    面向对象设计与分析C++Qt数据库编程Qt信号槽
    二、软件详细需求2.1 学生功能主界面学生登录后进入如图2.1所示的界面,进行考试考试或者练习。

    2.2 管理员功能主界面管理员登陆后进入如图2-2所示界面,管理员可以对题库里的题增删改查,也可为学生在线考试随机组卷。

    2.3 学生在线考试系统实现学生进入考试系统,从已生成的试卷随机抽取答题,进入如图2-3-1所示界面,要求学生在规定的时间内答完试卷,可随机跳转试题,且将已做过或即时更新的的答案保存到数组。点击交卷或者退出考试将显示如图2-3-2所示界面,显示考试用时和考试成绩等信息。


    2.4 学生练习系统实现该考试练习从题库按照各种类型题抽取,考生可以切换题型,答完题也可查看正确答案。

    2.5 试题管理系统实现该试题管理系统将对题库类型题分类管理,每个类型题对应一个增删改查界面,如图2-5-2所示。



    题型
    添加题目
    修改题目
    删除题目
    查询题目




    选择题
    题目id自动增加,填写相关题干,abcd选项内容,答案,设置分数,等级难度等信息,点提交即可写入数据库
    页面显示题库该类型题所有信息,点击修改
    页面显示题目该类型题所有信息,选中行点击删除弹出是否删除页面,若确定则删除该题,可刷新页面
    支持难度和关键字以与关系的四种查询方式


    判断题
    题目id自动增加,填写内容题干,答案,设置分数,等级难度等信息,点击提交即可
    页面显示题库该类型题所有信息,点击修改
    页面显示题目该类型题所有信息,选中行点击删除弹出是否删除页面,若确定则删除该题,可刷新页面
    支持难度和关键字以与关系的四种查询方式


    填空题
    题目id自动增加,填写内容题干,输入空格数量,在下面随机生成,填写入对应空格即可设置分数,等级难度等信息,点击提交即可
    页面显示题库该类型题所有信息,点击修改,其中空格数量不可修改
    页面显示题目该类型题所有信息,选中行点击删除弹出是否删除页面,若确定则删除该题,可刷新页面
    支持难度和关键字以与关系的四种查询方式







    2.6 组卷系统主界面
    2.7 试卷生成实现可按照要求从题库随机抽选题型组成填写的试卷数量,要求所选题型与对应个数成绩相加等于总分,且题库里有该填写内容的要求的试题,否则弹出相关不满足要求的题型表,如图2-7-2所示,若不填知识点描述默认为综合,不选择难度等级则是随机。


    2.8 查看已生成试卷信息


    试卷查询
    支持试卷名,难度等级,知识点查询的三种方式随机组合查询




    现有试卷数
    始终随着查询,删除的更新变化,显示当前试卷数


    删除试卷
    选中行点击删除弹出是否删除试卷,若确定则删除该试卷,可刷新页面,支持成批删除(选中多行删除)


    查看试卷
    对选中试卷查看具体信息,显示试卷名,以及改试卷所有题型(使用QScrollArea控件显示)





    三、系统整体设计3.1 系统结构图
    3.2 模块要求
    功能界面层
    Qt主界面
    学生功能界面
    管理员功能界面
    DataBase MySql
    提供给上层的数据库访问,完成指定试卷试题学生管理员等数据信息的取得
    各种类型题已保存在数据库中
    对数据的添加、修改、删除,查询提供指定数据表

    四、登陆测试



    Accou:wrong Answ:wrong
    Accou:wrong Answer:right
    Account:right Answ:wrong
    Account:right Answer:right





    请选择身份
    请选择身份
    请选择身份
    请选择身份


    学生
    用户名或密码错误
    用户名或密码错误
    用户名或密码错误
    进入学生功能界面


    管理员
    用户名或密码错误
    用户名或密码错误
    用户名或密码错误
    进入管理员功能界面





    五、数据库设计(试卷管理系统)
    4 评论 66 下载 2018-10-05 22:46:51 下载需要15点积分
显示 15 到 30 ,共 15 条
eject