用java模拟登录正方教务系统,抓取课表和个人成绩等数据
之前学了一些java web的编程,理解了web应用的原理后,就突然想到,可以用java模拟登录吉珠的教务系统,然后爬取里面的课表、成绩、个人信息等等数据,然后就可以写成一个简易的课表APP。
一、第三方工具
这里我用到了httpClient这个第三方包,相信很多人都认识这个包,不认识的话可以自行百度一下,httpClient和传统的URLConnection相比,更加强大,更加灵活,更加易用,我用的是httpcomponents-client-4.5.2,下载地址:http://hc.apache.org/downloads.cgi
二、思路描述
这里就拿获取课表的例子说一下,首先,要登录教务系统,就要获取登录的验证码,然后输入学号密码和验证码后,向教务网发起登录请求,接着要做的就是维持登录状态,登录状态是靠cookie里的sessionID这个参数维持的,只需要保存cookie就行了,在后续的所有提交的请求里,只要都带着cookie一起提交,就能保持在登录状态,保证获取正确的页面,在登录成功后,就在响应页面里找到查询个人课表的链接,然后再提交查询课表的请求,得到的响应就是个人课表的页面,这时就直接抓取课表部分的内容就行了。至于怎么准确抓取相应的数据,当然是用正则表达式了,不懂正则表达式的自行百度。
三、页面分析
四、代码实现
/*** 常量类* @author EsauLu**/
public class Constant {/*** 验证码为空*/public static final String CHECK_NULL_ERROR="验证码不能为空,如看不清请刷新!!";/*** 验证码不正确*/public static final String CHECK_ERROR="验证码不正确!!";/*** 密码错误*/public static final String PASSWD_ERROR="密码错误!!";/*** 字符编码*/public static final String ENCODING="GB2312";/*** 用户类型*/public static final String RADIO_BUTTON_LIST="学生";/*** 教务网地址*/public static final String BASE_URL="http://jw.jluzh.com";/*** 验证码URL*/public static final String CHECK_IMAGE_URL=BASE_URL+"/CheckCode.aspx";/*** 登陆URL*/public static final String LOGIN_URL=BASE_URL+"/default2.aspx";/*** 登陆后主页面URL*/public static final String STUDENT_URL=BASE_URL+"/xs_main.aspx?xh=";}
import org.apache.http.client.entity.UrlEncodedFormEntity;
import com.jluzh.jw.bean.CourseTable;
import com.jluzh.jw.bean.PersonalInfo;
import com.jluzh.jw.bean.User;/*** 获取数据的接口* @author EsauLu**/
public interface HttpInterface { /*** 初始化,主要用于收集cookie和viewState*/public void init();/*** 根据指定url发送给请求* @param url 请求url* @param ref 引用* @return 响应页面的HTML文档*/public StringBuffer sendGetRequest(String url,String ref);/*** 根据指定url和参数值发送post请求* @param url 请求url* @param ref 引用* @param entity 参数列表* @return 响应页面的HTML文档*/public StringBuffer sendPostRequest(String url,String ref, UrlEncodedFormEntity entity);/*** 获取验证码* @return 验证码图片*/public byte[] getCheckImg();/*** 登陆* @param user 用户信息* @return 返回登陆是否成功*/public boolean login(User user);/*** 根据学年度和学期获取课表* @param xnd 学年度* @param xqd 学期* @return 课表*/public CourseTable getCourseTable(String xnd,String xqd);/*** 根据学年度和学期获取课表的Json串* @param xnd 学年度* @param xqd 学期* @return 课表Json串*/public String getCourseTableAsJson(String xnd,String xqd);/*** 获取个人信息* @param url 查询个人信息的url* @return 个人信息*/public PersonalInfo getPersonalInfo(String url);/*** 获取错误信息* @return 返回错误信息*/public String getErrorMessege();}
然后下面就是接口的实现。需要说明一下的是,HttpService这个实现类做了维持登录的工作,就是把cookie保存起来。在初始化时,init()函数里首先访问一下教务网,然后记录cookie,cookie里有个参数ASP.NET_SessionId的参数,维持登录靠的就是这个参数。然后在获取验证码、登录、获取课表等的函数实现里,提交的请求都要设置cookie域,否则的话响应内容将是登录页面(这个知道web后端开发的都懂吧)。
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;import com.jluzh.jw.bean.Course;
import com.jluzh.jw.bean.CourseTable;
import com.jluzh.jw.bean.PersonalInfo;
import com.jluzh.jw.bean.StuSimpleInfo;
import com.jluzh.jw.bean.User;
import com.jluzh.jw.constant.Constant;
import com.jluzh.jw.tool.HtmlTools;public class HttpService implements HttpInterface {/*** Http客户端*/private CloseableHttpClient mHttpClient;/*** 记录cookie*/private String mCookie;/*** 记录正方教务系统页面表单的__VIEWSTATE的值*/private String mViewState;/*** 已登陆用户的信息*/private User mUser;/*** 登陆错误信息*/private String mErrorMessege;/*** 查询课程表信息的URL*/private String mCourseURL;/*** 查询个人信息的URL*/private String mPersonalInfoURL;/*** 查询成绩表的URL*/private String mScorceURL;/*** 构造函数*/public HttpService() {// TODO Auto-generated constructor stubthis.mErrorMessege="no error";mHttpClient=HttpClients.createDefault();init();}@Overridepublic void init() {// TODO Auto-generated method stubString url=Constant.BASE_URL;try {HttpGet httpGet=new HttpGet(url);CloseableHttpResponse response=mHttpClient.execute(httpGet);//提交请求获得响应mCookie=response.getFirstHeader("Set-Cookie").getValue(); //获取cookieStringBuffer sb=sendGetRequest(url,null); //发送访问请求并获得响应页面mViewState=HtmlTools.findViewState(sb.toString()); //提取页面表单中的__VIEWSTATE的值} catch (ClientProtocolException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublic StringBuffer sendGetRequest(String url,String ref) {// TODO Auto-generated method stubStringBuffer sb=new StringBuffer();InputStream in=null;try { HttpGet httpGet=new HttpGet(url);httpGet.setHeader("Cookie", mCookie);//设置cookie if(ref!=null&&!ref.equals("")){httpGet.setHeader("Referer",ref);//如果有地址引用则设置}CloseableHttpResponse response=mHttpClient.execute(httpGet);//提交请求获得响应in=response.getEntity().getContent();//获取响应内容if(in!=null){int len=-1;byte[] data=new byte[1024];while((len=in.read(data))!=-1){String s=new String(data,0,len,Constant.ENCODING);sb.append(s);}}} catch (Exception e) {// TODO: handle exception}finally{try {if(in!=null){in.close();}} catch (Exception e2) {// TODO: handle exception}}return sb;}@Overridepublic StringBuffer sendPostRequest(String url, String ref, UrlEncodedFormEntity entity) {// TODO Auto-generated method stubStringBuffer sb=new StringBuffer();HttpPost httpPost=new HttpPost(url);InputStream in=null; try {httpPost.setHeader("Cookie", mCookie); //设置cookie if(ref!=null&&!ref.equals("")){httpPost.setHeader("Referer",ref);//如果有地址引用则设置}httpPost.setEntity(entity);//设置请求参数CloseableHttpResponse response=mHttpClient.execute(httpPost);//提交请求in=response.getEntity().getContent();//获得响应流对象//获取响应内容int len=-1;byte[] data=new byte[1024];while((len=in.read(data))!=-1){String s=new String(data,0,len,Constant.ENCODING);sb.append(s);}} catch (Exception e) {// TODO: handle exception}finally{try {if(in!=null){in.close();}} catch (Exception e2) {// TODO: handle exception}} return sb;}@Overridepublic byte[] getCheckImg() {// TODO Auto-generated method stubString url=Constant.CHECK_IMAGE_URL;HttpGet httpGet=new HttpGet(url);byte[] imgByte=null;try {CloseableHttpResponse response=mHttpClient.execute(httpGet);imgByte=EntityUtils.toByteArray(response.getEntity());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return imgByte;}@Overridepublic boolean login(User user) {// TODO Auto-generated method stub//组织登陆请求参数ArrayList<BasicNameValuePair> params=new ArrayList<BasicNameValuePair>();params.add(new BasicNameValuePair("__VIEWSTATE", mViewState));//__VIEWSTATE,不可缺少这个参数params.add(new BasicNameValuePair("txtUserName", user.getName()));//学号params.add(new BasicNameValuePair("TextBox2", user.getPasswd()));//密码params.add(new BasicNameValuePair("txtSecretCode",user.getCheck()));//验证码params.add(new BasicNameValuePair("RadioButtonList1", Constant.RADIO_BUTTON_LIST));//登陆用户类型params.add(new BasicNameValuePair("Button1", ""));params.add(new BasicNameValuePair("lbLanguage", ""));params.add(new BasicNameValuePair("hidPdrs", ""));params.add(new BasicNameValuePair("hidsc", ""));try {UrlEncodedFormEntity entity=new UrlEncodedFormEntity(params,Constant.ENCODING); //封装成参数对象 StringBuffer sb=sendPostRequest(Constant.LOGIN_URL,null, entity);//发送请求mUser=user;//记录登陆的用户String html=sb.toString();//检测是否有登陆错误的信息,有则记录信息,否则登陆成功if(html.contains(Constant.CHECK_ERROR)){mErrorMessege=Constant.CHECK_ERROR;}else if(html.contains(Constant.CHECK_NULL_ERROR)){mErrorMessege=Constant.CHECK_NULL_ERROR;}else if(html.contains(Constant.PASSWD_ERROR)){mErrorMessege=Constant.PASSWD_ERROR;}else{ //登陆成功,重定向获取主页面StringBuffer userHtml=sendGetRequest(Constant.STUDENT_URL+user.getName(),null);saveQueryURL(userHtml.toString()); //根据响应内容找到并保存查询各种信息的URL return true;//返回登陆成功}} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}return false;}@Overridepublic CourseTable getCourseTable(String xnd,String xqd) {// TODO Auto-generated method stubString url=Constant.BASE_URL+mCourseURL;//查询课表的URLString referer=Constant.STUDENT_URL+mUser.getName();//引用地址StringBuffer courseHtml=null;CourseTable couresTable=new CourseTable();try {//没有学年度和学期的的信息,则发送get请求,否则发送post请求if(xnd==null||xqd==null){courseHtml=sendGetRequest(url, referer);}else{//记录post参数后发送请求ArrayList<BasicNameValuePair> params=new ArrayList<BasicNameValuePair>();params.add(new BasicNameValuePair("__EVENTTARGET", "xqd"));params.add(new BasicNameValuePair("__EVENTARGUMENT", ""));params.add(new BasicNameValuePair("__VIEWSTATE", mViewState));params.add(new BasicNameValuePair("xnd", xnd));params.add(new BasicNameValuePair("xqd", xqd));UrlEncodedFormEntity entity=new UrlEncodedFormEntity(params,Constant.ENCODING);courseHtml=sendPostRequest(url, referer, entity);}String html=courseHtml.toString(); //响应HTML文档 mViewState=HtmlTools.findViewState(html);//记录__VIEWSTATEStuSimpleInfo stuInfo=HtmlTools.getStuInfo(html);String[] xnds=HtmlTools.getXnd(html); //在响应内容中获取学年度选项列表 String[] xqds=HtmlTools.getXqd(html); //在响应内容中获取学期选项列表 ArrayList<Course> courses=HtmlTools.getCourseList(html); //在响应内容中获取课表//如果传进来的学年度和学期参数为空,则使用默认选项if(xnd==null&&xnds.length>0) xnd=xnds[0]; if(xqd==null&&xqds.length>0) xqd=xqds[0];//保存获取到的所有信息couresTable.setXnd(xnds);couresTable.setXqd(xqds);couresTable.setCurrXnd(xnd);couresTable.setCurrXqd(xqd); couresTable.setCourses(courses);couresTable.setSimpleInfo(stuInfo);} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}return couresTable;}@Overridepublic String getCourseTableAsJson(String xnd, String xqd) {// TODO Auto-generated method stubCourseTable courseTable=getCourseTable(xnd, xqd);StringBuffer sb=new StringBuffer();sb.append("{");for(Course c:courseTable.getCourses()){sb.append("\n\t{");sb.append("\n\t\t\"name\":\""+c.getName()+"\"");sb.append("\n\t\t\"classRoom\":\""+c.getClassRoom()+"\"");sb.append("\n\t\t\"teacher\":\""+c.getTeacher()+"\"");sb.append("\n\t\t\"classTime\":\""+c.getClassTime()+"\"");sb.append("\n\t\t\"weekNum\":\""+c.getWeekNum()+"\"");sb.append("\n\t\t\"startWeek\":\""+c.getStartWeek()+"\"");sb.append("\n\t\t\"endWeek\":\""+c.getEndWeek()+"\"");sb.append("\n\t\t\"weekState\":\""+c.getWeekState()+"\"");sb.append("\n\t\t\"number\":\""+c.getNumber()+"\"");sb.append("\n\t\t\"day\":\""+c.getDay()+"\""); sb.append("\n\t}\n");}sb.append("}");return sb.toString();}@Overridepublic PersonalInfo getPersonalInfo(String url) {// TODO Auto-generated method stubreturn null;}@Overridepublic String getErrorMessege() {// TODO Auto-generated method stubreturn mErrorMessege;}/*** 查找并保存查询各种信息的URL* @param html HTML文档*/private void saveQueryURL(String html) {// TODO Auto-generated method stub String pattern="<a href=\"(\\w+)\\.aspx\\?xh=(\\d+)&xm=(.+?)&gnmkdm=N(\\d+)\" target='zhuti' οnclick=\"GetMc\\('(.+?)'\\);\">(.+?)</a>";Pattern p=Pattern.compile(pattern);Matcher m=p.matcher(html);while(m.find()){String res=m.group();String url=res.substring(res.indexOf("href=\"")+6);url=url.substring(0,url.indexOf("\""));url="/"+url;if(res.contains("学生个人课表")){mCourseURL=url;continue;}if(res.contains("成绩查询")){mScorceURL=url;continue;}if(res.contains("个人信息")){mPersonalInfoURL=url;}}}public User getmUser() {return mUser;}}
上面的HttpService里用到一个HtmlTools类,这个类里包含了一些了的静态方法,是专门用来解析响应页面的HTML里面的信息的,代码如下
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import com.jluzh.jw.bean.Course;
import com.jluzh.jw.bean.StuSimpleInfo;
import com.jluzh.jw.factor.BeanFactor;/*** HTML工具类,主要用于提取HTML文档中的信息* @author EsauLu**/
public class HtmlTools {private static final int FIND_XND=1;//学年度private static final int FIND_XQD=2;//学期/*** 查找__VIEWSTATE参数的值* @param html HTML文档* @return 返回*/public static String findViewState(String html) {String res="";String pattern="<input type=\"hidden\" name=\"__VIEWSTATE\" value=\"(.*?)\" />";Pattern p=Pattern.compile(pattern);Matcher m=p.matcher(html);if(m.find()){res=m.group();res=res.substring(res.indexOf("value=\"")+7,res.lastIndexOf("\""));}return res;}/*** 查找课表部分的HTML字符串* @param html HTML文档* @return 课表的HTML串*/private static String findCourseTableHtml(String html){String res="";String tar="<table id=\"Table1\" class=\"blacktab\" bordercolor=\"Black\" border=\"0\" width=\"100%\">";String pattern="<table id=\"Table1\" class=\"blacktab\" bordercolor=\"Black\" border=\"0\" width=\"100%\">([\\S\\s]+?)</table>"; Pattern p=Pattern.compile(pattern);Matcher m=p.matcher(html); if(m.find()){res=m.group(0);res=res.substring(res.indexOf(tar)+tar.length(),res.lastIndexOf("</table>")).trim();}else{System.out.println(html);System.out.println("什么都没有");}return res;}/*** 在HTML中提取学年度或者学期的选项的HTML记录串* @param html HTML文档* @param x 代表学期或者学年度的参数* @return 返回HTML表示的学年度或者学期选项*/private static String findXndOrXqdHtml(String html,int x){String res="";String pattern=null;switch(x){case FIND_XND:pattern="<select name=\"xnd\" οnchange=\"__doPostBack\\('xnd',''\\)\" language=\"javascript\" id=\"xnd\">([\\s\\S]+?)</select>";break;case FIND_XQD:pattern="<select name=\"xqd\" οnchange=\"__doPostBack\\('xqd',''\\)\" language=\"javascript\" id=\"xqd\">([\\s\\S]+?)</select>";break;}Pattern p=Pattern.compile(pattern);Matcher m=p.matcher(html); if(m.find()){res=m.group(1);}return res.trim();}/*** 在HTML中提取学年度或者学期的选项列表* @param html HTML文档* @param x 代表学年度或者学期* @return 返回学年度或者学期的选项数组*/private static String[] getOptions(String html,int x){String[] ops=null;String res="";String tar=findXndOrXqdHtml(html, x);ArrayList<String> arr=new ArrayList<String>();String pattern="<option([\\s\\S]*?)>(.*?)</option>"; Pattern p=Pattern.compile(pattern);Matcher m=p.matcher(tar); while(m.find()){res=m.group(2);arr.add(res);}ops=new String[arr.size()];for(int i=0;i<ops.length;i++){ops[i]=arr.get(i);}return ops;}/*** 获取学年度选项* @param html HTML文档* @return 返回学年度选项数组*/public static String[] getXnd(String html){ return getOptions(html, FIND_XND);} /*** 获取学期选项数组* @param html HTML文档* @return 学期选项数组*/public static String[] getXqd(String html){ return getOptions(html, FIND_XQD);}/*** 获取学生简要信息* @param html HTML文档* @return 返回学生简要信息数组*/public static StuSimpleInfo getStuInfo(String html){String[] info=null;//(<span id="([\\s\\S]+?)">([\\s\\S]+?)</span>\\|)?<span id="(.+)">(.+?)</span>String res="";String pattern="<TR class=\"trbg1\">"+ "([\\s\\S]*?)<TD>([\\s\\S]*?)"+ "<span id=\"Labe(.+?)\">([\\s\\S]+?)</span>(\\|([\\s\\S]*?)<span id=\"Labe(.+?)\">([\\s\\S]+?)</span>)*"; Pattern p=Pattern.compile(pattern);Matcher m=p.matcher(html); if(m.find()){res=m.group(0);info=res.split("\\|");pattern="<span id=\"Labe(.+?)\">([\\s\\S]+?):([\\s\\S]+?)</span>";p=Pattern.compile(pattern);for(int i=0;i<info.length;i++){m=p.matcher(info[i]);if(m.find()){info[i]=m.group(3);}else{info[i]="";}}}return BeanFactor.createStuSimpleInfo(info);}/*** 在HTML文档中提取课表* @param html HTML文档* @return 返回课表*/public static ArrayList<Course> getCourseList(String html){String courseTableHtml=findCourseTableHtml(html);//找到课表部分的HTMLArrayList<Course> courses=new ArrayList<Course>();String[] rows=courseTableHtml.split("</tr><tr>");//按上课时间分隔HTMLfor(int i=2;i<rows.length;i+=2){String r=rows[i];String[] cols=r.split("</td><td([\\S\\s]*?)>");//按星期几分隔HTMLint j=1;if(i==2||i==6||i==10){j=2;}int x=1;for(;j<cols.length;x++,j++){String c=cols[j];String[] info=c.split("<br>");if(info[0].contains(" ")) continue; String[] tem=new String[4];int t=0;for(int k=0;k<info.length;k++){String item=info[k].trim();if(item.equals("")){//处理同一时间不同周数的课程t=0;tem=new String[4];continue;}tem[t++]=item;if(t==4){Course course=BeanFactor.createCourse(tem, i-1, x);courses.add(course);}}}}return courses;} }
然后还有一些封装类的代码就不贴上来了,后面有完整代码的链接
五、总结
另外,我还依照这些思路写了一个安卓的课表APP,由于安卓的一些特性,写这个APP时并没有采用HttpClient这个包而是用了另一个包OkHttpClient,这个课表APP的源码地址: https://github.com/EsauLu/CourseTable
用java模拟登录正方教务系统,抓取课表和个人成绩等数据相关推荐
- 【Android+OkHttp3+Jsoup】 模拟登录教务系统 抓取课表和成绩
原文链接:https://blog.csdn.net/u013347241/article/details/52711018 今天这篇文章为大家带来的是模拟登录教务系统并抓取课表和成绩的详细实现过程. ...
- 用 Python 实现模拟登录正方教务系统抢课
(点击上方蓝字,快速关注我们) 作者:小苏打 https://vhyz.me/2018/06/12/用Python实现模拟登录正方教务系统抢课/ 最近学校开始选课,但是如果选课时间与自己的事情冲突,这 ...
- JAVA使用HttpClient模拟登录正方教务系统,爬取学籍信息和课程表成绩等,超详细登录分析和代码注解
目录 前言 分析 代码实现 第一次GET POST登录 第二次Get 第三次GET 第四次GET 第五次GET 测试 完整代码 前言 最近在做一个APP,需要获取我们学校--武汉纺织大学皇家停水断电断 ...
- python模拟登录教务系统_用Python实现模拟登录正方教务系统抢课
最近学校开始选课,但是如果选课时间与自己的事情冲突,这时候就可以使用Python脚本自助抢课,抢课的第一步即是模拟登录,需要模拟登录后保存登录信息然后再进行操作. 而且整个流程是比较简单,这是因为正方 ...
- 用Python实现模拟登录正方教务系统抢课
最近学校开始选课,但是如果选课时间与自己的事情冲突,这时候就可以使用Python脚本自助抢课,抢课的第一步即是模拟登录,需要模拟登录后保存登录信息然后再进行操作. 而且整个流程是比较简单,这是因为正方 ...
- asp 退出登录修改cookie能进入后台_用Python实现模拟登录正方教务系统抢课
6月23日更新:由于国内高校正方教务系统或多或少都会有所不同,所以细节地方还是需要自己修改的,我这个过程也只是一个案例,但这其中的本质上是不变的,即是抓包分析. 如果有什么不懂的,可以在评论区评论,或 ...
- python抢课脚本 验证码_用Python实现模拟登录正方教务系统抢课
最近学校开始选课,但是如果选课时间与自己的事情冲突,这时候就可以使用Python脚本自助抢课,抢课的第一步即是模拟登录,需要模拟登录后保存登录信息然后再进行操作. 而且整个流程是比较简单,这是因为正方 ...
- python多次登录教务系统_python3模拟登录正方教务系统
刚学了大概两周的python ,顺便搞了下爬虫.试着用python模拟登录学校的教务系统. 看了个教程,附上链接 参照教程做了一下,发现系统经过一些变动,原教程还有很多可以改进的地方 准备工作 先来看 ...
- Java模拟登录正方教务管理系统
概述 这里我仅仅就说说怎么登录,后续有时间再更新怎么查成绩.课表等 工具 OkHttpClient(这个联网框架还不错) 抓包工具(这里演示的是使用chrome自带的) 流程简述 抓包,查看请求头,确 ...
最新文章
- Makefile 实现工程的本地部署
- 使用 Castal DynamicProxy 简化 Silverlight 数据绑定
- php5.6扩展编写,php 5.6版本中编写一个PHP扩展的简单示例
- 十篇论文攻克自然语言处理底层原理!推荐大家复现!
- 基于OHCI的USB主机 —— USB设备其它数据结构
- 断点下载的主要知识总结。
- 数据分析师必备的python包
- Git develop分支的一些操作
- arm汇编语言中bne 1b的意思
- 2018年结婚大数据来了:深圳离婚率高居第三,原因竟然是……
- Oracle分页查询存储过程(适用于单表查询)
- Bitvise SSH Client下载
- springboot 处理put请求参数
- 软件产品检测测试报告找谁做?出测试报告机构推荐
- 分享72个商务商城PHP源码,总有一款适合你
- 使用ajax发送数组请求,Ajax请求传递数组参数
- 51单片机 蜂鸣器播放提示音
- js 获取鼠标在画布的位置_JS获取鼠标位置(鼠标坐标)
- 【搜索与回溯算法】保卫农场(DFS)
- 从滚动条到画布的几个代码文件——Python学习笔记之十七
热门文章
- 微信小程序商城开发功能构架介绍
- IDEA之JDBC使用教程
- 8-14外部排序-置换选择排序
- C# 解析torrent文件
- 如何在以太坊浏览器上上传发布合约源码(合并上传单文件方式)
- 从零开始单排学设计模式「装饰模式」黑铁 I
- Leetcode答题 --- 独一无二的出现次数
- 三种快速转换PDF为TXT的方法:简单、高效、免费
- 电影《南京》观后感 -- 一个遗忘历史的民族终究也会被历史所唾弃
- python控制结构(一)if分支入门_【2020Python修炼记12】Python语法入门—流程控制(if分支结构+while/for循环结构)...