先来看一张图了解下爬虫


实现功能

  • 多线程爬取拉勾网招聘信息
  • 维护代理 ip 池
  • 搭建 node 服务器
  • Taro 使用 echarts 做数据分析

1、多线程爬取拉勾网招聘信息

Tip:涉及知识

1.Python3 基础语法 菜鸟教程

2.requests 模块 快速上手

3.Mongodb 数据库 快速安装

4.pymongo 的使用 快速上手

5.线程池 concurrent 快速上手

首先我们先了解下什么是爬虫,看下百度百科的定义

网络爬虫(又被称为网页蜘蛛,网络机器人,在 FOAF 社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。

简单来说就是按照一定规则来抓取内容

抓取什么内容?

我们的目的是抓取拉勾网的招聘信息。 拉勾网武汉站 Python 招聘信息

ok,明白了我们要抓取的数据,下一步就是要找数据的来源了。

我们通过点击下一页观察浏览器控制台,发现每次点击下一页时都有一个新的请求

我们发现这个请求正是招聘数据的来源,这样只要我们之间请求这个接口就可以得来数据了。
于是我们快速的写出来下面的代码

import requests
# 请求参数
data = {'first': False,  # 这个参数固定可以写False'pn': 2,         # pn表示页码'kd': 'Python'  # kd表示搜索关键测
}
# 发送post请求
response = requests.post('https://www.lagou.com/jobs/positionAjax.json?px=default&city=武汉&needAddtionalResult=false', data=data)
# 编码
response.encoding = 'utf-8'
# 获取json
res = response.json()
print(res)

运行后得到以下结果

{ "status": False, "msg": "您操作太频繁,请稍后再访问", "clientIp": "59.xxx.xxx.170", "state": 2408 }

为什么我们请求得到的结果和网页中返回的结果不一样呢?

再回到控制台看看这个请求,发现是需要携带 cookie 的,ok,那我们加上 cookie。可是 cookie 是从哪里来的,总不能写死吧。

我们先把浏览器的 cookie 清除以下(控制台-Application-Cookies 点击清除),然后再刷新下页面,发现了 cookie 的来源

ok,那我们先获取 cookie,再去请求接口

import requests
response = requests.get('https://www.lagou.com/jobs/list_Python?px=default&city=%E6%AD%A6%E6%B1%89')
response.encoding = 'utf-8'
print(response.text)

运行发现返回内容中有这么一句

<div class="tip">当前请求存在恶意行为已被系统拦截,您的所有操作记录将被系统记录!</div>

我擦,什么,怎么会被拦截了~

这个时候我们再想想最前面的一张图,这个网站不会就是有 User-Agent 验证

不管,先加上 User-Agent 再试试

import requests
# 新增了User-Agent请求头
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 QQBrowser/10.4.3587.400"
}
response = requests.get('https://www.lagou.com/jobs/list_Python?px=default&city=%E6%AD%A6%E6%B1%89', headers=headers)
response.encoding = 'utf-8'
print(response.text)

惊奇的发现正常了,返回结果正常了!!!

既然正常了,那我们就获取 cookie 再去请求接口了

import requests
UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 QQBrowser/10.4.3587.400"def getCookie():'''@method 获取cookie'''global UserAgentresponse = requests.get('https://www.lagou.com/jobs/list_Python?px=default&city=%E6%AD%A6%E6%B1%89', headers={"User-Agent": UserAgent})# 获取的cookie是字典类型的cookies = response.cookies.get_dict()# 因为请求头中cookie需要字符串,将字典转化为字符串类型COOKIE = ''for key, val in cookies.items():COOKIE += (key + '=' + val + '; ')return COOKIE# 请求头
headers = {"Cookie": getCookie()
}
print(headers)
# 请求数据
data = {'first': False,  # 这个参数固定可以写False'pn': 2,         # pn表示页码'kd': 'Python'  # kd表示搜索关键测
}
response = requests.post('https://www.lagou.com/jobs/positionAjax.json?px=default&city=武汉&needAddtionalResult=false', data=data, headers=headers)
# 编码
response.encoding = 'utf-8'
# 获取json
res = response.json()
print(res)

