# 数据挖掘期中 **Repository Path**: huang_zehao/mid-term-data-mining ## Basic Information - **Project Name**: 数据挖掘期中 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-05-19 - **Last Updated**: 2021-05-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 数据挖掘期中项目介绍 > - 项目任务:挖取51job网站信息 > - 撰写人:黄泽豪 > - 撰写日期:2021/5/20 ## 1、项目简介 本项目将爬取[前程无忧](http://www.51job.com)网站下其 **职能搜索** 页面的基本信息,爬取要点分以下五点: 1. 可选择的分类部分(任选3个构建参数模板); 2. url模板可以自动生成关键词 (构建参数模板); 3. 页面内容(页面展示的所有简要的招聘信息); 4. 详细页内容(页面中所有招聘信息里的详细内容); 5. 进行翻页爬取。 ## 2、爬取步骤 ### 2.1 前期准备(模块调用和网站链接解析) 首先进行模块调用,代码如下: ``` from requests_html import HTMLSession import requests_html import urllib.parse import pandas as pd ``` 接着对51job网站中我们要爬取的“职能搜索”网页链接进行解析,代码如下: ``` url = "https://search.51job.com/list/030000,000000,0000,00,9,99,%2B,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=" session = HTMLSession() res = session.get(url) res ``` 解析结束,我们将会获得200的成功反馈。 ### 2.2 获取参数 由于51job网站其网页为动态加载网页,所以需要我们先爬取出其动态加载的js文件,再从此文件中获取我们需要的参数数据,代码如下: ``` d_search_footer = "http:"+res.html.xpath('//script') [-3].html.split("\"")[3] d_search_footer ``` 结果返回:`'http://js.51jobcdn.com/in/resource/js/2021/search/common.f182f3d4.js'` 爬取js文件中的参数数据,代码如下: ``` import requests resp = requests.get(d_search_footer) resp.content.decode("gbk","ignore") 参数 = resp.content.decode("gbk","ignore") 参数 ``` 爬取结束后,会给我们返回js文件数据,我们可以通过这些数据进行下一步的参数构建,以此来获取我们需要的参数,但由于数据过多便不在此展示返回的js数据。 ### 2.3 建立参数 通过上面已经获取的js数据,我们可以进行这一步的参数构建了,但由于获取到的js数据没有进行清洗无法被我们直接使用,所以我们在对每一个参数进行构建的同时需要先将获取的对应数据进行一定清洗,即将其进行单独拆分清洗处理,以此来获取我们可以进行后续操作使用的参数信息。 #### 2.3.1 参数1-公司性质 先将js文件数据里关于公司性质的参数取出,代码如下: ``` 公司性质 = 参数.split("search_cottype=")[1].split(",window.d_search_workyear")[0] 公司性质 ``` 结果返回:`'[{k:"99",v:"请选择"},{k:"04",v:"国企"},{k:"01",v:"外资(欧美)"},{k:"02",v:"外资(非欧美)"},{k:"10",v:"上市公司"},{k:"03",v:"合资"},{k:"05",v:"民营公司"},{k:"06",v:"外企代表处"},{k:"07",v:"政府机关"},{k:"08",v:"事业单位"},{k:"09",v:"非营利组织"},{k:"11",v:"创业公司"}]'` 将返回结果数据进行拆分清洗,代码如下: ``` 公司性质1 = 公司性质.replace('k:"','').replace('}','').replace("{","").replace('[','').replace(']','').replace('"','') 公司性质2 = 公司性质1.replace(',v:','=') 公司性质分类 = 公司性质2.split(',') 公司性质分类 ``` 结果返回:`['99=请选择', '04=国企', '01=外资(欧美)', '02=外资(非欧美)', '10=上市公司', '03=合资', '05=民营公司', '06=外企代表处', '07=政府机关', '08=事业单位', '09=非营利组织', '11=创业公司']` 然后可以自定义一个列表,自己命名对应的元素,代码如下: ``` 公司性质_str = ["所有","国企","外资(欧美)","外资(非欧美)","上市公司","合资","民营公司","外企代表处","政府机关","事业单位","非营利组织","创业公司"] 公司性质_str ``` 最后,将上面我们拆分清洗后返回的数据与我们自定义的列表元素相结合,这样就将成功构建了一个参数,代码如下: ``` 公司性质_dict = {i:公司性质分类[k].split('=')[0] for k,i in enumerate(公司性质_str)} 公司性质_dict ``` 结果返回: ``` {'所有': '99', '国企': '04', '外资(欧美)': '01', '外资(非欧美)': '02', '上市公司': '10', '合资': '03', '民营公司': '05', '外企代表处': '06', '政府机关': '07', '事业单位': '08', '非营利组织': '09', '创业公司': '11'} ``` **以此类推,我们可以按同样的逻辑去获取第二、三个参数信息,并进行参数构建** #### 2.3.2 参数2-工作年限 整体代码如下: ``` 工作年限 = 参数.split("search_workyear=")[1].split(",window.d_search_providesalary")[0] 工作年限1 = 工作年限.replace('k:"','').replace('}','').replace("{","").replace('[','').replace(']','').replace('"','') 工作年限2 = 工作年限1.replace(',v:','=') 工作年限分类 = 工作年限2.split(',') 工作年限_str = ["所有","在校生/应届生","1-3年","3-5年","5-10年","10年以上","无需经验"] 工作年限_参数构建 = {工作年限_str[i]:工作年限分类[i].split('=')[0] for i in range(len(工作年限分类))} 工作年限_参数构建 ``` 结果返回: ``` {'所有': '99', '在校生/应届生': '01', '1-3年': '02', '3-5年': '03', '5-10年': '04', '10年以上': '05', '无需经验': '06'} ``` #### 2.3.3 参数3-学历要求 整体代码如下: ``` 学历要求 = 参数.split("search_degreefrom=")[1].split(",window.d_search_jobterm")[0] 学历要求1 = 学历要求.replace('k:"','').replace('}','').replace("{","").replace('[','').replace(']','').replace('"','') 学历要求2 = 学历要求1.replace(',v:','=') 学历要求分类 = 学历要求2.split(',') 学历要求_str = ["所有","初中及以下","高中/中技/中专","大专","本科","硕士","博士","无学历要求"] 学历要求_参数构建 = {学历要求_str[i]:学历要求分类[i].split('=')[0] for i in range(len(学历要求分类))} 学历要求_参数构建 ``` 返回结果: ``` {'所有': '99', '初中及以下': '01', '高中/中技/中专': '02', '大专': '03', '本科': '04', '硕士': '05', '博士': '06', '无学历要求': '07'} ``` ### 2.4 构建参数模板并封装 在对我们所需要的参数信息进行参数构建后,便要进行构建参数模板,使之能为下一步的封装做准备 #### 2.4.1 构建参数模板 代码如下: ``` 参数模版 = urllib.parse.urlparse(url) 参数模版_list = pd.Series(参数模版).tolist() print("参数模版_list=",参数模版_list,'\n') 参数模版_dict = {i.split("=")[0]:i.split("=")[1] for i in 参数模版.query.split("&")} 参数模版_dict_1 = {参数模版_list[2].split(",")[6] for i in 参数模版.query.split(",")} 参数模版_dict ``` 结果返回: ``` 参数模版_list= ['https', 'search.51job.com', '/list/030000,000000,0000,00,9,99,%2B,2,1.html', '', 'lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', ''] {'lang': 'c', 'postchannel': '0000', 'workyear': '99', 'cotype': '99', 'degreefrom': '99', 'jobterm': '99', 'companysize': '99', 'ord_field': '0', 'dibiaoid': '0', 'line': '', 'welfare': ''} ``` #### 2.4.2 封装 代码如下: ``` def url_参数模板生成(cotype,workyear,degreefrom): cotype_values = 公司性质_dict[cotype] workyear_values = 工作年限_参数构建[workyear] degreefrom_values = 学历要求_参数构建[degreefrom] 参数模版_dict["cotype"] = cotype_values 参数模版_dict["workyear"] = workyear_values 参数模版_dict["degreefrom"] = degreefrom_values query_所有 = "&".join([k+"="+v for k,v in 参数模版_dict.items()]) 参数模版_list[4] = query_所有 url_参数 = urllib.parse.urlunparse(参数模版_list) return (url_参数) ``` #### 2.4.3 调用 在把参数模板构建成功并封装好后,我们就可以通过输入参数信息进行网站链接的生成(参数信息为:公司性质,工作年限,学历要求),我们尝试调用: ``` url_参数模板生成('民营公司','所有','本科') ``` 结果返回:`'https://search.51job.com/list/030000,000000,0000,00,9,99,%2B,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare='`
调用成功 ### 2.5 自动生成关键词 按要求规定,我们还需在网站链接中自动生成并添加入关键词,通过观察法我得知了关键词的位置,然后通过`urllib.parse.quote("关键词")`可自动生成关键词的编码,并将其通过`replace`函数的方法将编码转换置放入链接中关键词位置,以此我也可以构建了一个关键词参数模板,并将其与前面的参数模板放在一起,这样就可以获得一个具备分类参数和关键词的模板,也就可以生成我们需要的网站链接,代码如下: ``` keyword = urllib.parse.quote("产品经理") cotype = '民营公司' workyear = '所有' degreefrom = '本科' url = url_参数模板生成(cotype,workyear,degreefrom) url_1 = url.replace('%2B',keyword) url_1 ``` 结果返回:`'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare='` ### 2.6 挖取数据 在我们通过以上步骤获取了我们想要的网站链接后,我们便可以在从基础上进行数据爬取了。 #### 2.6.1 挖取页面内容 通过xpath,我们可以挖取到关于此链接的页面数据内容,即招聘信息的简要内容,代码如下: ``` session = HTMLSession() r = session.get(url_1) g = r.html __SEARCH_RESULT__ = g.xpath('//script')[-4].html.split("__SEARCH_RESULT__ = ")[1].split("")[0] results = eval(__SEARCH_RESULT__) results ``` 返回结果后,我们可以检查成功爬取的内容长度,代码如下:`len(results['engine_search_result'])` 接着,我们便可以将其做成表格了,代码如下: ``` 招聘内容_df = pd.DataFrame(results['engine_search_result']) 招聘内容_df ``` > - **注意** :在将页面内容信息做成表格之前,我们还需设置我们的pd选项,即对表格进行一定设置,使其表格内的内容能被我们清楚观看和使用,且后期对于详细页的数据挖取也需要用到这一步,不然将无法全面的获取我们需要的数据信息,设置的代码如下: ``` pd.set_option('display.max_rows', None) pd.set_option('max_colwidth',1000) ``` 最后,我们便可以获取一份在我们选好参数后的招聘页面内容信息表格了,数据过多,不在此展示。 #### 2.6.2 挖取详细页内容 对于详细页内容的爬取,我们需要先将数据表格中取出工作详细页的每条链接并对其进行一定处理(从数据表格中取出的链接具有双反斜杠,在后期会因为格式错误而无法进行xpath),代码如下: ``` 招聘内容_df["job_href"] #去除后面无法被xpath的反双斜杠,使之成为可以被xpath的正确链接格式 url_all = list(招聘内容_df['job_href']) SYMBOLS = '\\' url_all_2 = [] for i in url_all: temp = "" for j in i: if j not in SYMBOLS: temp += j url_all_2.append(temp) url_all_2 ``` 结果会返回页面中所有工作详细页的链接且都是可以被xpath的格式。 在对详细页链接进行处理后便可通过xpath对详细页内容进行爬取,代码如下: ``` 职称 = [] 职位信息 = [] 薪酬 = [] for i in url_all_2: session = HTMLSession() c = session.get(i) 职称.append(c.html.xpath('//div[@class="tCompanyPage"]/div[@class="tCompany_center clearfix"]/div/div/div/h1/@title')) 职位信息.append(c.html.xpath('//div[@class="tCompanyPage"]/div[@class="tCompany_center clearfix"]/div[@class="tCompany_main"]/div[@class="tBorderTop_box"]/div[@class="bmsg job_msg inbox"]/p/text()')) 薪酬.append(c.html.xpath('//div[@class="tCompanyPage"]/div[@class="tCompany_center clearfix"]/div[@class="tHeader tHjob"]/div[@class="in"]/div[@class="cn"]/strong/text()')) pd.DataFrame({ "职称":职称, "薪酬":薪酬, "职位信息":职位信息, }) ``` 通过循环遍历,最后直接输出表格,这样我们就可以获取一份具有一个页面所有工作招聘的详细页内容了。 ### 2.7 翻页 在成功爬取单页内容以及详细页内容后,我们可以开始尝试翻页,爬取多个页面的全部内容以及它们的详细页内容,我们先找到链接中关于页面第几页的元素位置,再通过`format`函数进行遍历循环我们指定的页面数量链接,在此我以前九页为例进行爬取,代码如下: ``` fanye = [] for h in range(1,10): fanye.append("https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,{num}.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=".format(num = h)) fanye ``` 结果返回: ``` ['https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,2.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,3.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,4.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,5.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,6.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,7.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,8.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'https://search.51job.com/list/030000,000000,0000,00,9,99,%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86,2,9.html?lang=c&postchannel=0000&workyear=99&cotype=05°reefrom=04&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare='] ``` 我们再对这九个页面内容进行解析,代码如下: ``` fanye_all=[] for i in fanye: session = HTMLSession() fanye_all.append(session.get(i)) fanye_all ``` 得到200反馈后,我们需要整合翻页内容 先对翻页全部内容进行xpath爬取,将全部内容放置在一个列表中,代码如下: ``` results = [] for fan in fanye_all: __SEARCH_RESULT__ = fan.html.xpath('//script')[-4].html.split("__SEARCH_RESULT__ = ")[1].split("")[0] results.append(eval(__SEARCH_RESULT__)) results ``` 再将列表中的每个页面列表重新整合成一个完整的一体的列表,这样我们才能对其进行后面的数据挖取,不然会报错,代码如下: ``` results_2 = [] for page in range(9): results_2.append(results[page]) results_2 ``` 得到一个一体的列表后,我们再次取出翻页爬取后的全部详细页链接,代码如下: ``` results_3 = [] for v in range(9): for n in range(50): results_3.append(results_2[v]['engine_search_result'][n]['job_href']) results_3 ``` 再次进行去除双反斜杠的处理,只不过我们需要通过for循环来实现,代码如下: ``` fanye_all = list(results_3) SYMBOLS = '\\' fanye_all_2 = [] for i in fanye_all: temp = "" for j in i: if j not in SYMBOLS: temp += j fanye_all_2.append(temp) fanye_all_2 ``` 接着,继续通过xpath爬取翻页整合后所有的详细页内容,代码如下: ``` 职称 = [] 职位信息 = [] 薪酬 = [] for i in fanye_all_2: session = HTMLSession() c = session.get(i) 职称.append(c.html.xpath('//div[@class="tCompanyPage"]/div[@class="tCompany_center clearfix"]/div/div/div/h1/@title')) 职位信息.append(c.html.xpath('//div[@class="tCompanyPage"]/div[@class="tCompany_center clearfix"]/div[@class="tCompany_main"]/div[@class="tBorderTop_box"]/div[@class="bmsg job_msg inbox"]/p/text()')) 薪酬.append(c.html.xpath('//div[@class="tCompanyPage"]/div[@class="tCompany_center clearfix"]/div[@class="tHeader tHjob"]/div[@class="in"]/div[@class="cn"]/strong/text()')) pd.DataFrame({ "职称":职称, "薪酬":薪酬, "职位信息":职位信息, }) ``` 最后,我们就可以爬取到九个页面中所有工作招聘信息的详细页内容并输出一份完整的表格了,翻页成功。 > 翻页爬取逻辑与前面单页爬取详细页内容的逻辑相似,只不过需要在每一个步骤中多使用遍历循环而已,多练练你也可以实现翻页功能。 ## 3、总结 期中项目又一次的锻炼到我,使我对Python的一些基础知识运用更加熟练,也使我的数据挖掘能力有了进一步提升,在此,我要感谢老师的教导以及另一个同学,这份项目是我与他花了两个晚上的时间共同完成的,期间我和他合作解决了遇到的问题,最终完成了项目,也希望同学们能互帮互助,共同进步。最后,我仍认为我们这份文档中的一些代码还不够完善和优化,仍存在一些问题,但本人能力有限,如若您能看出其中的不足并有更好的实现方法,希望您能倾囊相授,吾必将虚心请教。在此,我与各位同学相互勉励,希望大家能互相学习,一起进步。 **我的数据挖掘期中项目介绍到此结束,谢谢大家!**