解决方案:基于简单协同过滤推荐算法职位推荐系统
优采云 发布时间: 2022-11-25 19:56解决方案:基于简单协同过滤推荐算法职位推荐系统
基于简单协同过滤推荐算法的职位推荐系统。篇幅比较大,需要分几个博客
文章目录第一部分 1 爬虫方面(我的项目也叫信息采集器)
前言
使用python网络爬虫技术爬取51job网站。爬取的数据被清洗并入库。然后通过python的django web框架搭建一个小网站,展示职位信息。对于注册的用户行为信息,通过简单的协同过滤推荐算法计算用户相似度。根据用户相似度推荐相似用户的职位信息。
1、用网络爬虫爬取51job网站
爬取的数据存储在数据库中。这个项目有这样一个功能:就是让管理员选择一个大厂的名字,爬取对应的名字去51job上下载位置。即管理员选择名称,然后点击采集按钮开始爬取数据。图片示例如下。我的前端设计很丑。对不起
最终爬取数据存储展示
2. 信息采集
器
第三方库:
#信息采集器,负责采集招聘信息
import requests
import re
import random
from multiprocessing import Pool
from .models import workdeilts,company
from lxml import etree
import time
from django.shortcuts import render,redirect
2. 爬行动物
本来想爬boss的,但是技术有限。模拟登录后,我用的是自己账号的session。老板只给了我爬取5页左右的机会,试过其他账号的session也是一样。无法解决反爬boss,放弃爬boss。爬取51job后,51job职位等都是json格式的。还是比较简单的。然后深度爬虫根据职位的url,爬取职位的完整信息。由于爬虫的时效性,这个系统还是会在2021年3月和4月测试生效,下面是我最初的爬取方式。
深度爬取,找url,爬取,爬下有用的信息,这里忍不住吐槽一下,这种格式太不规范了。有些是 p 标签,有些是 li 标签。反正嵌套很乱。
**代码贴在下面,由于我是在网上做的,可能不能直接复制粘贴。明白就好。我会把我的项目挂在博客上,需要拿起来。
# 爬取51job
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Cookie": "guid=8766426d6a6e7cb73f5784127814feeb; nsearch=jobarea%3D%26%7C%26ord_field%3D%26%7C%26recentSearch0%3D%26%7C%26recentSearch1%3D%26%7C%26recentSearch2%3D%26%7C%26recentSearch3%3D%26%7C%26recentSearch4%3D%26%7C%26collapse_expansion%3D; __guid=212605071.4274319711180497400.1594717185324.2678; _ujz=MTg3NTgzNTU3MA%3D%3D; ps=needv%3D0; 51job=cuid%3D187583557%26%7C%26cusername%3Demail_20210320_d7612b93%26%7C%26cpassword%3D%26%7C%26cname%3D%25C0%25B2%25C0%25B2%25C0%25B2%26%7C%26cemail%3D1283062150%2540qq.com%26%7C%26cemailstatus%3D0%26%7C%26cnickname%3D%26%7C%26ccry%3D.0v0O9eWnGAtg%26%7C%26cconfirmkey%3D12a4WxI%252FuvU0Y%26%7C%26cautologin%3D1%26%7C%26cenglish%3D0%26%7C%26sex%3D0%26%7C%26cnamekey%3D1246IFugsIKHc%26%7C%26to%3D08ee79b7343b47f6629abf87204ca02160686738%26%7C%26; adv=adsnew%3D0%26%7C%26adsnum%3D4858120%26%7C%26adsresume%3D1%26%7C%26adsfrom%3Dhttps%253A%252F%252Fwww.so.com%252Fs%253Fq%253D51job%2525E5%252589%25258D%2525E7%2525A8%25258B%2525E6%252597%2525A0%2525E5%2525BF%2525A7%2525E7%2525BD%252591%2526src%253Dsrp_suggst_revise%2526fr%253D360se7_addr%2526psid%253Dcff8a6a527fbe2af36a5885576c3039a%2526eci%253D%2526nlpv%253Dtest_dt_61%26%7C%26ad_logid_url%3Dhttps%253A%252F%252Ftrace.51job.com%252Ftrace.php%253Fadsnum%253D4858120%2526ajp%253DaHR0cHM6Ly9ta3QuNTFqb2IuY29tL3RnL3NlbS9MUF8yMDIwXzEuaHRtbD9mcm9tPTM2MGFk%2526k%253D7d16490a53bc7f778963fbe04432456c%2526qhclickid%253D38a22d9fefae38b3%26%7C%26; search=jobarea%7E%60000000%7C%21ord_field%7E%600%7C%21recentSearch0%7E%60000000%A1%FB%A1%FA000000%A1%FB%A1%FA0000%A1%FB%A1%FA00%A1%FB%A1%FA99%A1%FB%A1%FA%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA9%A1%FB%A1%FA99%A1%FB%A1%FA%A1%FB%A1%FA0%A1%FB%A1%FA%BF%AA%B7%A2%A1%FB%A1%FA2%A1%FB%A1%FA1%7C%21recentSearch1%7E%60000000%A1%FB%A1%FA000000%A1%FB%A1%FA0000%A1%FB%A1%FA00%A1%FB%A1%FA01%A1%FB%A1%FA%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA9%A1%FB%A1%FA99%A1%FB%A1%FA%A1%FB%A1%FA0%A1%FB%A1%FApython%A1%FB%A1%FA2%A1%FB%A1%FA1%7C%21recentSearch2%7E%60000000%A1%FB%A1%FA000000%A1%FB%A1%FA0000%A1%FB%A1%FA00%A1%FB%A1%FA99%A1%FB%A1%FA%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA9%A1%FB%A1%FA99%A1%FB%A1%FA%A1%FB%A1%FA0%A1%FB%A1%FApython%A1%FB%A1%FA2%A1%FB%A1%FA1%7C%21recentSearch3%7E%60000000%A1%FB%A1%FA000000%A1%FB%A1%FA0000%A1%FB%A1%FA00%A1%FB%A1%FA01%A1%FB%A1%FA%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA9%A1%FB%A1%FA99%A1%FB%A1%FA%A1%FB%A1%FA0%A1%FB%A1%FA%BF%AA%B7%A2%A1%FB%A1%FA2%A1%FB%A1%FA1%7C%21recentSearch4%7E%60000000%A1%FB%A1%FA000000%A1%FB%A1%FA0000%A1%FB%A1%FA00%A1%FB%A1%FA01%A1%FB%A1%FA%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA99%A1%FB%A1%FA9%A1%FB%A1%FA99%A1%FB%A1%FA%A1%FB%A1%FA0%A1%FB%A1%FA%B2%E2%CA%D4%A1%FB%A1%FA2%A1%FB%A1%FA1%7C%21collapse_expansion%7E%601%7C%21; slife=lastlogindate%3D20210406%26%7C%26; monitor_count=3",
"Host": "jobs.51job.com",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
}
params = {
"VerType": "3",
"webId": "2",
"logTime": "1617756869425",
"ip": "111.61.205.194",
"guid": "8766426d6a6e7cb73f5784127814feeb",
"domain": "jobs.51job.com",
"pageCode": "10201",
"cusParam": "118758355751job_web0",
"vt": "1617756869524",
"logType": "pageView"
}
def get_data(url):
response = requests.get(url, headers=headers)
status = response.status_code
data = response.content.decode('gbk')
return data, status
def get_job(url):
data, status = get_data(url)
if status == 200:
job_name_p = re.compile('job_name":"(.*?)","job_title')
job_name = job_name_p.findall(data) # 工作名称
job_url_p = re.compile('job_href":"(.*?)","')
job_url = job_url_p.findall(data) # url中获取详细职位描述
<p>
" />
attribute_text_p = re.compile('attribute_text":\["(.*?)"\],"companysize_text')
attribute_text = attribute_text_p.findall(data)#
company_name_p = re.compile('company_name":"(.*?)","')
company_name = company_name_p.findall(data) # 公司名称
saily_p = re.compile('providesalary_text":"(.*?)","')
saily = saily_p.findall(data) # 工资
address_p = re.compile('workarea_text":"(.*?)","')
address = address_p.findall(data) # 工作地点
updatadate_p = re.compile('updatedate":"(.*?)","')
updatadate = updatadate_p.findall(data) # 更新日期
company_text_p = re.compile('companytype_text":"(.*?)","')
company_text = company_text_p.findall(data) # 公司类型
companysize_text_p = re.compile('companysize_text":"(.*?)","')
companysize_text = companysize_text_p.findall(data) # 公司规模
companyind_text_p = re.compile('companyind_text":"(.*?)","')
companyind_text = companyind_text_p.findall(data) # 公司行业
for i in range(len(job_name)):
try:
job_name1=job_name[i]# 工作名称
company_name1=company_name[i]# 公司名称
saily1=saily[i].replace('\\', '')# 工资
address1=address[i]# 工作地点
exper_req=attribute_text[0].split('","')[1].replace('/',"")#经验要求
edu_req=attribute_text[0].split('","')[2]#*敏*感*词*要求
need_num=attribute_text[0].split('","')[3]#招工人数
updatadate1=updatadate[i]# 更新日期
companyind_text1=companyind_text[i].replace('\\', '')# 公司行业
company_text1=company_text[i]# 公司类型
companysize1=companysize_text[i] # 公司规模
end_url = job_url[i].replace('\\', '')
response = requests.get(url=end_url, headers=headers, params=params)
data = response.content.decode('gbk')
selector = etree.HTML(data)
content_xml = selector.xpath('/html/body/div[3]/div[2]/div[3]/div[1]/div/*')
br = selector.xpath('/html/body/div[3]/div[2]/div[3]/div[1]/div/text()')
str = ""
for p in content_xml:
span = p.xpath('span')
li = p.xpath('li')
p_p = p.xpath('strong')
if span != [] or li != [] or p_p != []:
if span != []:
for i in span: # 如果是p标签套span标签,则依次取出span
if i.text == None:
span1 = i.xpath('span')
for j in span1:
str = str + j.text
else:
# print(i.text)
str = str + i.text
elif li != []:
for i in li: # 如果是p标签套li标签,则依次取出li
# print(i.text)
str = str + i.text
else:
for i in p_p: # 如果是p标签套p标签,则依次取出p
# print(i.text)
str = str + i.text
else: # 如果是单独的p标签,则无须取span
if p.text != None and p != []:
# print(p.text)
str = str + p.text
else:
for i in br:
str = str + i
# print(str)
break
" />
#try:
list1 = ['任职资格', '任职要求', '岗位要求', '职位要求', '岗位职责', '要求']
for i in list1:
if i in str:
job_description, job_requirements = str.split(i)[0], '任职资格' + \
str.split(i)[1]
#print(job_description)
#print(job_requirements)
if job_description and job_requirements:
company1=company.objects.filter(name=company_name1)
if company1.exists():
#print('公司存在!')
company_name2=company.objects.get(name=company_name1)
data = workdeilts.objects.filter(name=job_name1, company_name=company_name1,adress=address1, update=updatadate1)
if data.exists():
#print('职位存在!')
continue
else:
workdeilts.objects.create(company_id=company_name2,name=job_name1, company_name=company_name1,exper_req=exper_req,edu_req=edu_req,need_num=need_num,adress=address1, wage=saily1,jobdescription=job_description,jobrequirements=job_requirements,update=updatadate1)
#print('插入职位成功')
else:
#print('公司不存在!')
company.objects.create(name=company_name1, people=companysize1,nature_of_bissiness=company_text1,industry=companyind_text1)
#print('添加公司成功')
company2=company.objects.get(name=company_name1)
workdeilts.objects.create(company_id=company2,name=job_name1, company_name=company_name1,exper_req=exper_req,edu_req=edu_req,need_num=need_num,adress=address1, wage=saily1,jobdescription=job_description,jobrequirements=job_requirements,update=updatadate1)
#print('插入职位成功')
continue
else:
continue
#except:
#pass
except:
pass
else:
j = 19
return j
def collect(request):
if request.method=='POST':
data=request.POST
zhiwei_post_list=data.getlist('company')
#print(zhiwei_post_list)
# zhiwei_list=['开发','python','java','c++','']
zhiweilist = ['web', '前端', '嵌入式', '大数据', 'python', 'java', 'c++', 'linux', 'IT实习', '机器学习','后端', '人工智能', '测试', '运维']
zhiwei_list=zhiwei_post_list+zhiweilist
random.shuffle(zhiwei_list)
#print(zhiwei_list)
#p=Pool(1)想利用异步多进程实现爬取,存储,没实现,有空了解决
for i in zhiwei_list:
for j in range(1, 6):
#https://search.51job.com/list/000000,000000,0100%252c7700%252c7200%252c7300,01,9,99,+,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=
#https://search.51job.com/list/000000,000000,0000,00,9,99,字节跳动,2,1.html?lang=c&postchannel=0000&workyear=99
url = "https://search.51job.com/list/000000,000000,0000,00,9,99," + i + ",2," + str(
j) + ".html?lang=c&postchannel=0000&workyear=99"
get_job(url)
#p.apply_async(get_job, args=(url,))
time.sleep(0.5)
#p.close()
#p.join()
print('数据采集结束!!!!')
return render(request,'index.html')
</p>
总结
接下来,我将彻底更新我的项目。我也是菜鸟。哈哈哈,手写吧。我挂断了我的项目。本文为本人原创。未经本人同意不得传播为商业价值。
解决方案:基于百度地图API的城市数据采集方式
在进行定量的城市分析时(比如研究某个城市某个区域的空间分析),需要用到地理位置信息和现有设施、建筑物的分布,这就需要获取相关的地理坐标信息。因此,数据的获取和处理是城市定量分析所需的前期工作,这一阶段的工作决定了后续分析的有效性和质量。
1.使用工具
这里用来采集
数据的工具是优采云
Collector 8.5。
优采云
Collector是一款互联网数据抓取、处理、分析、挖掘软件,可以抓取网页上零散的数据信息,通过一系列的分析处理,准确挖掘出需要的数据。
特点:采集
不限于网页和内容;
分布式采集
系统,提高效率;
支持PHP和C#插件扩展,方便修改和处理数据,但需要懂优采云
规则或正则表达式。
2、数据采集方式——基于百度地图API的数据采集
API 是预定义的功能,旨在为应用程序开发人员提供访问基于软件或硬件的一组程序的能力,而无需访问源代码或了解程序内部工作的细节。API服务商在提供数据的同时也在采集
用户信息,这是一个双向的过程。
百度地图Web服务API提供位置检索服务、正向/反向地理编码服务、路线规划、批处理服务、时区服务、坐标转换服务、鹰眼轨迹服务。其中,位置搜索服务(也称为Place API)提供了多种场景下的位置(POI)搜索功能,包括城市搜索、周边搜索、矩形区域搜索等。
" />
以通过百度地图API获取绵阳市酒店数据为例。
3. 数据采集
步骤
1.申请百度地图开放平台开发者密钥
首先打开百度地图开放平台( ),点击右上角控制台,然后点击应用管理→我的应用→创建应用,申请开发者密钥(ak),选择以下服务项目。
申请后获得的ak
2.通过接口获取详细的地理信息
开发者可以通过接口获取基本或详细的地点地理信息(POI)。返回Json类型的数据(一个区域最多返回400条,每页最多返回20条)。当某区域某类POI超过400个时,可选择将该区域划分为子区域进行搜索,或以矩形或圆形区域的形式进行搜索。查找页面上的Place搜索格式和示例如下:
格式:
例子:(你的秘钥是从上一步的应用中获取的)
(关于什么是 Place API 的详细说明可以在这个 URL 找到)
" />
其中“Keyword”、“Query Area”、“Output Format Type”、“User Key”可以根据自己的需要替换,page_num是可选项,表示页码,因为只有当page_num字段设置后会在结果中显示页面返回标识总条数的total字段,方便在优采云
采集
器中进行相关设置,如下:
访问这个URL,返回结果如下:
3.使用优采云
采集
器采集
地理信息
在优采云
软件中,先点击左上角的“新建”,然后点击“分组”,进入新界面,自己命名并保存。创建组后,单击新建。然后点击“任务”,命名为“百度API”。
然后在第一步-采集规则页面点击“添加”按钮,在添加起始URL采集页面选择“批量/多页”方式获取地址格式,在地址格式中填写Place search link column, and page_num字段用(*)标记为变量,选择算术差量法。项数与返回的Json结果中的total字段一致,容差为1。
在访问百度地图API接口返回的Json网页中,复制需要提取信息的地方的信息。比如复制这里得到“姓名”:周飞昌(总店)”。
第二步优采云
采集
器采集
内容规则页面,添加标签,标签名称填写“name”,数据提取方式选择“前后”,替换内容为在标签编辑页面用(*)采集,在“起始字符串”和“结束字符串”中填写采集内容前后的内容。
获取经纬度的方法同“名称”。
添加完成后,点击规则测试页面的“测试”按钮,测试POI“名称”、“经度”、“纬度”三个标签。在“设置”页面,选择“方法二:另存为本地Word、Excel、Html、Txt等文件”,制作标签对应的Gsv格式模板。完成后点击“保存”按钮完成数据采集。
回到主界面,勾选“采集网页”和“采集内容”下的复选框,点击开始任务,完成百度地图绵阳市酒店数据采集。