这下总该成功然后数据了吧,然后就发现…
尼玛,这么坑,怎么返回结果还是您操作太频繁,请稍后再访问

沉住气,再看看请求头

其他请求头全加上试试

# 把headers改成这样
headers = {"Accept": "application/json, text/javascript, */*; q=0.01","Connection": "keep-alive","Host": "www.lagou.com","Referer": 'https://www.lagou.com/jobs/list_Python?px=default&city=%E6%AD%A6%E6%B1%89',"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8","User-Agent": UserAgent,"Cookie": getCookie()
}

运行之后就成功抓取到数据了。

到这里我们就已经成功的抓取了一页的数据,然后我们就要抓取多页啦。。。

考虑到抓取数据较多,可以采用多线程的方式来提高效率,同时应该将数据存到数据库去(这里使用 Mongodb 数据库,其他数据库一样的道理)

爬虫完成代码

import requests
from pymongo import MongoClient
from time import sleep
# 连接数据库
client = MongoClient('127.0.0.1', 27017)
db = client.db  # 连接mydb数据库,没有则自动创建
# 请求头的cookie和UserAgent
COOKIE = ''
UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 QQBrowser/10.4.3587.400"
# 武汉
city = '%E6%AD%A6%E6%B1%89'# 获取cookie
def getCookie(key_world):global COOKIE, UserAgent, citydata = requests.get('https://www.lagou.com/jobs/list_' + key_world + '?px=default&city=' + city, headers={"User-Agent": UserAgent})cookies = data.cookies.get_dict()COOKIE = ''for key, val in cookies.items():COOKIE += (key + '=' + val + '; ')# 请求数据接口
def getList(page, key_world):global COOKIE, UserAgentdata = {"first": "false","pn": page + 1,"kd": key_world == 'web' and '前端' or key_world}headers = {"Accept": "application/json, text/javascript, */*; q=0.01","Connection": "keep-alive","Host": "www.lagou.com","Referer": 'https://www.lagou.com/jobs/list_' + key_world + '?px=default&city=' + city,"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8","User-Agent": UserAgent,"Cookie": COOKIE}response = requests.post('https://www.lagou.com/jobs/positionAjax.json?px=default&city=武汉&needAddtionalResult=false', data=data, headers=headers)response.encoding = 'utf-8'res = response.json()return res# 抓取数据
def getData(key_world):global COOKIE, UserAgent, client, dbprint('开始抓取'+key_world)# 前端需要转为webif key_world == '%E5%89%8D%E7%AB%AF':table = db.web  # 连接mydb数据库,没有则自动创建else:table = db[key_world]  # 连接mydb数据库,没有则自动创建# 因为请求接口需要cookie,先获取cookiegetCookie(key_world)# 抓取数据for page in range(1, 100):# 请求数据res = getList(page, key_world)# 如果请求成功存入数据库中if res['msg'] == None:print('成功')# 工作岗位position = res['content']['positionResult']['result']# 记录当前的数据one_data = []for idx, item in enumerate(position):one_data.append({'positionName': item['positionName'],'workYear': item['workYear'],'salary': item['salary'],'education': item['education'],'companySize': item['companySize'],'companyFullName': item['companyFullName'],'formatCreateTime': item['formatCreateTime'],'positionId': item['positionId']})# 没有数据了if len(one_data) == 0:break# 存储当前数据table.insert_many(one_data)else:print('失败')# 写日志with open('./log.txt', 'a', -1, 'utf-8') as f:f.write(str(res))f.write('\n')# 重新获取cookiegetCookie(key_world)# 再爬取当页数据res_once = getList(page, key_world)# 工作岗位position_once = res_once['content']['positionResult']['result']# 记录当前的数据one_data = []for idx, item in enumerate(position_once):one_data.append({'positionName': item['positionName'],'workYear': item['workYear'],'salary': item['salary'],'education': item['education'],'companySize': item['companySize'],'companyFullName': item['companyFullName'],'formatCreateTime': item['formatCreateTime']})# 没有数据了if len(one_data) == 0:print(key_world + '存入成功')# 这里用新cookie获取数据还是被限制了,获取不到,这里暂时先休眠60秒,等后面有代理ip池再使用代理ip来解决这个问题sleep(60)return# 存储当前数据table.insert_many(one_data)print(key_world + '存入成功')sleep(60)# 抓取的数据搜索关键词, 前面的示例是Python,这里抓取多个类型的
key_worlds = ['Python', 'Java', 'PHP', 'C', 'C++', 'C#']
# 开始抓取数据
for idx, key_world in enumerate(key_worlds):getData(key_world)

