小言_互联网的博客

Python多线程爬虫,腾讯招聘网职位爬取程序,Ajax异步数据爬取模板

354人阅读  评论(0)

目录

腾讯招聘网职位爬取程序

1.需求分析

2.URL分析

3.程序设计思路

4.设置多线程

5.程序代码 


腾讯招聘网职位爬取程序

1.需求分析

腾讯招聘网首页URL:https://careers.tencent.com/search.html

首页与大部分求职网并无太大差别,我们的目的是爬取某个岗位(如运维,设计爬虫程序时会提示输入工作名称)的所有工作岗位信息。

这些信息包括:岗位名称、发起时间、工作地点,工作职责、工作要求。

 

2.URL分析

1)分析首页URL

首先看到腾讯招聘网的首页:https://careers.tencent.com/search.html,查看源码:

啥也没有,说明所有的招聘信息都是JS嵌进去的,或者说是异步(AJax)获取的。

进入控制台,点击Network,点击XHR,找到两个文件,我们在Query开头的文件中获取到了岗位信息。

 

 URL一长串,提取出来之后是:https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1582098990287&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex=1&pageSize=10&language=zh-cn&area=cn

下面来分析这一段URL。

 在地址栏中输入这一串URL,我的格式之所以这么清晰是以为装了一个叫JSONView的插件(谷歌浏览器插件,安装教程:https://blog.csdn.net/ck784101777/article/details/104291634

下面来分析这一段URL,此URL有很多参数,我们的目的是尽量的缩短这个URL的长度(不缩短也可以),进过缩短,发现必须保留的参数有timestampkeyword、pageIndex、pageSize

 

timestamp:好理解,时间桩,进过我的测试这个时间桩写什么都无所谓,但是必须有,所以我们下面让他为1就可以了。

keyword:搜索的职位名称,为空时代表搜索全部职位,当搜索java工程师时keyword=java工程师

pageIndex:代表页数,一个URL下只有10条职位信息,页数从1开始,我们接下来也要计算某职位的页数,只有这样才能将数据抓全

pageSize:固定值,职位数量,这里是10

经过缩短,这条URL变为(其中keyword和pageIndex是变化的):

https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1&keyword=&pageIndex=1&pageSize=10

 

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的值。

https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1&keyword=&pageIndex=1&pageSize=10

#功能函数:将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.程序代码 

 


  
  1. import requests
  2. import time
  3. import json
  4. from UserAgent import get_UserAgent
  5. from urllib import parse
  6. from queue import Queue
  7. from threading import Thread,Lock
  8. class TecentJobs_spider(object):
  9. #初始化函数
  10. #定义一二级页面URL格式
  11. #定义headers
  12. #定义url队列
  13. #定义一个整数用于记录工作的个数,并赋予锁
  14. def __init__(self):
  15. self.index_url= "https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1582079472895&keyword={}&pageIndex={}&pageSize=10"
  16. self.second_url= "https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1582086419823&postId={}&language=zh-cn"
  17. self.headers={
  18. 'User-Agent':get_UserAgent(),
  19. }
  20. self.q1=Queue()
  21. self.q2=Queue()
  22. self.jobcount= 0
  23. self.lock=Lock()
  24. #功能函数:调用requests请求页面
  25. def get_html(self,url):
  26. html=requests.get(url=url,headers=self.headers).text
  27. return html
  28. #功能函数:获取工作总页数
  29. def get_pageCount(self,keyword):
  30. #keyword是工作的关键字,1代表仅请求第一页(第一页就有总的个数)
  31. url=self.index_url.format(keyword, 1)
  32. html=self.get_html(url)
  33. html=json.loads(html)
  34. count=html[ "Data"][ 'Count']
  35. pagecount= 0
  36. #pageSize=10 每页有10条工作信息,除以10获取到总的页数
  37. if count % 10 == 0:
  38. pagecount=count // 10
  39. else:
  40. pagecount=count // 10 + 1
  41. return pagecount
  42. #功能函数:将url放入队列(一级url)
  43. def url_inQueue(self,keyword):
  44. count=self.get_pageCount(keyword)
  45. for i in range( 1,count+ 1):
  46. url=self.index_url.format(keyword,i)
  47. self.q1.put(url)
  48. #核心函数:解析一级url,获取二级url并放入队列
  49. def parse_indexurl(self):
  50. #死循环用于阻止线程阻塞
  51. while True:
  52. if not self.q1.empty():
  53. url = self.q1.get()
  54. html=self.get_html(url)
  55. jsonlists=json.loads(html)
  56. for i in jsonlists[ "Data"][ "Posts"]:
  57. postsid=i[ 'PostId']
  58. #格式化二级url
  59. url=self.second_url.format(postsid)
  60. #将二级url放入二级url的队列
  61. self.q2.put(url)
  62. else:
  63. break
  64. # 核心函数:解析二级url,获取职位信息
  65. def parse_secondurl(self):
  66. # 死循环用于阻止线程阻塞
  67. while True:
  68. if not self.q2.empty():
  69. url=self.q2.get()
  70. html=self.get_html(url)
  71. jsonlist=json.loads(html)
  72. items={}
  73. # 封装数据
  74. items[ "RecruitPostName"] = jsonlist[ 'Data'][ 'RecruitPostName']
  75. items[ "LocationName"] = jsonlist[ 'Data'][ 'LocationName']
  76. items[ "Responsibility"] = jsonlist[ 'Data'][ 'Responsibility']
  77. items[ 'LastUpdateTime'] = jsonlist[ 'Data'][ 'LastUpdateTime']
  78. print(items)
  79. #加锁(防止线程争抢资源),找到一个工作总数加1
  80. self.lock.acquire()
  81. self.jobcount+= 1
  82. self.lock.release()
  83. else:
  84. break
  85. # 执行函数:程序执行的入口
  86. def run(self):
  87. #获取职位关键词
  88. keyword=input( "请输入搜索的职位:")
  89. keyword=parse.quote(keyword)
  90. #调用入队函数,把一级页面都放入队列
  91. self.url_inQueue(keyword)
  92. t1_lists=[]
  93. t2_lists=[]
  94. # 开启1个线程用于抓取一级页面的url
  95. for i in range( 1):
  96. t=Thread(target=self.parse_indexurl())
  97. t1_lists.append(t)
  98. t.start()
  99. # 开启大于1个线程用于抓取二级页面的url,缩短抓取时间
  100. for i in range( 2):
  101. t=Thread(target=self.parse_secondurl())
  102. t2_lists.append(t)
  103. t.start()
  104. # 阻塞线程
  105. for t in t1_lists:
  106. t.join()
  107. for t in t2_lists:
  108. t.join()
  109. if __name__== "__main__":
  110. start_time=time.time()
  111. spider=TecentJobs_spider()
  112. spider.run()
  113. end_time=time.time()
  114. print( "耗时:%.2f" % (end_time-start_time))
  115. print( "职位数量:",spider.jobcount)

注意:

from UserAgent import get_UserAgent : 这是我的一个反爬策略(通过交替User-Agent),代码如下


  
  1. import random
  2.  
  3.  
  4. agentPools=[
  5.     "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
  6.     "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",
  7.     "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0"
  8. ]
  9.  
  10. def get_UserAgent():
  11.     return agentPools[random.randint( 0, 2)]

 

执行效果:

 

 

 


转载:https://blog.csdn.net/ck784101777/article/details/104391182
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场