欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息

程序员文章站 2022-04-27 11:25:27
新手训练 51job 网站完整的Python爬虫程序文章目录前言一、网站解析1.分析网站加载形式2.分析网站的url二、代码实现1.引入库2.解析城市编码3.获取数据3.1 解析数据3.2 数据的清洗和存储4.完整代码总结下载重要前言今天在学Python爬虫的书,看到有个爬取51job招聘网的信息实战,使用的是requests + BeautifulSoup4,但是代码才写了一半就发现出不来结果。之前也有学过一些爬虫,本着学习的精神,我打算自己实现一下。一、网站解析1.分析网站加载形.....

训练 51job 网站完整的Python爬虫程序




前言

今天在学Python爬虫的书,看到有个爬取51job招聘网的信息实战,使用的是requests + BeautifulSoup4,但是代码才写了一半就发现出不来结果。

之前也有学过一些爬虫,本着学习的精神,我打算自己实现一下。


一、网站解析

1.分析网站加载形式

使用Chrome打开51job网站首页,点击 鼠标右键 -> 检查 查看网页的HTML和请求头等信息,如下图所示:

【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息
接下来点击Chrome的 设置 -> 隐私设置和安全性 -> 网站设置 -> 内容 -> JavaScript 将JavaScript关闭,如图所示:
【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息
回去51job网站,随便点击一个城市和职业(或者刷新),发现内容一直载入不出来,如图所示:
【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息
很明显,网页内容是使用JavaScript渲染的,因此加载不出内容。

现在重新打开JavaScript,刷新页面,页面很快就恢复了。

这时候应该考虑使用Selenium模拟爬取,但是这样爬取大量数据的时候很慢不说,也达不到学习的目的。

于是,我开始分析网站,通过搜索,找到document文件中找到了包含本页所有招聘信息的script标签,并发现它处在所有javascript标签中的最后一个,如图:
【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息


2.分析网站的url

分析url是爬虫必须的步骤,通过分析url能够实现翻页等功能。

现在来分析一下,将几种不同城市、关键字、页数的url放在一起,找规律,如下图:
【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息
很明显可以看出:城市不同,前面的编码就不同;页数不同,.html前的数字也会跟着变;关键字不同,中间的一堆编码也会变。

那么问题来了,这个城市的编码怎么获取呢?

如果一个个试,肯定不现实,考虑到刚才关闭JavaScript的时候城市的选项没有载入,因此去搜索java文件才是正解,于是按下 Crtl + F 键搜索一个城市,例如搜索北京,多次搜索后,如图所示:
【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息
得到这个link就可以进行数据清洗了!

但是!!

如果在51job首页进行搜索,就可以得到一个仅有城市编码的js文件!!

【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息
这大大简化了数据清洗的步骤!

最后,我们来验证一下规律,比如我要搜索在广州有关java的招聘信息的第三页,按照规律应该输入以下url:

https://search.51job.com/list/030200,000000,0000,00,9,99,java,2,3.html

【Python爬虫】requests+BeautifulSoup4+MongoDB 爬取51job招聘信息
成功了!


二、代码实现

1.引入库

通过分析后,引入以下库:

import requests from bs4 import BeautifulSoup import time import re import json import random import pandas as pd 

2.解析城市编码

代码如下:

def get_city_code(city_name): """
    获取城市编码
    :param city_name: 城市名
    :return: 城市编码
    """ # 51job地区列表地址 url = 'https://js.51jobcdn.com/in/js/2016/layer/area_array_c.js' r = requests.get(url) html = r.content.decode('gbk') # 编码为GBK格式,要不然部分地名会出现乱码 # 用字符串表达式去除多余部分 city_area_dict = eval(html.split('=')[1].split(';')[0]) # 将得到的字典键值互换,使得地名在前,编号在后 city_area_dict = {v: k for k, v in city_area_dict.items()} # print(city_area_dict) return city_area_dict[city_name] 

3.获取数据

这一步是代码的核心,包括数据解析、数据清洗和数据存储:

3.1 解析数据

def get_data(url): """
    获取数据的json列表
    :param url: 搜索网页的url
    :return: 页面职位全部信息的js文件
    """ # 获取搜索页html headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
                      AppleWebKit/537.36 (KHTML, like Gecko) \
                      Chrome/84.0.4147.135 Safari/537.36' } r = requests.get(url, headers=headers) # 数据清洗 soup = BeautifulSoup(r.content, 'lxml') # 获取页面职位全部信息的js文件 js = soup.find_all('script')[-1].text
    js = js.replace('\nwindow.__SEARCH_RESULT__ = ', '') js = re.sub(r'.*\"engine_search_result\":\[', '', js) data = re.findall('{.*?}', js) return data 

3.2 数据的清洗和存储