目前还需要解决的两个问题,等有了代理 ip 池再解决。

1.未使用多线程

2.还是会存在封 ip 的情况,需要使用代理

2、维护代理 ip 池

Tip:涉及知识

1.之前的所有知识

2.xpath 解析模块 lxml 快速上手

维护一个 ip 池大致分为两步

1.抓取网上免费代理存到数据库
2.筛选出数据库中的有效代理

看到这里相信你已经知道爬虫的运行原理。维护一个自己 ip 池其实也就是一个定时爬虫不停的去爬取网上免费的代理

先完成第一步, 抓取网上免费代理存到数据库

我们这里爬取 西拉免费代理 IP

老套路,先把网页抓下来,再提取我们想要的内容

先抓数据

import requests
response = requests.get('http://www.xiladaili.com/gaoni/1/')
response.encoding = 'utf-8'
print(response.text)

运行起来就发现已经把所有的内容都抓下来了,很显然这个网站没有反爬虫。

再提取数据
xPath 怎么获取看这里

import requests
from lxml import etree  # xpath解析模块
response = requests.get('http://www.xiladaili.com/gaoni/1/')
response.encoding = 'utf-8'
# print(response.text)
s = etree.HTML(response.text)
# 所有的ip
'''
第一条xpath /html/body/div[1]/div[3]/div[2]/table/tbody/tr[1]/td[1]
第二条xpath /html/body/div[1]/div[3]/div[2]/table/tbody/tr[2]/td[1]
所有的xpath就是把选择tr的部分去掉
/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[1]
'''
ips = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[1]/text()')
# 所有的请求代理协议
types = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[2]/text()')
print(ips)
print(types)

这样我们就提取了我们需要的内容了,再把需要的内容存到数据库

import requests
from lxml import etree  # xpath解析模块
from pymongo import MongoClient# 数据库连接
client = MongoClient('127.0.0.1', 27017)
db = client.ip  # 连接ip数据库,没有则自动创建
table = db.table  # 使用table集合,没有则自动创建response = requests.get('http://www.xiladaili.com/gaoni/1/')
response.encoding = 'utf-8'
s = etree.HTML(response.text)
# 所有的ip
ips = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[1]/text()')
# 所有的请求代理协议
types = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[2]/text()')# 存储到数据库
for index, ip in enumerate(ips):host = ip.split(':')[0]port = ip.split(':')[1]table.insert_one({"ip": host, "port": port, "type": types[index]})

前面我们只爬取了一页,最后就改用多线程来爬取多页数据

import requests
from lxml import etree  # xpath解析模块
from pymongo import MongoClient
from concurrent.futures import ThreadPoolExecutor  # 线程池# 数据库连接
client = MongoClient('127.0.0.1', 27017)
db = client.ip  # 连接mydb数据库,没有则自动创建
table = db.table  # 使用test_set集合,没有则自动创建spider_poll_max = 5  # 爬虫线程池最大数量
spider_poll = ThreadPoolExecutor(max_workers=spider_poll_max)  # 爬虫线程池 max_workers最大数量# 爬取单页数据
def getIp(page):response = requests.get('http://www.xiladaili.com/gaoni/' + str(page + 1)+'/')response.encoding = 'utf-8's = etree.HTML(response.text)# 所有的ipips = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[1]/text()')# 所有的请求代理协议types = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[2]/text()')print(ips)print(types)# 存储到数据库for index, ip in enumerate(ips):host = ip.split(':')[0]port = ip.split(':')[1]table.insert_one({"ip": host, "port": port, "type": types[index]})# 爬取10页
for page in range(0, 10):# 添加一个线程spider_poll.submit(getIp, (page))

