这是实习期间做的第二个小工具,帮助人事的采集职位报名人员的信息
敲代码前确实不太懂这方面,所以期间查询了不少资料,各种方式进行尝试和比较,中间碰到一些bug,但写完之后觉得挺简单的了,不知道这个小工具有没有长远作用,但还是先写在这分享给大家,自己也回顾复习下。
程序功能概述:
Java代码使用公司的账号密码登录到斗米网站,对发布的所有职位信息进行采集成表,而且对于每一个职位上的“报名管理”进去后的所有报名过的人员的信息进行采集。(斗米2017.4.26之前是可以下载报名列表的,但之后采集人员信息只能自己去复制粘贴了)
思路如下:
1、编写Java程序登录到斗米网站
我是直接登录到企业版的首页,登陆的网址是从网页的源码里得来的(注意:模拟登陆的网址与在网站的地址栏上登录时显示的网址不同)(https://vip.doumi.com/employer/user/ajaxlogin),使用的是HttpURLConnection的对象连接,将自己的账号密码写成字符串形式,如:String str=”phone_login=12345678911&passwd_login=123456”;写入到连接对象的输出信息流里面去OutputStream os = connection.getOutputStream();os.write(str.toString().getBytes(“UTF-8”));最好还是多写些头部请求信息到connection对象里面去,作用是模拟浏览器访问网站,获得cookie作为全局变量(以便之后网页跳转需要使用),之后需要真正的到达企业版的首页,使用网址(https://vip.doumi.com/managecenter)及cookie即可,这里样就不一一累述了,封装成方法GetCookie(),返回的是网页的源码内容,以流文件的形式进行读取,存在StringBuffer对象里面,使用代码如下:
//获取全局Cookie值,并返回企业版首页的网页内容
//得到全局变量cookie值
public static StringBuffer GetCookie() throws IOException{
URL url = new URL("https://vip.doumi.com/employer/user/ajaxlogin");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
String str="phone_login=12345678911&passwd_login=123456";
connection.setDoOutput(true);//允许连接提交信息
connection.setRequestMethod("POST");//网页提交方式“GET”、“POST”
connection.setRequestProperty("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
connection.setRequestProperty("Accept-Encoding", "gzip, deflate, sdch, br");
connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
connection.setRequestProperty("Cache-Control", "max-age=0");
connection.setRequestProperty("Connection", "keep-alive");
connection.setRequestProperty("Host", "vip.doumi.com");
connection.setRequestProperty("Referer", "http://www.doumi.com/wh/");
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36");
//json方式
OutputStream os = connection.getOutputStream();
//os.write(jsonParam.toString().getBytes());
os.write(str.toString().getBytes("UTF-8"));
os.flush();
//os.close();
String cookieVal = "";
String key = null;
//取cookie
for(int i = 1; (key = connection.getHeaderFieldKey(i)) != null; i++){
if(key.equalsIgnoreCase("set-cookie")){
cookieVal = connection.getHeaderField(i);
cookieVal = cookieVal.substring(0, cookieVal.indexOf(";"));
responseCookie = responseCookie + cookieVal + ";";
}
}
//System.out.println("cookie:" + responseCookie);
int responsecode = connection.getResponseCode();
//System.out.println("responsecode:"+responsecode);
//acces
URL url1 = new URL("https://vip.doumi.com/managecenter");
HttpURLConnection connection1 = (HttpURLConnection) url1.openConnection();
connection1.setRequestProperty("Cookie", responseCookie);//给服务器送登录后的cookie
BufferedReader br1 = new BufferedReader(new InputStreamReader(connection1.getInputStream(),"UTF-8"));
StringBuffer content1=new StringBuffer();
String line1= br1.readLine();
//System.out.println("登陆后:");
int j=0;
while (line1 != null) {
content1.append(line1+System.getProperty("line.separator"));
//System.out.println((j++)+"===="+new String(line1.getBytes()));
line1 = br1.readLine();
}
//System.out.println(content1.toString());
return content1;
}
2、解析出当前页面的所有发布职位的信息及获取下一页的链接的String
我是将之前获取到的网页内容放到该方法中进行解析,还是依旧用的正则表达式,另外,匹配到一条发布的职位就写入Sheet表中去(包括进入“报名管理”的链接,都会在Excel表中),再去获取下一页的链接信息(发布的职位太多,至少有五六页吧,所以要进行分页处理),并返回,在主方法main里进行判断是否为空,若不为空,使用public static StringBuffer GetHTML(String href)方法获得网页内容并继续使用该方法LookAllCareer():
public static String LookAllCareer(StringBuffer strb,WritableSheet sheet,WritableWorkbook wb) throws RowsExceededException, WriteException, IOException{
//得到下一页职位信息页面链接的正则,即用最后一个li判断是否有a标签,若有,则记下链接属性,若没有则停止
Pattern P=Pattern.compile("<div class=\"pageBox\">[\\s]*?<ul class=\"pagination\">[\\s\\S]*?</ul>[\\s]*?</div>");//[\\s]*?<li></li>[\\s]*?
//得到当前页面下所有职位的相关信息的正则
Pattern p1=Pattern.compile("<div class=\"bList-item-t\">[\\s]*?<a.*?>([^\n]*)</a>");
Pattern p2=Pattern.compile("<div class=\"bList-item-mid\">[\\s]*?<ul.*?>[\\s]*?<li>[\\s\\S]*?</li>[\\s]*?<li>[\\s\\S]*?</li>[\\s\\S]*?<li class=\"pubTime\">[\\s\\S]*?</li>[\\s]*?<li>[\\s\\S]*?</li>[\\s]*?<li>[\\s\\S]*?</li>[\\s]*?</ul>[\\s]*?</div>");
Pattern p3=Pattern.compile("<div class=\"bList-item-opBtn\">[\\s]*?<a.*?>([^\n]*)</a>[\\s]*?<a.*?>([^\n]*)</a>[\\s]*?</div>");
Matcher nextpage = P.matcher(strb);
Matcher m = p1.matcher(strb);
Matcher r = p2.matcher(strb);
Matcher s = p3.matcher(strb);
//得到单个职位报名管理信息的跳转链接
Pattern reg2=Pattern.compile("<li>[\\s]*?<span>([^\n]*)</span>([\\s\\S]*?)[\\s]*?</li>");
Pattern reg22=Pattern.compile("<li>[\\s]*?<span>([^\n]*)</span>[\\s]*?<em.*?>([^\n]*)</em>[\\s]*?</li>");
Pattern reg3=Pattern.compile("<a" + "[^<>]*?\\s" + "href" + "=['\"]?(.*?)['\"]?(\\s.*?)?>");
while(m.find() && r.find() && s.find()){
Matcher rr=reg2.matcher(r.group(0));
Matcher rrr=reg22.matcher(r.group(0));
Matcher ss=reg3.matcher(s.group(0));
if(rr.find() && rrr.find() && ss.find()){
Label label0 = new Label(0,i,m.group(1));
sheet.addCell(label0);
Label label1 = new Label(1,i,rr.group(2));
sheet.addCell(label1);
Label label2 = new Label(2,i,rrr.group(2));
sheet.addCell(label2);
Label label3 = new Label(3,i,ss.group(1));
sheet.addCell(label3);
i++;
//System.out.println(i+"==="+m.group(1)+","+rr.group(2)+","+rrr.group(2)+","+ss.group(1));
}
//System.out.println("职位名称:"+m.group(0)+","+r.group(0)+","+s.group(0));
}
//wb.write();
//得到发布职位的名称
//Pattern reg1=Pattern.compile("<a>[\\s]*?<span>([^\n]*)</span>([\\s\\S]*?)</a>");
Pattern Reg=Pattern.compile("<li class=\"active\">[\\s\\S]*?</li><li><a href"+"=['\"]?(.*?)['\"]?(\\s.*?)?>([\\s\\S])*?</a></li>");
if(nextpage.find()){
//System.out.println(nextpage.group(0));
Matcher NextPage=Reg.matcher(nextpage.group(0));
if(NextPage.find()){
//System.out.println(NextPage.group(1));
return NextPage.group(1);
}
}
return null;
}
//获取该链接下的网页内容,结合Cookie进行跳转
//根据Cookie及URL得到网页内容
public static StringBuffer GetHTML(String href) throws UnsupportedEncodingException, IOException{
URL url = new URL(href);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//System.out.println(responseCookie);
connection.setRequestProperty("Cookie", responseCookie);//给服务器送登录后的cookie
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
StringBuffer content=new StringBuffer();
int j=0;
String line1= br.readLine();
while (line1 != null) {
content.append(line1+System.getProperty("line.separator"));
//System.out.println((j++)+"===="+new String(line1.getBytes()));
line1 = br.readLine();
}
return content;
}
3、读取关于职位的Excel表格,有用的信息就是pid(斗米网站设置的职位标号)及“报名管理”的链接
事实上,在对职位表信息获取的同时也可以同时处理“报名管理”链接的信息,但我还是分开了,这样一步步的来简单很多,可能我这个做的有点繁琐了,但逻辑整体还是没问题的,就先这样做吧。
读取职位表的信息,使用方法public static void GetAllEnrollInfo(),根据链接继续使用public static StringBuffer GetHTML(String href)方法获得网页内容,再对内容进行解析public static void GetSingleEnrollInfo(String pid,StringBuffer content,WritableWorkbook wb,WritableSheet sheet),获取报名人员的信息,并存入Excel表中。代码如下:
//读取DoumiCareer表中的信息,获取所有职位的报名人员的信息,并写入DoumiResume表中
//读取Excel表中每个职位的的报名管理链接,跳转到报名管理的页面,得到所有页面的报名人员信息
public static void GetAllEnrollInfo() throws BiffException, IOException, RowsExceededException, WriteException{
//int j=1;
Calendar c = Calendar.getInstance();//可以对每个时间域单独修改
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH)+1;
int date = c.get(Calendar.DATE);
String path="C:\\resume\\DoumiCareer"+year+"."+month+"."+date+".xlsx";
InputStream readfile = new FileInputStream(path);
Workbook rexcel = Workbook.getWorkbook(readfile);
//这里有两种方法获取sheet表:名字和下标(从0开始)
//Sheet st = rwb.getSheet("original");
Sheet st = rexcel.getSheet(0);
//创建DoumiResume.xlsx,将报名人员信息写入Excel表中
String setpath="C:\\resume\\DoumiResume1"+year+"."+month+"."+date+".xlsx";
WritableWorkbook wb = Workbook.createWorkbook(new File(setpath));
WritableSheet ws = wb.createSheet("Sheet1", 0);
Label label0 = new Label(0,0,"姓名");
ws.addCell(label0);
Label label1 = new Label(1,0,"性别");
ws.addCell(label1);
Label label2 = new Label(2,0,"年龄");
ws.addCell(label2);
Label label3 = new Label(3,0,"联系电话");
ws.addCell(label3);
Label label4 = new Label(4,0,"报名时间");
ws.addCell(label4);
x++;
for(int j=1;j<i;j++){
Cell cell1=st.getCell(1, j);//当前网页的职位ID(pid)
Cell cell3=st.getCell(3, j);//获取当前职位的网页链接
System.out.println(cell3.getContents());
StringBuffer content=GetHTML(cell3.getContents());
//System.out.println(content);
GetSingleEnrollInfo(cell1.getContents(),content,wb,ws);
}
//关闭输入流及读取信息的表
readfile.close();
rexcel.close();
//写入报名信息到表中并关闭
wb.write();
wb.close();
}
//根据报名管理的网页内容,匹配到所有报名人员的信息
public static void GetSingleEnrollInfo(String pid,StringBuffer content,WritableWorkbook wb,WritableSheet sheet) throws IOException, RowsExceededException, WriteException{
//System.out.println(content);
//匹配到下一页指向的网址,并返回
Pattern P=Pattern.compile("<div class=\"pageBox\"></div>");
Matcher M=P.matcher(content);
//匹配到所有报名人员的相关信息:姓名,性别,年龄,使用aid和pid请求Ajax得到联系电话// class=\"fc-4b mr5 hover-after\" [\\s\\S]
Pattern q1=Pattern.compile("<em.*?>[\\s]*?<span.*?>[\\s]*?([\\S]*?)[\\s]*?</span>[\\s]*?</em>[\\s]*?<span.*?></span>[\\s]*?<span class=\"mr5\">([^\n]*?)</span>[\\s]*?<span class=\"mr5\">([^\n]*?)</span>");
Pattern q2=Pattern.compile("<td class=\"read-phone-"+"([\\S]*?)\">[\\s]*([\\s\\S]*?)[\\s]*</td>[\\s]*?<td>[\\s]*([\\s\\S]*?)[\\s]*</td>");//([\\S]*?)[\\s]*<div.*?>([\\s\\S]*?)</div>
//匹配到姓名,性别,年龄
Matcher m1=q1.matcher(content);
//匹配到read-phone标签的aid和pid
Matcher m2=q2.matcher(content);
while(m1.find() && m2.find()){
//获取报名时间
String bmtime=null;
if(m2.group(3).indexOf("div")>-1){
Pattern q22=Pattern.compile("([^\n]*?)[\\s]*<div class=\"b-ico-time\">([\\s\\S]*?)</div>");
Matcher m22=q22.matcher(m2.group(3));
//label4=new Label(4,x,m.group(1));
if(m22.find()){
//System.out.println(m22.group(1));
bmtime=m22.group(1);
}
else{
bmtime="";
}
}
else{
bmtime=m2.group(3);
}
//判断是否为当天时间
if(bmtime.indexOf(":")>-1){
Label label0 = new Label(0,x,m1.group(1));
Label label1 = new Label(1,x,m1.group(2));
Label label2 = new Label(2,x,m1.group(3));
sheet.addCell(label0);
sheet.addCell(label1);
sheet.addCell(label2);
//System.out.println(m1.group(1)+","+m1.group(2)+","+m1.group(3));
Label label3;
if(m2.group(2).indexOf("查看电话")>-1){
String aid=m2.group(1);
//System.out.println();//获取aid并进行处理
String tel=CheckTelphone(pid,aid);
label3=new Label(3,x,tel);
//sheet.addCell(label3);
//System.out.println(tel);
}
else{
label3=new Label(3,x,m2.group(2));
//sheet.addCell(label3);
}
sheet.addCell(label3);
Label label4 = new Label(4,x,bmtime);
sheet.addCell(label4);
x++;
}
else continue;
}
}
(以上我存入表的判断是只存入当天的报名人员信息,而且表的名字也是系统当天的时间)。
4、对“隐形”手机号进行处理的方法
因为进去网址后看到的会是“查看电话”的隐形号码,需要点击一下才能看到,所以在得到的源码里面并没有显示出来,这是使用了ajax的异步请求技术,这时就需要自己模拟浏览器提交请求得到数据了,需要先去网页查看请求提交的数据规律就可以了,我这次做的是只需要两个数据就行了,即pid和aid:
//对pid和aid进行处理获得手机号
public static String CheckTelphone(String pid,String aid) throws IOException{
String tel=null;
URL url=new URL("https://vip.doumi.com/employer/manage/readphone");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Cookie", responseCookie);//给服务器送登录后的cookie
connection.setDoOutput(true); //通过把URLConnection设为输出,你可以把数据向你个Web页传送。
String str="aid="+aid+"&pid="+pid;
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
out.write(str.toString()); //向页面传递数据。post的关键所在!
// remember to clean up
out.flush();
out.close();
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
StringBuffer content=new StringBuffer();
String line= br.readLine();
Pattern p=Pattern.compile("\"data\":\\{\""+aid+"\":\"([\\S]*?)\"}}");//{符号前面加了两杠,不加的话会出错
Matcher m=p.matcher(line);
if(m.find()){
tel=m.group(1);
//System.out.println(m.group(1));
}
return tel;
}
5、头文件及主方法main调用的情况
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;
public static String responseCookie = "";
public static int i=0,x=0;//i是用来记录发布的职位数,x是用来记录报名人员的人数
public static void main(String[] args) throws IOException, RowsExceededException, WriteException, BiffException {
// TODO Auto-generated method stub
Calendar c = Calendar.getInstance();//可以对每个时间域单独修改
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH)+1;
int date = c.get(Calendar.DATE);
String path="C:\\resume\\DoumiCareer1"+year+"."+month+"."+date+".xlsx";//使用当天的时间来命名文件名
WritableWorkbook wb = Workbook.createWorkbook(new File(path));
WritableSheet ws = wb.createSheet("Sheet1", 0);//创建Excel表并打开
Label label0 = new Label(0,0,"职位名");
ws.addCell(label0);
Label label1 = new Label(1,0,"职位ID");
ws.addCell(label1);
Label label2 = new Label(2,0,"发布城市");
ws.addCell(label2);
Label label3 = new Label(3,0,"报名链接");
ws.addCell(label3);
i++;
//GetCookie()方法得到Cookie值作为全局变量,便于之后跳转网页是随时使用,最后返回网页内容
StringBuffer str=GetCookie();
//解析网页内容,得到当前页面所有职位的部分信息
String hrefs=LookAllCareer(str,ws,wb);
//判断是否有下一页的链接,若不为null,则继续获取网页内容,并解析
while(hrefs!=null){
String href=hrefs.replaceAll("amp;","");
System.out.println(href);
StringBuffer content=new StringBuffer();
//获取链接下网页的内容
content=GetHTML(href);
hrefs=LookAllCareer(content,ws,wb);
}
//将信息写入表中并关闭
wb.write();
wb.close();
//根据上面建立的表,得到所有报名人员的信息的方法
GetAllEnrollInfo();
}
(类名就自己随机取名吧,对内部无影响)
目前的功能需求已经初步完成,后期boss如果需要再添加其他功能的话我再来补充。。。。。。
欢迎大家随时来指正交流,如果有大牛有更好的更简洁的方法,望能指导!!!