def get_more_data(key_word, city_name=None, page_num=None, filename=None, csv_append=True): """
    获取指定页数的信息,默认爬取所有页、存储方式为csv
    :param key_word: 关键字(必选参数)
    :param city_name: 城市名,默认None,即"全国"
    :param page_num: 爬取页数,默认0,爬取所有页
    :param filename: 文件名称,默认'51job_关键字.csv'
    :param csv_append: csv追加模式,不覆盖之前爬取的信息,默认True
    :return: None
    """ city_co = [] if page_num is None: page_num = 2000 if filename is None: filename = '51job_' + key_word + '.csv' city_name = city_name.split(' ') try: for i, city in enumerate(city_name): city_co.append(get_city_code(city)) except KeyError: if city_name is None or city_name == '全国': city_co = ['000000'] else: print('输入的城市不存在!') time.sleep(0.5) print('请重新输入!') return start() for page in range(int(page_num)): code = '%252c'.join(city_co) # 搜索页网址 url = 'https://search.51job.com/list/' + code + ',000000,0000,00,9,99,' +\
              key_word + ',2,' + str(page + 1) + '.html' # 获取该页全部信息的json列表 data = get_data(url) # 判断是否超过最大页数 if data[0].startswith('{"type":"engine_search_result"'): # 将json列表转换为字典列表 data_list = [json.loads(data[i]) for i in range(len(data)) if data[i].startswith('{"type":"engine_search_result"')] df = pd.DataFrame(data_list) df.drop(labels=['ad_track', 'adid'], axis=1, inplace=True) if page == 0: if csv_append: df.to_csv(filename, encoding='utf_8_sig', mode='a+', index=False) else: df.to_csv(filename, encoding='utf_8_sig', mode='w', index=False) else: df.to_csv(filename, encoding='utf_8_sig', mode='a', header=False, index=False) # 提示+休眠 print('第', str(page + 1), '页爬取完成!') time.sleep(random.random() * 2 + 1) # 达到最大页数 else: print('已达到最大页数!停止爬取!') break 

4.完整代码

import requests from bs4 import BeautifulSoup import time import re import json import random import pandas as pd def get_city_code(city_name): """
    获取城市编码
    :param city_name: 城市名
    :return: 城市编码
    """ # 51job地区列表地址 url = 'https://js.51jobcdn.com/in/js/2016/layer/area_array_c.js' 'https://js.51jobcdn.com/in/resource/js/2020/search/index.72ce0fa0.js' r = requests.get(url) html = r.content.decode('gbk') # 编码为GBK格式,要不然部分地名会出现乱码 # 用字符串表达式去除多余部分 city_area_dict = eval(html.split('=')[1].split(';')[0]) # 将得到的字典键值互换,使得地名在前,编号在后 city_area_dict = {v: k for k, v in city_area_dict.items()} # print(city_area_dict) return city_area_dict[city_name] def get_data(url): """
    获取数据的json列表
    :param url: 搜索网页的url
    :return: 页面职位全部信息的js文件
    """ # 获取搜索页html headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \
                      AppleWebKit/537.36 (KHTML, like Gecko) \
                      Chrome/84.0.4147.135 Safari/537.36' } r = requests.get(url, headers=headers) # 数据清洗 soup = BeautifulSoup(r.content, 'lxml') # 获取页面职位全部信息的js文件 js = soup.find_all('script')[-1].text
    js = js.replace('\nwindow.__SEARCH_RESULT__ = ', '') js = re.sub(r'.*\"engine_search_result\":\[', '', js) data = re.findall('{.*?}', js) return data def get_more_data(key_word, city_name, page_num=None, filename=None, csv_append=True): """
    获取指定页数的信息,默认爬取所有页、存储方式为csv
    :param key_word: 关键字(必选参数)
    :param city_name: 城市名
    :param page_num: 爬取页数,默认0,爬取所有页
    :param filename: 文件名称,默认'51job_关键字.csv'
    :param csv_append: csv追加模式,不覆盖之前爬取的信息,默认True
    :return: None
    """ city_co = [] if page_num is None: page_num = 2000 if filename is None: filename = '51job_' + key_word + '.csv' city_name = city_name.split(' ') try: for i, city in enumerate(city_name): city_co.append(get_city_code(city)) except KeyError: if city_name is None or city_name == '全国': city_co = ['000000'] else: print('输入的城市不存在!') time.sleep(0.5) print('请重新输入!') return start() for page in range(int(page_num)): code = '%252c'.join(city_co) # 搜索页网址 url = 'https://search.51job.com/list/' + code + ',000000,0000,00,9,99,' + \
              key_word + ',2,' + str(page + 1) + '.html' # 获取该页全部信息的json列表 data = get_data(url) # 判断是否超过最大页数 if data[0].startswith('{"type":"engine_search_result"'): # 将json列表转换为字典列表 data_list = [json.loads(data[i]) for i in range(len(data)) if data[i].startswith('{"type":"engine_search_result"')] df = pd.DataFrame(data_list) df.drop(labels=['ad_track', 'adid'], axis=1, inplace=True) if page == 0: if csv_append: df.to_csv(filename, encoding='utf_8_sig', mode='a+', index=False) else: df.to_csv(filename, encoding='utf_8_sig', mode='w', index=False) else: df.to_csv(filename, encoding='utf_8_sig', mode='a', header=False, index=False) # 提示+休眠 print('第', str(page + 1), '页爬取完成!') time.sleep(random.random() * 2 + 1) # 达到最大页数 else: print('已达到最大页数!停止爬取!') break def start(): keyword = input('请输入关键字:') city = input('请输入地区(输入多个地区以单个空格隔开):') page = input('请输入页数:') get_more_data(keyword, city, page) if __name__ == '__main__': start() 

总结

本文通过requests+BeautifulSoup4实现了对51job招聘信息的获取、清洗和储存。

觉得有帮助的点个赞呗。


下载

在此代码上添加存储到MongoDB的代码,粉丝可下载哦!:

点此下载


重要

  • 本文仅用于Python爬虫知识的交流,勿作其他用途,违者后果自负!!!
  • 不推荐抓取太多数据,容易对服务器造成负载,设置较长时间的休眠,浅尝辄止即可。

本文地址:https://blog.csdn.net/kunger6/article/details/108241760