还存在的问题

1.还是会存在封 ip 的情况,需要使用代理

抓取 ip 完成了,现在到了验证 ip 的步骤了

再完成第二步,筛选出数据库中的有效代理
我们之前在数据库中创建了一个叫 table 的集合(表),用来存贮所有抓取的 ip(并未有效性检测),再这里我们要专门准备一个叫 ip 的集合,用来存有效 ip。

有效 ip 的检测也分为两步,第一:将 ip 表中的失效代理删除,第二:将 table 表中的有效代理存到 ip 表中。

import requests
from pymongo import MongoClient
from concurrent.futures import ThreadPoolExecutor  # 线程池REQ_TIMEOUT = 3  # 代理的超时时间,# 数据库连接
client = MongoClient('127.0.0.1', 27017)
db = client.ip  # 连接mydb数据库,没有则自动创建
table = db.table  # 所有的ip表
ip_table = db.ip  # 有效ip表# 线程池
spider_poll_max = 20  # 多线程的最大数量
# 创建一个线程池
proving_poll = ThreadPoolExecutor(max_workers=spider_poll_max)def proving(ip):'''@method 检测所有ip中有效的ip'''global table, ip_tablehost = ip['ip']port = ip['port']_id = ip['_id']types = ip['type']proxies = {'http': host+':'+port,'https': host+':'+port}try:# 通过比较ip是否相同来判断代理是否有效OrigionalIP = requests.get("http://icanhazip.com", timeout=REQ_TIMEOUT).contentMaskedIP = requests.get("http://icanhazip.com",timeout=REQ_TIMEOUT, proxies=proxies).content# 删除代理if OrigionalIP == MaskedIP:result = table.delete_one({"ip": host, "port": port, "type": types})else:print('新增有效代理', host+':'+port)# 有效代理则存到ip表中ip_table.insert_one({"ip": host, "port": port, "type": types})except:# 删除代理result = table.delete_one({"ip": host, "port": port, "type": types})def proving_ip(ip):'''@method 检测有效ip中无效ip'''global ip_tablehost = ip['ip']port = ip['port']_id = ip['_id']types = ip['type']#代理proxies = {'http': host+':'+port,'https': host+':'+port}#try except用于检测超时的代理try:# 通过比较ip是否相同来判断代理是否有效OrigionalIP = requests.get("http://icanhazip.com", timeout=REQ_TIMEOUT).contentMaskedIP = requests.get("http://icanhazip.com",timeout=REQ_TIMEOUT, proxies=proxies).content# 删除代理if OrigionalIP == MaskedIP:# ip相同则是无效代理ip_table.delete_one({"ip": host, "port": port, "type": types})else:print('有效代理', host+':'+port)except:# 删除代理超时的代理ip_table.delete_one({"ip": host, "port": port, "type": types})# 进行第一步,先检测有效ip表中无效的ip
proving_ips = ip_table.find({})
print('开始清理无效ip...')
# 有效性验证
for data in proving_ips:# 添加一个线程proving_poll.submit(proving_ip, (data))# 再进行第二步,提取所有ip中的有效ip
ips = table.find({})
print('开始代理有效性验证...')
# 有效性验证
for data in ips:# 添加一个线程proving_poll.submit(proving, (data))

到这里我们就有了一个专门存在有效代理的数据表了(ip 表),以后直接从这里取一个有效代理就可以直接使用了

现在解决一下前面所遗留的问题
1.使用 多线程 + 代理 完成招聘数据爬取

