Python3 爬取Boss直聘网 工作基本信息(数据清洗)

2018/05/04 16:52
阅读数 60

今天我们来搞一下Boss直聘网,试着拿一下工作相关的信息,有助于ITer们在找工作的时候,做决策分析。

说一下思路,跟之前做的小练习,动态爬取中国图书网相比,Boss直聘的网站是采取REST风格的GET请求,我们首先看一下第一页,query=‘java’的请求URL:

Request URL: https://www.zhipin.com/c101280600/h_101280600/?query=java&page=1&ka=page-1

尝试着爬取深圳地区(101280600)的java工作信息,当然如果想要搜索其他职位也是可以的。

 

首先老规矩观察一下chrome开发者模式中的各项参数,尤其是Response返回的一系列参数,我要需要这些参数来做浏览器模拟登陆。

 

写好需要模拟浏览器登录的header后,我们分析一下拿下一页数据的操作,发现直接改变Url参数就可以跳转到下一页,

因此跳转到下一页代码:

url = "https://www.zhipin.com/c101280600/h_101280600/?"  # 深圳地区编号101280600

paras = 'query=' + language + '&page=' + str(page) + '&ka=page-' + str(page)

 

只需要修改__main__方法的参数,可以控制要读的页数,以及要搜索的职位关键字:

if __name__ == '__main__':
    main('java',3)

 

对于首页的DOM, 我决定用熟悉的 漂亮的汤 来进行解析,通过对网站的DOM源码分解,我们找到了对应需要的一些信息的<tags>,

并且拿到我们想要的职位名称,薪资,学历,以及工作年限;

观察一下Boss直聘的DOM,发现想要的数据比较好拿;

如图都放在<li>里面:

 

 

对应的BeautifulSoup代码:

response = requests.get(newUrl, headers = header, proxies = proxy )
soup = BeautifulSoup(response.text, "html.parser")

for item in soup.select("div .job-list ul li"):

    job_title = item.select("div .job-title")[0].text
    job_detail = 'http://www.zhipin.com'+ item.select("div .info-primary a")[0].get('href')
    job_salary = item.select('div .info-primary span')[0].text
    job_requirements = item.select('div .info-primary p')[0].text.replace("深圳 ", "")
    job_diploma = job_requirements[-2 : len(job_requirements)]  # 拿最后两位
    job_years = job_requirements.replace(job_diploma,"")
    job_info = {"职位": job_title, "薪资": job_salary, "学历": job_diploma, "工龄": job_years}

 

单击选中的job后,可以进入到job的详细信息,其中包括公司的详细信息,岗位要求,任职要求等信息。其实如果能熟练使用bs去解析,我还是更倾向于用漂亮的汤。

但是为了练习,我们尝试用正则表达式去拿我们需要的岗位要求等信息,在py中需要引入re这个包。

 

通过对DOM的分析,我们发现所需要的职位描述等信息,都保存在<div class="text">内,所以我们先用正则直接拿这个div内的信息,Tip:在正则表达式编辑过程中用(.*?)表示

自己需要的内容,可以先调用re.compile('regax...')编译了正则表达式后,再用re.findall()方法去拿需要的值。因为后面我们想通过岗位需求内的数据做词频分析,所以也做了数

清洗 。

response = requests.get(url,headers = header)
 regax = re.compile('<div class="job-sec">.*?<div class="text">(.*?)</div>',re.S)
 result = re.findall(regax, response.text)[0]  # 拿到任职要求等信息
 filtedData = re.findall(r'[一-龥]+',result) # 拿到清洗过的数据,存在数组中

 

然后我们想把job对应的信息存入excel表格,以及需要进行词频分析的数据,存入txt文件,对应的方法在上一篇以及记录啦。

在程序写到一半的时候,突然发生一个致命的问题,因为访问次数过多,Boss官网把我的Ip给封了。。。

 

