目录
腾讯招聘网职位爬取程序
1.需求分析
腾讯招聘网首页URL:https://careers.tencent.com/search.html
首页与大部分求职网并无太大差别,我们的目的是爬取某个岗位(如运维,设计爬虫程序时会提示输入工作名称)的所有工作岗位信息。
这些信息包括:岗位名称、发起时间、工作地点,工作职责、工作要求。
2.URL分析
1)分析首页URL
首先看到腾讯招聘网的首页:https://careers.tencent.com/search.html,查看源码:
啥也没有,说明所有的招聘信息都是JS嵌进去的,或者说是异步(AJax)获取的。
进入控制台,点击Network,点击XHR,找到两个文件,我们在Query开头的文件中获取到了岗位信息。
下面来分析这一段URL。
在地址栏中输入这一串URL,我的格式之所以这么清晰是以为装了一个叫JSONView的插件(谷歌浏览器插件,安装教程:https://blog.csdn.net/ck784101777/article/details/104291634)
下面来分析这一段URL,此URL有很多参数,我们的目的是尽量的缩短这个URL的长度(不缩短也可以),进过缩短,发现必须保留的参数有timestamp、keyword、pageIndex、pageSize
timestamp:好理解,时间桩,进过我的测试这个时间桩写什么都无所谓,但是必须有,所以我们下面让他为1就可以了。
keyword:搜索的职位名称,为空时代表搜索全部职位,当搜索java工程师时keyword=java工程师
pageIndex:代表页数,一个URL下只有10条职位信息,页数从1开始,我们接下来也要计算某职位的页数,只有这样才能将数据抓全
pageSize:固定值,职位数量,这里是10
经过缩短,这条URL变为(其中keyword和pageIndex是变化的):
2)分析岗位详情URL
这是岗位详细的URL:https://careers.tencent.com/jobdesc.html?postId=1229978126717554688
很明显的可以看出postId决定这一页面显示什么信息,所以我们的目的就是提取postId。而恰好,刚才我们分析首页URL时,页面里面正好有PostId的信息。
3.程序设计思路
1)URL分级
根据刚才对URL的分级,我们可以将首页URL称为1级URL,将工作详情URL称为2级URL,我们要先抓取1级URL,再抓取2级URL。
根据这个思路,我们可以设置两个队列,一个队列用于存放1级URL,一个队列存放2级URL。
2)功能函数:网页请求函数
这个函数用于请求网页内容,使用requests模块的get()方法即可,该函数还需要传入一个参数url。如下
#功能函数:调用requests请求页面 def get_html(self,url): html=requests.get(url=url,headers=self.headers).text return html
3)功能函数:一级URL入队
根据上面所述,我们需要一个函数来把一级页面的URL添加到队列里,但是我们不知道有多少个一级页面,我们需要计算出pageIndex的值。
#功能函数:将url放入队列(一级url) def url_inQueue(self,keyword): count=self.get_pageCount(keyword) for i in range(1,count+1): url=self.index_url.format(keyword,i) self.q1.put(url)
4)功能函数:计算pageIndex
在看到一级URL所展示的内容,我们可以看到有个叫Count的值,这个值就是工作的总数,根据工作的总数,除以每个页面的职位数量(10个),就可以计算出总的页数。
#功能函数:获取工作总页数 def get_pageCount(self,keyword): #keyword是工作的关键字,1代表仅请求第一页(第一页就有总的个数) url=self.index_url.format(keyword,1) html=self.get_html(url) html=json.loads(html) count=html["Data"]['Count'] pagecount=0 #pageSize=10 每页有10条工作信息,除以10获取到总的页数 if count % 10 == 0: pagecount=count // 10 else: pagecount=count // 10 + 1 return pagecount
5)核心函数:解析一级URL
此时我们已经拿到所有的一级URL,下面就要提取PostID,组成二级URL,在把URL插入到二级URL的队列
#核心函数:解析一级url,获取二级url并放入队列 def parse_indexurl(self): #死循环用于阻止线程阻塞 while True: if not self.q1.empty(): url = self.q1.get() html=self.get_html(url) jsonlists=json.loads(html) for i in jsonlists["Data"]["Posts"]: postsid=i['PostId'] #格式化二级url url=self.second_url.format(postsid) #将二级url放入二级url的队列 self.q2.put(url) else: break
6)核心函数:解析二级URL
直接调用get_html()方法拿到页面内容,再将其json化,就可以输出职位的信息。
# 核心函数:解析二级url,获取职位信息 def parse_secondurl(self): # 死循环用于阻止线程阻塞 while True: if not self.q2.empty(): url=self.q2.get() html=self.get_html(url) jsonlist=json.loads(html) items={} # 封装数据 items["RecruitPostName"] = jsonlist['Data']['RecruitPostName'] items["LocationName"] = jsonlist['Data']['LocationName'] items["Responsibility"] = jsonlist['Data']['Responsibility'] items['LastUpdateTime'] = jsonlist['Data']['LastUpdateTime'] print(items) #加锁(防止线程争抢资源),找到一个工作总数加1 self.lock.acquire() self.jobcount+=1 self.lock.release() else: break
4.设置多线程
按照程序执行的步骤:
1)拿到1级URL并放到队列
2)从队列中提取并解析1级URL
3)将解析出的2级URL放入队列
4)解析2级URL并输出信息
这样的执行步骤是单线程的,也就是按顺序执行,程序要等到提取完所有的1级URL再进行2级URL的解析,并且2级URL的解析也是单线程进行解析,如果遇到一个页面需要较长的等待时间,程序会一直等待。
所以我们要将程序做成多线程的,一旦爬到1级URL,就有线程去解析2级URL,整个过程是并行进行的。
多线程代码如下:
# 执行函数:程序执行的入口 def run(self): #获取职位关键词 keyword=input("请输入搜索的职位:") keyword=parse.quote(keyword) #调用入队函数,把一级页面都放入队列 self.url_inQueue(keyword) t1_lists=[] t2_lists=[] # 开启1个线程用于抓取一级页面的url for i in range(1): t=Thread(target=self.parse_indexurl()) t1_lists.append(t) t.start() # 开启大于1个线程用于抓取二级页面的url,缩短抓取时间 for i in range(2): t=Thread(target=self.parse_secondurl()) t2_lists.append(t) t.start() # 阻塞线程 for t in t1_lists: t.join() for t in t2_lists: t.join()
5.程序代码
-
import requests
-
import time
-
import json
-
from UserAgent
import get_UserAgent
-
from urllib
import parse
-
from queue
import Queue
-
from threading
import Thread,Lock
-
-
class TecentJobs_spider(object):
-
#初始化函数
-
#定义一二级页面URL格式
-
#定义headers
-
#定义url队列
-
#定义一个整数用于记录工作的个数,并赋予锁
-
def __init__(self):
-
self.index_url=
"https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1582079472895&keyword={}&pageIndex={}&pageSize=10"
-
self.second_url=
"https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1582086419823&postId={}&language=zh-cn"
-
self.headers={
-
'User-Agent':get_UserAgent(),
-
}
-
self.q1=Queue()
-
self.q2=Queue()
-
self.jobcount=
0
-
self.lock=Lock()
-
-
#功能函数:调用requests请求页面
-
def get_html(self,url):
-
html=requests.get(url=url,headers=self.headers).text
-
return html
-
-
#功能函数:获取工作总页数
-
def get_pageCount(self,keyword):
-
#keyword是工作的关键字,1代表仅请求第一页(第一页就有总的个数)
-
url=self.index_url.format(keyword,
1)
-
html=self.get_html(url)
-
html=json.loads(html)
-
count=html[
"Data"][
'Count']
-
pagecount=
0
-
#pageSize=10 每页有10条工作信息,除以10获取到总的页数
-
if count %
10 ==
0:
-
pagecount=count //
10
-
else:
-
pagecount=count //
10 +
1
-
return pagecount
-
-
#功能函数:将url放入队列(一级url)
-
def url_inQueue(self,keyword):
-
count=self.get_pageCount(keyword)
-
for i
in range(
1,count+
1):
-
url=self.index_url.format(keyword,i)
-
self.q1.put(url)
-
-
#核心函数:解析一级url,获取二级url并放入队列
-
def parse_indexurl(self):
-
#死循环用于阻止线程阻塞
-
while
True:
-
if
not self.q1.empty():
-
url = self.q1.get()
-
html=self.get_html(url)
-
jsonlists=json.loads(html)
-
for i
in jsonlists[
"Data"][
"Posts"]:
-
postsid=i[
'PostId']
-
#格式化二级url
-
url=self.second_url.format(postsid)
-
#将二级url放入二级url的队列
-
self.q2.put(url)
-
else:
-
break
-
-
# 核心函数:解析二级url,获取职位信息
-
def parse_secondurl(self):
-
# 死循环用于阻止线程阻塞
-
while
True:
-
if
not self.q2.empty():
-
url=self.q2.get()
-
html=self.get_html(url)
-
jsonlist=json.loads(html)
-
items={}
-
# 封装数据
-
items[
"RecruitPostName"] = jsonlist[
'Data'][
'RecruitPostName']
-
items[
"LocationName"] = jsonlist[
'Data'][
'LocationName']
-
items[
"Responsibility"] = jsonlist[
'Data'][
'Responsibility']
-
items[
'LastUpdateTime'] = jsonlist[
'Data'][
'LastUpdateTime']
-
print(items)
-
#加锁(防止线程争抢资源),找到一个工作总数加1
-
self.lock.acquire()
-
self.jobcount+=
1
-
self.lock.release()
-
else:
-
break
-
-
# 执行函数:程序执行的入口
-
def run(self):
-
#获取职位关键词
-
keyword=input(
"请输入搜索的职位:")
-
keyword=parse.quote(keyword)
-
#调用入队函数,把一级页面都放入队列
-
self.url_inQueue(keyword)
-
t1_lists=[]
-
t2_lists=[]
-
-
# 开启1个线程用于抓取一级页面的url
-
for i
in range(
1):
-
t=Thread(target=self.parse_indexurl())
-
t1_lists.append(t)
-
t.start()
-
-
# 开启大于1个线程用于抓取二级页面的url,缩短抓取时间
-
for i
in range(
2):
-
t=Thread(target=self.parse_secondurl())
-
t2_lists.append(t)
-
t.start()
-
-
# 阻塞线程
-
for t
in t1_lists:
-
t.join()
-
-
for t
in t2_lists:
-
t.join()
-
-
if __name__==
"__main__":
-
start_time=time.time()
-
spider=TecentJobs_spider()
-
spider.run()
-
end_time=time.time()
-
print(
"耗时:%.2f" % (end_time-start_time))
-
print(
"职位数量:",spider.jobcount)
注意:
from UserAgent import get_UserAgent : 这是我的一个反爬策略(通过交替User-Agent),代码如下
-
import random
-
-
-
agentPools=[
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36",
-
"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0"
-
]
-
-
def get_UserAgent():
-
return agentPools[random.randint(
0,
2)]
执行效果:
转载:https://blog.csdn.net/ck784101777/article/details/104391182