import requests
import random
from pymongo import MongoClient
from time import sleep
from concurrent.futures import ThreadPoolExecutor  # 线程池# 连接数据库
client = MongoClient('127.0.0.1', 27017)
db = client.db  # 连接mydb数据库,没有则自动创建
ip_client = client.ip  # 连接mydb数据库,没有则自动创建
ip_table = ip_client.ip  # 有效ip表# 线程池
spider_poll_max = 7  # 爬虫线程池最大数量
# 爬虫线程池 max_workers最大数量
spider_poll = ThreadPoolExecutor(max_workers=spider_poll_max)# 请求头的cookie和UserAgent
UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3704.400 QQBrowser/10.4.3587.400"
# 武汉
city = '%E6%AD%A6%E6%B1%89'def getRandomIp():'''@method 有效ip表中随机取一个ip'''global ip_table# 获取总数量count = ip_table.count_documents({})  # 查询一共有多少数据# 随机取一个index = random.randint(0, count)  # 获取0到count的随机整数print(count, index)data = ip_table.find().skip(index).limit(1)for item in data:print({'ip': item['ip'], 'port': item['port']})return {'ip': item['ip'], 'port': item['port']}def getCookie(key_world):'''@method 获取cookie'''global UserAgent, city, ip_table# 随机获取一个代理,防止被封iprow = getRandomIp()print(50, row)try:proxies = {'http': row['ip'] + ':' + row['port'],'https': row['ip'] + ':' + row['port']}data = requests.get('https://www.lagou.com/jobs/list_' + key_world + '?px=default&city=' + city, timeout=10, proxies=proxies, headers={"User-Agent": UserAgent})cookies = data.cookies.get_dict()COOKIE = ''for key, val in cookies.items():COOKIE += (key + '=' + val + '; ')return COOKIEexcept:print('获取cookie失败,无效代理', row['ip'] + ':' + row['port'])# 删除无效代理ip_table.delete_one({"ip": row['ip'], "port": row['port']})# 重新获取cookiereturn getCookie(key_world)def getData(obj):'''@method 抓取一页数据'''global UserAgent, client, dbkey_world = obj['key_world']  # 关键词page = obj['page']  # 分页print('开始抓取')# 连接数据表,前端需要转为web表,if key_world == '%E5%89%8D%E7%AB%AF':table = db.web  # 连接mydb数据库,没有则自动创建else:table = db[key_world]  # 连接mydb数据库,没有则自动创建# 随机获取一个代理,防止被封iprow = getRandomIp()print(102, row)proxies = {'http': row['ip'] + ':' + row['port'],'https': row['ip'] + ':' + row['port']}try:# 因为请求接口需要cookie,先获取cookiecookie = getCookie(key_world)# 抓取数据开始data = {"first": "false","pn": page + 1,"kd": key_world == '%E5%89%8D%E7%AB%AF' and '前端' or key_world}headers = {"Accept": "application/json, text/javascript, */*; q=0.01","Connection": "keep-alive","Host": "www.lagou.com","Referer": 'https://www.lagou.com/jobs/list_' + key_world + '?px=default&city=' + city,"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8","User-Agent": UserAgent,"Cookie": cookie}response = requests.post('https://www.lagou.com/jobs/positionAjax.json?px=default&city=武汉&needAddtionalResult=false',data=data, timeout=10, proxies=proxies, headers=headers)response.encoding = 'utf-8'res = response.json()print(res)# 如果请求成功存入数据库中if res['msg'] == None:# 工作岗位position = res['content']['positionResult']['result']# 记录当前的数据one_data = []for idx, item in enumerate(position):one_data.append({'positionName': item['positionName'],'workYear': item['workYear'],'salary': item['salary'],'education': item['education'],'companySize': item['companySize'],'companyFullName': item['companyFullName'],'formatCreateTime': item['formatCreateTime'],'positionId': item['positionId']})# 没有数据了if len(one_data) == 0:print(key_world + '第'+page+'页数据为空')return# 存储当前数据table.insert_many(one_data)print(key_world + '第'+page+'页存入成功')else:print(key_world + '第'+page+'页存入失败')# 写日志with open('./log.txt', 'a', -1, 'utf-8') as f:f.write('key_world:'+key_world+',page:'+page+'\n')f.write(str(res))f.write('\n')# 删除无效代理ip_table.delete_one({"ip": row['ip'], "port": row['port']})# 重新添加到任务中spider_poll.submit(getData, ({'key_world': key_world, 'page': page}))except:print('超时代理', row['ip'] + ':' + row['port'])# 删除无效代理ip_table.delete_one({"ip": row['ip'], "port": row['port']})# 重新添加到任务中spider_poll.submit(getData, ({'key_world': key_world, 'page': page}))# 搜索的关键词, 第一个为前端
key_worlds = ['%E5%89%8D%E7%AB%AF', 'Python', 'Java', 'PHP', 'C', 'C++', 'C#']
# 添加任务
for idx, key_world in enumerate(key_worlds):# 每种搜索关键词爬取100页for page in range(1, 100):# 添加一个任务spider_poll.submit(getData, ({'key_world': key_world, 'page': page}))