好尴尬,在网上查了一下,发现爬虫程序很多都是挂代理来抓别人数据的。。。所以,我们就先去抓代理网站的代理ip :-)

在网上搜了搜,有很多免费的代理ip池,我们就去抓取一部分ip,生成自己的random ip池。

这样。。我们就需要首先去爬下代理ip网站的ip地址喽:

def get_ip_list(url, headers):
    web_data = requests.get(url, headers=headers)
    soup = BeautifulSoup(web_data.text, 'lxml')
    ips = soup.find_all('tr')
    ip_list = []
    for i in range(1, len(ips)):
        ip_info = ips[i]
        tds = ip_info.find_all('td')
        ip_list.append(tds[1].text + ':' + tds[2].text)
    return ip_list


def get_random_ip(ip_list):
    proxy_list = []
    for ip in ip_list:
        proxy_list.append('http://' + ip)
    proxy_ip = random.choice(proxy_list)
    proxies = {'http': proxy_ip}
    return proxies


def get_ip():
    url = 'http://www.xicidaili.com/nn/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
    }
    ip_list = get_ip_list(url, headers=headers)
    proxies = get_random_ip(ip_list)
    return proxies

 

拿到生成的代理ip后,我们在每个request请求里,加上他:

proxy = get_ip()
 response = requests.get(newUrl, headers = header, proxies = proxy )

 

进程池的部分我们就不多说啦,请看上回分解,我加了两个锁,分别锁csv文件的写入,以及txt文件的写入。

文本预处理:

之后我们对于已经收集过的数据,进行词频分析,首先要使用jieba这个包,jieba可以帮助分割中文字符,调用jieba的cut方法即可。在中文语言中,会有很多没有特殊意义的字词,比如,的,你,然后。。等等,我们想把这些词从我们的数据中去掉,要洗掉这些stopwords。首先我们需要google一下中文的stopwords.txt文件,里面会有很多连词,使我们需要去掉的,当然我们也可以在这个文件中添加我们自己想去掉的一些词,所以我们先试试把我们不想要的一些词从stopwords里面去掉。

def  data_filter(path):
    content = read_txt_file(path)

    stopwords = {}.fromkeys([line.rstrip() for line in open('stopwords.txt')])
    result = ''
    for word in content:
        if(word not in stopwords):
            result += word
    result = jieba.cut(result)
    result = dict(Counter(result))  # list字典,包含key与出现字词value
    for k,v in result.items():
        print('%s%d' %(k,v))

 

然后打印出来,当然可以根据统计进行排序,做成统计图,这里就不多描述了

 

完整代码:

from bs4 import BeautifulSoup
import requests
import re
import csv
import multiprocessing
import random
import jieba
from collections import Counter


def get_one_page(lock1, lock2, language, page, title, path1, path2):
    '''
        第一页的Request URL: https://www.zhipin.com/c101280600/h_101280600/?query=java&page=1&ka=page-1
    '''
    url = "https://www.zhipin.com/c101280600/h_101280600/?"  # 深圳地区编号101280600

    paras = 'query=' + language + '&page=' + str(page+5) + '&ka=page-' + str(page)

    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
        'Host': 'www.zhipin.com',
        'Referer': 'https://www.zhipin.com/',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9'
    }
    newUrl = url+paras
    proxy = get_ip()
    response = requests.get(newUrl, headers = header, proxies = proxy )
    soup = BeautifulSoup(response.text, "html.parser")
    print(multiprocessing.current_process().name + ' ' + newUrl) #显示进程拿url的情况

    for item in soup.select("div .job-list ul li"):

        job_title = item.select("div .job-title")[0].text
        job_detail = 'http://www.zhipin.com'+ item.select("div .info-primary a")[0].get('href')
        job_salary = item.select('div .info-primary span')[0].text
        job_requirements = item.select('div .info-primary p')[0].text.replace("深圳 ", "")
        job_diploma = job_requirements[-2 : len(job_requirements)]  # 拿最后两位
        job_years = job_requirements.replace(job_diploma,"")
        job_info = {"职位": job_title, "薪资": job_salary, "学历": job_diploma, "工龄": job_years}
        write_csv_rows(lock1, path1, title, job_info)
        # print(job_title, job_detail, job_salary, job_diploma, job_years)
        parse_detailed_info(lock2, job_detail, header, path2)  # 交给另一个方法分析 公司详细信息
    print(multiprocessing.current_process().name+ " complete!")

