一. 爬虫数据获取相关说明 1. 数据来源: 拉勾网
2. 数据量: 越大越好(初步测试用java大数据岗位搜索到了2500页的数据)
3. 数据爬取方式 由于网站上是通过ajax请求访问得到的数据,所以我们选择伪装请求进行访问
4. 初步页面显示数据结构:
positionId = ‘’ #岗位id
positionName = ‘’ #岗位名称
salary = ‘’ # 薪水
education = ‘’ # 学历
positionAdvantage = ‘’ # 岗位优势
firstType = ‘’ # 岗位类型
skillLables = ‘’ #技能标签
jobNature = ‘’ # 是否全职
workYear = ‘’ # 工龄
companyFullName = ‘’ # 公司全称
city = ‘’ #公司城市
industryField = ‘’ # 领域
companyLabelList = ‘’ # 公司标签
companySize = ‘’ # 公司规模
financeStage = ‘’ # 公司资金状况,是否融资等
5. 工作地点具体信息数据结构
advantage 职位优势
job-detail 工作职责/任职资格
job-address 工作地点
company-url 有意向联系我们
. 6.数据爬取过程中遇到的问题及解决方法(这里反爬虫技术可以写很多,比如伪ip,切换cookies,延时访问等方法)
通过requests.post请求访问网页数据的时候,伪装头文件传输过去的数据无法被识别到,导致多次请求到的数据都只是初始的头文件返回来的数据
解决方法:直接将需要更新修改的数据添加在网页链接上传输到后台请求数据,多次尝试通过对网页重定向理解得到的解决方法。
网页数据只显示30页的数据如何获取到更多
解决方法:网页上虽然只显示了30页,但是通过在后台伪装头文件发现是可以获取到其数据库中未显示在网页上的数据的
批量获取具体信息的时候访问多条后就程序自动停止,但是将停止的数据单独获取则没有问题
问题原因:反爬虫机制,一次大量访问后封锁ip需要验证码登录
解决方法: 中使用休眠方法但无效 访问一定条数之后使用伪ip访问
程序报异常,打印请求返回的数据时显示“{“status”:false,”msg”:”您操作太频繁,请稍后再访问”,”clientIp”:”218.206.101.37”,”state”:2402}”
问题原因:网站设置了cookies拦截,对于手动设置的cookie模拟浏览器访问的爬虫程序自动进行了拉黑,并且不登录就无法查看所有的数据
解决方法: 如果登录获取数据的话需要用到大量的拉勾网账号,比较复杂。这里通过使用手机app端的接口访问拉钩网进行数据的抓取。但是访问到一定数据量之后还是会被封,需要登录验证。通过app端口访问失败 访问30条之后便更新cookie进行爬取 最终成功方法:设置延时访问并访问一定数据量后进行自动获取当前浏览器的cookie进行更新,继续爬取数据
二.ElasticSearh的安装使说明 环境说明linux centos6.9(用户组mima12345,用户test_12345),ElasticSearch5.2.2,kibana5.2.2 1.ElasticSearch介绍(抄一些书,资料什么的)
index:类,所有对象的集合
document:类的实例化——》对象,以字典的形式存储,键为filed
type:对象的种类
shard(分片):index所有数据在集群运算中被存放在多个节点上,称之为shard
replica:shard的保存副本,防止数据丢失系统出现意外
ElasticSearch的概念和数据库概念的对比 index —- 库 document —- 行 type —- 表 mappings —- 表结构
中文分词器
部分操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 批量检索获取文档: GET /lib/user/_mget { //检索id为1和2的对象 "ids":["1","2"] } 批量操作: Bulk API: 导入数据(在account.json文件的当前目录输入进行导入操作): $ curl -H "Content-Type: application/json" -XPOST 'localhost:9200/bank/account/_bulk?pretty' --data-binary @accounts.json 查看是否导入成功: curl "localhost:9200/_cat/indices?v" 数据导入并创建在kibana中创建index及相关使用 https://blog.csdn.net/magerguo/article/details/79849863
2.ElasticSearch安装
安装jdk
1 2 3 因为最新版的jdk1.9和ElasticSearch存在部分不兼容,所以这里安装的是jdk1.8版本的 具体步骤见:https://www.cnblogs.com/xqzt/p/4934451.html
安装elasticSearch5.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 下载:wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.2.tar.gz 解压:tar -zxvf elasticsearch-5.2.2.tar.gz -C /usr/local/ElasticSearch 使用:./bin/elasticsearch 查看是否成功开启:打开“http://localhost:9200”有以下结果: { "name" : "zLIR9JJ", "cluster_name" : "elasticsearch", "cluster_uuid" : "thS180_KTj6vZK9szI3Mag", "version" : { "number" : "5.2.2", "build_hash" : "f9d9b74", "build_date" : "2017-02-24T17:26:45.835Z", "build_snapshot" : false, "lucene_version" : "6.4.1" }, "tagline" : "You Know, for Search" }成功开启
如图所示:
安装elasticsearch-head插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 https://www.cnblogs.com/scharfsinnig/p/6706880.html 安装node.js: https://blog.csdn.net/liangxw1/article/details/78251025安装npm: 安装npm:nodejs安装的时候附带了npm 下载elasticserach-head插件: git clone https://github.com/mobz/elasticsearch-head.git 安装elasticsearch-head依赖包: npm install -g grunt-cli 遇到问题: node npm install Error: CERT_UNTRUSTED 解决方法: ssl验证问题,使用下面的命令取消ssl验证即可解决 npm config set strict-ssl false 打开方式: 在./elasticsearch-head目录下输入 grunt server 出现下图为正确无误结果
安装kibana5.2.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 下载:wget https://artifacts.elastic.co/downloads/kibana/kibana-5.2.2-linux-x86_64.tar.gz 解压:tar -zxvf kibana-5.2.2-linux-x86_64.tar.gz -C /usr/local/ElasticSearch 在kibana安装目录的config下,编辑kibana.yml配置文件,添加如下配置: server.port: 5601 server.host: "本机的IP" elasticsearch.url: "http://192.168.222.131:9200" 其中elasticsearch.url 配置对应搜索引擎服务的真实地址 打开5601端口(kinaba默认端口): /sbin/iptables -I INPUT -p tcp --dport 端口号 -j ACCEPT 写入修改 /etc/init.d/iptables save 保存修改 service iptables restart 重启防火墙,修改生效 启动服务 $ cd kibana-6.4.0-linux-x86_64/bin $ ./kibana 浏览器访问“http:/.192.168.222.131:5601”验证是否成功安装 结果如下图
3.ElasticSearch的初步使用及项目实现 1.初步使用
查询功能的javaAPI使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.net.InetAddress; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.junit.Test; public class EsDemo { @Test public void test1() throws Exception{ //指定ES集群 Settings settings = Settings.builder().put("cluster.name","my-application").build(); //创建访问es服务器的客户端 InetSocketTransportAddress node = new InetSocketTransportAddress( InetAddress.getByName("192.168.222.131"),9300); TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(node); //数据查询 GetResponse response = client.prepareGet("lib","user","1").execute().get(); //打印结果 System.out.println(response.getSourceAsString()); client.close(); } }
结构化查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 ①通过id检索实现简单查询 import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.net.InetAddress; import java.net.UnknownHostException; import org.apache.lucene.util.fst.Util; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.transport.client.PreBuiltTransportClient; public class simpleSearch { public static void main(String[] args) throws Exception { //指定ES集群 Settings settings = Settings.builder().put("cluster.name","my-application").build(); //创建访问es服务器的客户端 InetSocketTransportAddress node = new InetSocketTransportAddress( InetAddress.getByName("192.168.222.131"),9300); TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(node); //数据查询 BufferedWriter writer = new BufferedWriter(new FileWriter(new File("C:\\Users\\94452\\Desktop\\1.txt"),true)); BoolQueryBuilder qb = QueryBuilders.boolQuery(); qb.must(QueryBuilders.termQuery("positionName", "java大数据")); // termQuery 是代表短语、不可拆分。就是不会把你teamId拿去切词,然后再去查,而是直接拿去查询 //prepareSearch写库的名称,setTypes写表的名称,setQuery写查询语句,setSize写默认结果返回条数的个数(默认为10条),get语句执行,返回的结果存放在getHits中 SearchResponse response = client.prepareSearch("lagou").setTypes("work").setQuery(qb).setSize(3000).get(); SearchHits searchHits = response.getHits(); System.out.println("查询到记录数=" + searchHits.getTotalHits()); for(SearchHit hit:searchHits.getHits()){ System.out.println(hit.getSourceAsString()); writer.write(hit.getSourceAsString()+"\n"); } writer.close(); client.close(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 ②多条件查询,查询出学历要求为本科,工作城市在杭州,工作类型为全职的信息 import java.net.InetAddress; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.transport.client.PreBuiltTransportClient; public class simpleSearch { public static void main(String[] args) throws Exception { // 指定ES集群 Settings settings = Settings.builder().put("cluster.name", "my-application").build(); // 创建访问es服务器的客户端 InetSocketTransportAddress node = new InetSocketTransportAddress(InetAddress.getByName("192.168.222.131"), 9300); TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(node); // 数据查询 QueryBuilder qb = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("education.keyword", "本科")) .must(QueryBuilders.termQuery("city.keyword", "杭州")) .must(QueryBuilders.termQuery("jobNature.keyword", "全职")); SearchRequestBuilder sv = client.prepareSearch("lagou").setTypes("work").setQuery(qb).setFrom(0).setSize(100); SearchResponse response = sv.get(); SearchHits searchHits = response.getHits(); System.out.println("查询到记录数=" + searchHits.getTotalHits()); for (SearchHit hit : searchHits.getHits()) { System.out.println(hit.getSourceAsString()); } client.close(); } }
出现问题及解决方法:
对于包含中文字符的查询语句无法执行进行
问题原因:elasticsearch 里默认的IK分词器是会将每一个中文都进行了分词的切割,所以你直接想查一整个词,或者一整句话是无返回结果的。
解决方法:
1 2 3 4 termQuery("cityName", "北京市"); 改成 termQuery("cityName.keyword", "北京市"); 就可以了
2.项目实现
数据结构的设计并转换数据格式
1 5309423 大数据Java工程师 8k-16k 本科 六险一金,补贴,带薪年假 开发|测试|运维类 全职 1-3年 成都数联铭品科技有限公司 成都 带薪年假/绩效奖金/扁平管理/定期体检 150-500人 C轮
需要的格式为:(index行为设置索引,第二行为数据)
1 2 {"index": {"_id":"1"}} {"positionId":"5309423","positionName":"大数据Java工程师","salary":"8k-16k","education":"本科","positionAdvantage":"六险一金,补贴,带薪年假","firstType":"开发|测试|运维类","skillLables":"","jobNature":"全职","workYear":"1-3年","companyFullName":"成都数联铭品科技有限公司","city":"成都","companyLabelList":"带薪年假/绩效奖金/扁平管理/定期体检","companySize":"150-500人","financeStage":"C轮","detailInfo":"https://www.lagou.com/jobs/5309423.html"}
批量数据导入ElasticSearch系统
1 curl -H "Content-Type: application/json" -XPOST '192.168.222.131:9200/lagou/work/_bulk?pretty' --data-binary @work.json
查看结果 如图1 ,图2所示
通过kibana建立索引(操作步骤)
检索数据结构的设计
positionName:职位名称
salary: 20-30k(这里的数据检索前需要处理,仔细想想该怎么做)
education:本科/大专/硕士/不限
skillLables:手动填写
jobNature:全职/实习
workYear:1-3年/3-5年/不限/应届毕业生
city:城市
公司规模:100-200人等等
通过编写javaAPI接口实现搜索功能 javaAPI中主要使用ElasticSearch中的布尔查询来实现复杂信息的检索功能。 1 2 3 must:必须出现在匹配文档中,并且会影响匹配得分 filter:必须出现在匹配文档中,并且匹配得分会被忽略 should:应该出现在匹配文档中
三.小程序开发部分
小程序的相关概念学习:
index.js 文件配置页面入口与逻辑,还有存储数据并与网页使用的数据进行绑定;还有函数的绑定也是放在js文件中的
index.wxml配置布局与ui,index.wxss为页面的样式文件(类似与css),二级目录下的json文件可有可无,如果存在的话会覆盖掉app.json的数据,wxss文件同理
小程序开发软件的安装
小程序软件的编写
前台与后台的交互实现部分
服务器端使用的是tomcat8,通过微信小程序的wx.request方法对服务器端进行servlet请求,然后通过servlet层对dao层发送搜索请求,dao层处理完成后得到结果再返回微信小程序端。
实现样例:通过小程序点击按钮对“http://localhost:8080/weixin/getInfoServlet”发送请求,并传输过去data内的值,在服务器端查看数据是否传输过来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 //index.js Page({ bindtest: function () { wx.request({ url: 'http://localhost:8080/weixin/getInfoServlet', data: { username: '001', password: 'abc' }, method: 'GET', header: { 'content-type': 'application/json' // 默认值 }, success: function (res) { console.log(res.data); }, fail: function (res) { console.log(".....fail....."); } }) } } ) //index.wxml <view> <button bindtap='bindtest'>test</button> </view> //servlet package com.weixin.servlet; import java.io.IOException; import java.io.Writer; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/getInfoServlet") public class getInfoServlet extends HttpServlet { private static final long serialVersionUID = 1L; public getInfoServlet() { super(); // TODO Auto-generated constructor stub } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setContentType("text/html;charset=utf-8"); /* 设置响应头允许ajax跨域访问 */ response.setHeader("Access-Control-Allow-Origin", "*"); /* 星号表示所有的异域请求都可以接受, */ response.setHeader("Access-Control-Allow-Methods", "GET,POST"); //获取微信小程序get的参数值并打印 String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println("username="+username+" ,password="+password); //返回值给微信小程序 Writer out = response.getWriter(); out.write("成功访问客户端"); out.flush(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
查看结果:先打开tomcat8,然后打开小程序客户端点击test按钮如图(小程序测试按钮截图),可以查看到小程序的调试器界面出现了“成功访问客户端”几个字,而这几个字是通过“http://localhost:8080/weixin/getInfoServlet”所返回到小程序端的结果。然后在服务器端查看小程序端传输过去的data是否成功接收,打开服务器端(如图:小程序测试服务器端截图)可以看到数据成功传输过去了。 提交按钮
1 2 3 sequenceDiagram 小程序端->>服务器端: 传输data 服务器端->>小程序端: 返回“成功访问客户端”说明,并打印data
项目具体实现
selvert层:处理小程序端传过来的请求,传输到dao层进行查询处理
dao层:进行数据检索功能
model层: 该层创建信息对象(字段全部都为ES中的text格式,在java对象中对应设置为string格式)
遇到的问题及解决方法
小程序页面的编写及功能实现,主要两个功能:
1.不含条件的工作名称检索(默认) 2.含条件的工作名称检索。
服务器端的servlet层从小程序端获取到数据后,传入dao层进行数据检索,并返回前端页面显示结果。目前存在以下问题:
1.在使用javaAPI的过程中对于多条件检索语句如何编写?2.检索到的数据以什么格式存储并传输到前端的小程序中?
小程序接收到数据后静态刷新页面显示结果页面。以列表形式将所有的信息打印出来,点击直接跳转到detailInfo的值包含的页面。
多单选框如何传入数据
maven项目依赖包无法导入tomcat中进行部署(https://blog.csdn.net/class2class/article/details/83066073)
前端与后端的数据传输部分(json格式的修改)
事件绑定bindtap等