2.使用代理爬取代理

import requests
import random
from pymongo import MongoClient
from lxml import etree  # xpath解析模块
from concurrent.futures import ThreadPoolExecutor  # 线程池# 数据库连接
client = MongoClient('127.0.0.1', 27017)
db = client.ip  # 连接mydb数据库,没有则自动创建
table = db.table  # 将抓取的ip全部存到table表中
ip_table = db.ip  # 有效ip表# 线程池
spider_poll_max = 50  # 爬虫线程池最大数量
# 爬虫线程池 max_workers最大数量
spider_poll = ThreadPoolExecutor(max_workers=spider_poll_max)def getRandomIp():'''@method 有效ip表中随机取一个ip'''global ip_table# 获取总数量count = ip_table.count_documents({})  # 查询一共有多少数据# 随机取一个index = random.randint(0, count)  # 获取0到count的随机整数# print(count, index)data = ip_table.find().skip(index).limit(1)for item in data:return {'ip': item['ip'], 'port': item['port']}def getIp(page):'''@method 爬取数据'''# 随机获取一个代理,防止被封iprow = getRandomIp()proxies = {'http': row['ip'] + ':' + row['port'],'https': row['ip'] + ':' + row['port']}try:# 抓取代理response = requests.get('http://www.xiladaili.com/gaoni/' + str(page + 1)+'/', timeout=10, proxies=proxies)# 设置编码response.encoding = 'utf-8'# 解析s = etree.HTML(response.text)# 获取ip和请求类型ips = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[1]/text()')types = s.xpath('/html/body/div[1]/div[3]/div[2]/table/tbody/tr/td[2]/text()')if (len(ips) == 0):print('抓取数据为空')# 写日志with open('./log.txt', 'a', -1, 'utf-8') as f:f.write(response.text)f.write('--------------------------------------------------')f.write('\n')f.write('\n')f.write('\n')f.write('\n')# 删除无效代理ip_table.delete_one({"ip": row['ip'], "port": row['port']})else:print('抓取数据成功, 正在存入数据库...')# 存储ipfor index, ip in enumerate(ips):host = ip.split(':')[0]port = ip.split(':')[1]table.insert_one({"ip": host, "port": port, "type": types[index]})except:print('超时')# 删除无效代理ip_table.delete_one({"ip": row['ip'], "port": row['port']})# 抓取网页的数量
for page in range(0, 100):# 添加一个线程spider_poll.submit(getIp, (page))

3、搭建 node 服务器

Tip:涉及知识

1.JavaScript 基础语法 菜鸟教程

2.http 模块 快速上手

3.mongoose 模块 快速安装

server.js