def parse_detailed_info(lock, url, header, path):
    ''' 用正则来拿公司详细信息页面的 职位描述,任职要求等 '''
    response = requests.get(url,headers = header)
    regax = re.compile('<div class="job-sec">.*?<div class="text">(.*?)</div>',re.S)
    result = re.findall(regax, response.text)[0]  # 拿到任职要求等信息
    filtedData = re.findall(r'[一-龥]+',result) # 拿到清洗过的数据,存在数组中
    write_txt_file(lock, path, ''.join(filtedData))


def write_csv_headers(path, title):
#  write header of csv
    with open(path, 'a', encoding='gb18030', newline='') as f:
        f_csv = csv.DictWriter(f, title)
        f_csv.writeheader()


def write_csv_rows(lock, path, title, rows):
# write one row
    lock.acquire()
    with open(path, 'a', encoding='gb18030', newline='') as f:
        f_csv = csv.DictWriter(f, title)
        if type(rows) == type({}):
            f_csv.writerow(rows)
        else:
            f_csv.writerows(rows)
    lock.release()


def write_txt_file(lock, path, txt):
    lock.acquire()
    with open(path, 'a', encoding='gb18030', newline='') as f:
        f.write(txt)
    lock.release()


def read_txt_file(path):
    with open(path, 'r', encoding='gb18030', newline='') as f:
        return f.read()


def scraping_data(language, page, title, path1, path2):
    pool = multiprocessing.Pool()
    lock1 = multiprocessing.Manager().Lock()  # for csv
    lock2 = multiprocessing.Manager().Lock()  # for txt
    for i in range(page):
        pool.apply_async(get_one_page, (lock1, lock2, language, i+1, title, path1, path2))
    pool.close()
    pool.join()


def get_ip_list(url, headers):
    web_data = requests.get(url, headers=headers)
    soup = BeautifulSoup(web_data.text, 'lxml')
    ips = soup.find_all('tr')
    ip_list = []
    for i in range(1, len(ips)):
        ip_info = ips[i]
        tds = ip_info.find_all('td')
        ip_list.append(tds[1].text + ':' + tds[2].text)
    return ip_list


def get_random_ip(ip_list):
    proxy_list = []
    for ip in ip_list:
        proxy_list.append('http://' + ip)
    proxy_ip = random.choice(proxy_list)
    proxies = {'http': proxy_ip}
    return proxies


def get_ip():
    url = 'http://www.xicidaili.com/nn/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
    }
    ip_list = get_ip_list(url, headers=headers)
    proxies = get_random_ip(ip_list)
    return proxies

def  data_filter(path):
    content = read_txt_file(path)

    stopwords = {}.fromkeys([line.rstrip() for line in open('stopwords.txt')])
    result = ''
    for word in content:
        if(word not in stopwords):
            result += word
    result = jieba.cut(result)
    result = dict(Counter(result))  # list字典,包含key与出现字词value
    for k,v in result.items():
        print('%s%d' %(k,v))

def main(language, page):
    cvs_file_name = '深圳_' + language + '_jobInfo.csv'
    txt_file_name = language + '关键字数据库.txt'
    title = ['职位','薪资','学历','工龄']
    write_csv_headers(cvs_file_name,title)
    scraping_data(language, page, title, cvs_file_name, txt_file_name)
    data_filter(txt_file_name)



if __name__ == '__main__':
    main('java',10)

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部