python爬取岗位数据并分析_爬取拉勾数据分析岗位

拉勾的反爬机制做得特别残暴。

javascript加密和直接访问json数据会给你返回伪装的数据不说。最残暴也是最简单的,限制短时间内的多次访问。只要爬虫速度稍快点,就会要你进行验证。还有未登陆状态下,每访问10个页面,就会要求你登录。不管你是用爬虫还是正常访问。

一般是有两种爬取思路。一种是requests,一种是selenium。

requests逆向

requests需要逆向。逆推源网址的构造方法。 拉勾的逆向比较简单,从XHR文件中可以看到,在请求搜索的时候传递了三个数据:first,pn(页数),kd(搜索关键词)。所以可以在请求的时候带上这三个数据,获取json中的数据。 62d0ea88fb4ed760afe1c220946c6b2b.png 不过还有一个问题,获取的json数据构造有点特殊。拉勾这里又藏了一手,导致无法获取json字典中的数据,目前还没解析出来,所以这次我们用的是另外一种方法。

selenium

selenium相比于要去逆向网站这种方法来的简单,主要就是模仿浏览器的行为,无需进行逆向等操作。 在用selenium进行爬取的时候会遇到以下几个问题: 1.新打开的职业搜索列表首页会有一个红包弹窗,可以用显示等待相关元素加载出来后点击退出。 2.拉勾职业列表最多显示30页(有的城市可能不足30页),每页15条数据。所以需要判断是否爬取到最后一页。 3.为了防止每打开10个页面拉勾跳出登录界面,需要将爬取到的链接分为10个一组。

4.因为爬取的时候,selenium会不断的调用浏览器,所以没有添加'--headless'的话,在爬取的时候你就无法使用你的电脑,因为浏览器会不断的弹出来。