const http = require('http');
var url = require('url');
var qs = require('qs');
const { get_education } = require('./api/education.js');
const { get_workYear } = require('./api/workYear.js');
const { get_salary } = require('./api/salary.js');//用node中的http创建服务器 并传入两个形参
http.createServer(function(req, res) {//设置请求头  允许所有域名访问 解决跨域res.setHeader('Access-Control-Allow-Origin', '*');res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' }); //设置response编码try {//获取地址中的参数部分var query = url.parse(req.url).query;//用qs模块的方法  把地址中的参数转变成对象 方便获取var queryObj = qs.parse(query);//获取前端传来的myUrl=后面的内容  GET方式传入的数据var type = queryObj.type;/*/get_education 获取学历分布/get_workYear 获取工作经验分布/get_salary 获取薪资分布*/if (req.url.indexOf('/get_education?type=') > -1) {get_education(type, function(err, data) {if (err) res.end({ errmsg: err });console.log('[ok] /get_education');res.end(JSON.stringify(data));});} else if (req.url.indexOf('/get_workYear?type=') > -1) {get_workYear(type, function(err, data) {if (err) res.end({ errmsg: err });console.log('[ok] /get_workYear');res.end(JSON.stringify(data));});} else if (req.url.indexOf('/get_salary?type=') > -1) {get_salary(type, function(err, data) {if (err) res.end({ errmsg: err });console.log('[ok] /get_salary');res.end(JSON.stringify(data));});} else {console.log(req.url);res.end('404');}} catch (err) {res.end(err);}
}).listen(8989, function(err) {if (!err) {console.log('服务器启动成功,正在监听8989...');}
});

education.js 文件 (其他文件与这个类似)

const { model } = require('./db.js');//获取学历
exports.get_education = function(type, callback) {//查询所有的本科学历model[type].find({}, { education: 1 }, function(err, res) {if (err) return callback(err);let result = [],type = [];//找出每种学历的数量res.forEach(item => {if (type.includes(item.education)) {result[type.indexOf(item.education)].count++;} else {type.push(item.education);result.push({label: item.education,count: 1});}});callback(null, result);});
};

db.js

const mongoose = require('mongoose');
const DB_URL = 'mongodb://localhost:27017/db';
// 连接数据库
mongoose.connect(DB_URL, { useNewUrlParser: true });var Schema = mongoose.Schema;//所有的表
let collections = ['web', 'Python', 'PHP', 'Java', 'C++', 'C#', 'C'];
let model = {};//为每张表都生成一个model用来操作表
collections.forEach(collection => {let UserSchema = new Schema({positionName: { type: String }, //职位workYear: { type: String }, //工作年限salary: { type: String }, //薪水education: { type: String }, //学历companySize: { type: String }, //规模companyFullName: { type: String }, //公司名formatCreateTime: { type: String }, //发布时间positionId: { type: Number } //id},{collection: collection});let web_model = mongoose.model(collection, UserSchema);model[collection] = web_model;
});exports.model = model;

然后我们用运行 server.js 文件, cmd 中输入 node server.js

运行成功后再用浏览器打开 localhost:8989/get_education?type=Python就可以看到数据了

4、Taro 使用 echarts 做数据分析

Tip:涉及知识

1.熟悉微信小程序 官方文档

2.熟悉 react 语法 官方文档

3.熟悉 Taro 的使用 官方文档

4.熟悉 echarts 的使用 快速上手 官网实例

5.在微信小程序中使用 echarts 快速上手

小程序源码请移步 github

Tip.

1.第一步请先抓取代理存入的table表中
2.第二步再验证代理确保ip表中有数据
3.最后在运行爬虫爬取数据
4.写个定时任务去循环前三步

项目源码 github 欢迎star

Python搭建代理池爬取拉勾网招聘信息相关推荐

  1. Python爬虫实战之一 - 基于Requests爬取拉勾网招聘信息,并保存至本地csv文件

    Python爬虫实战之二 - 基于Requests抓取拉勾网招聘信息 ---------------readme--------------- 简介:本人产品汪一枚,Python自学数月,对于小白,本 ...

  2. 【python爬虫02】使用Scrapy框架爬取拉勾网招聘信息

    使用Scrapy框架爬取拉勾网招聘信息 最近接触了Scrapy爬虫框架,简单写了个爬虫爬取拉钩网的招聘信息,加深对Scrapy框架的理解,不得不说Scrapy框架其实还是蛮方便的,就像爬虫流水线一样, ...

  3. 什么你还不知道招聘信息,小唐来教你——最新2021爬取拉勾网招聘信息(一)

    文章目录 前言 一.准备我们的库 二.分析分析 三. 代码 四.数据展示 小唐的心路历程 上一篇:没有啦! 下一篇:什么你还不知道招聘信息,小唐来教你--最新2021爬取拉勾网招聘信息(二) 前言 有 ...

  4. 什么你还不知道招聘信息,小唐来教你——最新2021爬取拉勾网招聘信息(二)

    文章目录 前言 一.准备我们的库 二.数据清洗 三.核密度图及词云制作 四.完整代码 五.扩展 上一篇:什么你还不知道招聘信息,小唐来教你--最新2021爬取拉勾网招聘信息(一) 下一篇:没有拉! 前 ...

  5. Python爬虫-代理池-爬取代理入库并测试代理可用性

    目的:建立自己的代理池.可以添加新的代理网站爬虫,可以测试代理对某一网址的适用性,可以提供获取代理的 API. 整个流程:爬取代理 ----> 将代理存入数据库并设置分数 ----> 从数 ...

  6. Python爬取拉勾网招聘信息

    此代码运行建议Python3,省却中文编码的麻烦 遇到的几个问题: (1)拉钩网的数据是通过js的ajax动态生成,所以不能直接爬取,而是通过post'http://www.lagou.com/job ...

  7. 什么样的人才能做互联网产品经理【爬取拉勾网招聘信息】

    分析目的 在这个校招如火如荼的季节,相信大家对各种招聘网站已经再熟(yan)悉(fan)不过了,各种java开发.算法.前端.后端岗位真的是琳琅满目,而我觉得其中与我的个人兴趣和专业匹配度最高的就是产 ...

  8. 爬取拉勾网招聘信息(招聘岗位,公司名称,薪资等)

    用爬虫框架进行爬取,框架还是feapder 代码如下: import feapder#轻量级爬虫 class LastAirSpider(feapder.AirSpider):def start_ca ...

  9. 2.简单爬虫————爬取拉勾网招聘信息(一)

    该文章仅供学习,如有错误,欢迎指出 1.开始创建一个项目 mkdir lagou 2.进入到文件夹下创建python3的虚拟环境 pipenv install scrapy 3.进入pipenv 下使 ...

最新文章

  1. 新建eclipse的java项目报错处理
  2. C++宏assert()
  3. 从 Java 档案(JAR) 中读取文件
  4. python创建文件os_Python之文件与目录操作(os、zipfile、tarfile、shutil)
  5. linux shell脚本中调用另一个shell脚本
  6. 详解@Builder用法
  7. book mac pro怎么重装系统_MAC笔记本电脑解决NTFS硬盘无法写入的简要方法
  8. f1 score 代码_腾讯广告算法大赛冠军代码解读:稠密特征工程
  9. 优麒麟在linux下安装教程,在优麒麟Ubuntu Kylin系统中安装百度网盘Linux版.deb的方法...
  10. C语言程序设计与有限元,C语言与有限元程序设计.pdf
  11. 利用Python我发现我女朋友每天都在看这种网站
  12. C语言基础项目:200 行代码实现贪吃蛇,思路+源码详解
  13. 广东南方地形地籍成图软件CASS10.1十大新亮点(资源下载在文尾)
  14. 前端使用Aliplayer 播放器 播放rtmp直播流
  15. Andersen Global宣布进驻印度市场
  16. JAVA跑步计时器app_锻炼计时器app
  17. 【计算机网络】学习笔记
  18. AD19画原理图和PCB注意事项和步骤(主要为PCB和总结注意事项)
  19. 使用eclipse开发Java Web项目(最最最基础)
  20. 用容器类实现事件坚挺器接口的示例

热门文章

  1. Cannot run program jad
  2. 积水成渊之softmax函数
  3. 第四十九,反射基本介绍
  4. html网页宽度自动适应手机屏幕
  5. 男人凭什么三十而立——一定要攒点钱
  6. 基于不同策略的英文单词的词频统计和检索系统(C++)
  7. 计算语言学之语法理论
  8. js实现flappybird解析
  9. c语言编程TLC2543AD采集,基于tlc2543的电压表c语言程序
  10. 网易考拉海购:电商高并发架构设计的铁律