源码如下 :
"""@Author: Newyee@Python: 3.6.5@selenium: 3.141.0@Chrome: 72.0.3626.81"""# 导入相关模块(未安装可执行 pip install xxx 命令安装)from selenium import webdriverfrom lxml import etreeimport randomimport timefrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byimport csvfrom selenium.webdriver import ChromeOptions# 创建类class LagouSpider():    def __init__(self):        option = ChromeOptions()        option.add_argument('--headless')        # 初始化类实例时打开谷歌浏览器(可查看测试过程)        self.driver = webdriver.Chrome(options=option)        self.driver.set_window_size(1920, 1080)        # 搜索页面的url        self.url = "https://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/p-city_6?px=default#filterBox"        # 存放所有职位详情页的url        self.all_links = []    def run2(self, ten_links):        '''        每次对10个职位详情url请求并解析,保存职位详细信息,退出浏览器        :param ten_links: 10个职位详情页url组成的list        :return:        '''        # 遍历每个detail_url        for link in ten_links:            # 调用request_detail_page请求并解析            self.request_detail_page(link)            # 随机间隔3-6s,避免反爬            time.sleep(random.randint(5, 9))        # 获取10个职位信息后退出浏览器        self.driver.quit()    def run1(self):        '''        打开搜索页面,并循环翻页至最后一页,解析html获得all_detail_links        :return:        '''        # 在当前打开的浏览器中加载页面        self.driver.get(self.url)        time.sleep(10)        #点击弹出的广告按钮        button = self.driver.find_element_by_class_name('body-btn')        button.click()        # 用于记录当前是第几页        count_page = 1        # 循环翻页直到最后一页        while True:            # 获取当前页的网页源代码            source = self.driver.page_source            # 利用xpath解析source获得detail_links并保存到            self.get_all_detail_links(source)            print('Fetched page %s.' % str(count_page))            # 找到【下一页】按钮所在的节点            next_btn = self.driver.find_element_by_xpath('//div[@]/span[last()]')            # 判断【下一页】按钮是否可用            if "pager_next_disabled" in next_btn.get_attribute("class"):                # 【下一页】按钮不可用时即达到末页,退出浏览器                self.driver.quit()                # 返回所有职位详情页url列表(去重后的)                return list(set(self.all_links))            else:                # 【下一页】按钮可用则点击翻页                next_btn.click()                count_page += 1                time.sleep(random.randint(2, 4))            time.sleep(random.randint(3, 5))    def get_all_detail_links(self, source):        '''        利用xpath解析source获得detail_links并保存到self.all_links        :param source: 网页源代码html        :return:        '''        html = etree.HTML(source)        links = html.xpath('//a[@]/@href')        self.all_links += links    def request_detail_page(self, url):        '''        请求职位详情页面,并调用parse_detail_page函数        :param url: 职位详情页url        :return:        这部分不建议修改        '''        # 在当前窗口中同步执行javascript        self.driver.execute_script("window.open('%s')" % url)        # 执行后打开新页面(句柄追加一个新元素)        # driver.switch_to.window:将焦点切换到指定的窗口        # driver.window_handles:返回当前会话中所有窗口的句柄        self.driver.switch_to.window(self.driver.window_handles[1])  # 切换到新打开的窗口,即第2个--index==1        source = self.driver.page_source        self.parse_detail_page(source)        self.driver.close()        self.driver.switch_to.window(self.driver.window_handles[0])  # 切换到主窗口(否则不能再次打开新窗口)    def parse_detail_page(self, source):        '''        解析详情页,用xpath提取出需要保存的职位详情信息并保存        :param source: 职位详情页的网页源代码html        :return:        '''        # 将source传入lxml.etree.HTML()解析得到etree.HTML文档        html = etree.HTML(source)        # 对html用xpath语法找到职位名称所在节点的文本,即position_name        position_name = html.xpath("//h1[@class='name']/text()")[0]        # 对html用xpath语法找到职位id所在的节点,提取获得position_id        #position_id = html.xpath("//link[@rel='canonical']/@href")[0].split('/')[-1].replace('.html', '')        # 找到职位标签,依次获取:薪资、城市、年限、受教育程度、全职or兼职        job_request_spans = html.xpath('//dd[@]//span')        salary = job_request_spans[0].xpath('.//text()')[0].strip()         # 列表索引0==xpath第1个节点        city = job_request_spans[1].xpath('.//text()')[0].strip().replace("/", "").strip()        work_year = job_request_spans[2].xpath('.//text()')[0].strip("/").strip()        education = job_request_spans[3].xpath('.//text()')[0].strip("/").strip()        work_full = job_request_spans[4].xpath('.//text()')[0]        # 找到公司标签,获取company_short_name        company_short_name = html.xpath('//dl[@]//em/text()')[0].replace("\n", "").strip()        # 找到公司标签中的industry_field和finance_stage、scale规模        company_infos = html.xpath('//dl[@]//li')   # 注意该节点下的text()索引0和2是空的        industry_field = company_infos[0].xpath('.//h4[@]/text()')[0]        finance_stage = company_infos[1].xpath('.//h4[@]/text()')[0]        scale = company_infos[2].xpath('.//h4[@]/text()')[0]        # 找到工作地址所在的区        district = html.xpath('//div[@]/a[2]/text()')[0].strip()        # 找到职位诱惑,获取position_advantage        position_advantage = html.xpath('//dd[@]//p/text()')[0].strip("/").strip().replace(",", ",")        # 职位描述        job_des = html.xpath('//div[@]/p/text()')        # 以追加的方式写入csv文建        with open('test.csv', 'a', encoding='utf-8', newline='') as f:            writer = csv.writer(f)            writer.writerow([position_name, salary, city, work_year, education, work_full, company_short_name, industry_field, finance_stage, scale, district, position_advantage, job_des])if __name__ == "__main__":    # 记录项目开始时间    start_time = time.time()    # 实例化LagouSpider类,调用run1方法获取所有职位详情页的url    needed_all_links = LagouSpider().run1()    # 将所有职位详情url以10位单位拆分成嵌套列表    nested_all_links = [needed_all_links[i:i + 10] for i in range(0, len(needed_all_links), 10)]    count = 10    # 连续请求10个详情页就会弹出登录页,故每请求10个重启一次浏览器    for ten_links in nested_all_links:        # 每10个为一组,打开一次浏览器,调用run2方法保存职位详细信息        LagouSpider().run2(ten_links)        # count计数调整间隔时间,请求过多弹出登录        time.sleep(random.randint(6, 12) * (count // 100 + 1))        count += 10        print('-------------------------')        print('Have fetched %s positions.' %str(count))    # 记录项目结束时间    end_time = time.time()    print('\n【项目完成】\n【总共耗时:%.2f分钟】' %((end_time - start_time) / 60))
简单一点的改善的话,可以将城市代号作为参数写入函数中,循环爬取。再进一步的话,就是做多线程,提高爬取速度。 参考:

1.拉勾网Python爬虫:Selenium+Xpath 反反爬、免登陆获取全部职位详情

2.python爬虫:爬取拉勾网职位并分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值