2

概要

最近学起了python爬虫,感觉入门挺容易的,甚至还有点好玩,于是试了下用来爬取拉勾网以练练手(其实也试过boss直聘的,由于boos的防爬太强,就放弃了。。)。
主要功能是,在特定的筛选条件遍历和获取每个工作职位信息并保存到MongoDB中。

环境

python的依赖包使用pipenv安装

  • Python 3.7.5
  • selenium 3.141
  • Chrome 79
  • MongoDB 4.2

实例代码

from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver
from lxml import etree
import pymongo
import random
import time
import re


class WantYou:
    def __init__(self):
        self.mongo = pymongo.MongoClient('mongodb://localhost:27017/')
        self.mongo_db = self.mongo['lagou']
        self.use_chrome_browser()

    def use_chrome_browser(self):
        driver_path = r'D:\APP\chromedriver\chromedriver.exe'
        self.browser = webdriver.Chrome(executable_path=driver_path)

    def start(self):
        """
        主体流程,包括:设置筛选条件、点击跳转详情、翻页等
        :return:
        """
        # 第2条url自带query参数可以免去1、2、3步设置过滤条件操作,这里因为练习需要使用简版url
        list_url = 'https://www.lagou.com/zhaopin/Python/?labelWords=label'
        # list_url = 'https://www.lagou.com/jobs/list_Python/p-city_213-gm_6?px=default&yx=50k%E4%BB%A5%E4%B8%8A#filterBox'
        self.browser.get(list_url)
        element = WebDriverWait(self.browser, 10).until(
            EC.visibility_of_element_located((By.CLASS_NAME, 'salary'))
        )
        print('salary text:', element.text)
        # 如果出现红包弹层,那关闭它
        time.sleep(0.5)
        if self.is_element_exist(By.CSS_SELECTOR, '.body-container .body-btn'):
            self.browser.find_element_by_css_selector('.body-container .body-btn').click()
            print('关闭红包弹层')

        # 设置过滤条件,如果不需要请注释掉
        self.filter()

        page_count = 0
        job_count = 0
        while True:
            page_count += 1
            # 处理并记录当前列表信息到数据库
            self.parse_list_page(self.browser.page_source)
            # 遍历打开第一个工作的详情面,新标签跳转,h3标签即是职位名,点击可跳转
            for i in self.browser.find_elements_by_css_selector('h3'):
                job_count += 1
                print('当前:(%s)页,(%s)条:[%s]' % (page_count, job_count, i.text), '》' * 10)
                # 点击详情链接,会自动跳转到新标签打开页面
                i.click()
                # 切换新标签为当前标签
                self.browser.switch_to.window(self.browser.window_handles[1])
                WebDriverWait(self.browser, 10).until(
                    EC.visibility_of_element_located((By.CLASS_NAME, 'job-detail'))
                )
                # 处理并记录详情页的信息,补充添加到数据库
                self.parse_detail_page(self.browser.page_source)
                # 关闭新标签
                self.browser.close()
                # 切换当前标签为列表页所在的标签
                self.browser.switch_to.window(self.browser.window_handles[0])

            # 获取下一页按钮,当可用时,点击跳转下一页
            if not self.is_element_exist(By.CSS_SELECTOR, '.pager_next.pager_next_disabled'):
                self.browser.find_element_by_css_selector('.pager_next').click()
                WebDriverWait(self.browser, 10).until(
                    EC.visibility_of_element_located((By.CLASS_NAME, 'salary'))
                )
                time.sleep(1)
                print('next page')
            else:
                break

        print('顺利完成,共(%s)页(%s)条' % (page_count, job_count))

    def filter(self):
        """
        设置过滤条件
        :return:
        """
        # 1.设置城市筛选条件
        if self.is_element_exist(By.CSS_SELECTOR, '.city-wrapper [data-id="763"]'):
            # 因为所处的网络位置问题,有时候默认就是广州,因此只有默认非广州时才设置本条件
            time.sleep(random.uniform(0.5, 1.5))
            # 763是广州,765是深圳
            self.browser.find_element_by_css_selector('.city-wrapper [data-id="763"]').click()
            WebDriverWait(self.browser, 10).until(
                # 上一步会造成页面刷新,这一步主要是等待页面加载完成,检测的是哪个元素不重要
                EC.visibility_of_element_located((By.CLASS_NAME, 'salary'))
            )
            print('城市条件完成')

        # 2.设置薪酬筛选条件
        time.sleep(random.uniform(0.5, 1.5))
        # 展开下拉框
        self.browser.find_element_by_css_selector('.salary .text').click()
        # 选择指定的条件,这里选最后一个
        self.browser.find_element_by_css_selector('.salary li:last-of-type > a').click()
        WebDriverWait(self.browser, 10).until(
            EC.visibility_of_element_located((By.CLASS_NAME, 'salary'))
        )
        print('薪酬条件完成')

        # 3.设置公司规模筛选条件
        time.sleep(random.uniform(0.5, 1.5))
        self.browser.find_element_by_css_selector('li.multi-chosen:nth-of-type(4) > a:last-of-type').click()
        WebDriverWait(self.browser, 10).until(
            EC.visibility_of_element_located((By.CLASS_NAME, 'salary'))
        )
        print('规模条件完成')

    def parse_list_page(self, page_source):
        """
        :param page_source: 列表页的html字串
        :return:
        """
        page = etree.HTML(page_source)
        jobs = page.xpath('//li[contains(@class,"con_list_item")]')
        jobs_info = []
        for i in jobs:
            item = {
                'job_id': i.xpath('./@data-positionid')[0],
                'company_id': i.xpath('./@data-companyid')[0],
                'company_name': i.xpath('./@data-company')[0],
                'name': i.xpath('./@data-positionname')[0],
                'salary': i.xpath('./@data-salary')[0],
                # 'company_name': i.xpath('string(.//div[@class="company_name"]//a)'),
                # 'name': i.xpath('string(.//h3)'),
                # 'salary': i.xpath('string(.//span[@class="money"])'),
                'district': i.xpath('string(//span[@class="add"])').strip('[]'),
                'industry': i.xpath('string(.//div[@class="industry"])').strip(),
                'logo': i.xpath('.//div[@class="com_logo"]//img/@src')[0],
                'link': i.xpath('.//a[@class="position_link"]/@href')[0],
                'tags': i.xpath('.//div[@class="list_item_bot"]//span/text()'),
                'advantage': i.xpath('.//div[@class="list_item_bot"]/div[last()]/text()')[0].strip('“”'),
                'post_at': i.xpath('.//span[@class="format-time"]/text()')[0],
            }
            exp, degree = ''.join(i.xpath('.//div[@class="p_bot"]/div/text()')).strip().split(' / ')

            item['exp'] = exp
            item['degree'] = degree
            jobs_info.append(item)

        print('列表页获得数据:', jobs_info)
        # 将获取的每一条招聘信息,保存到mongo
        self.mongo_db['jobs'].insert_many(jobs_info)

    def parse_detail_page(self, page_source):
        """
        :param page_source: 详情页的html字串
        :return:
        """
        # 保存详情页信息补充增加到mongo
        page = etree.HTML(page_source)
        job_detail = page.xpath('//dl[@id="job_detail"]')[0]
        # print(job_detail)
        item = {
            'description': job_detail.xpath('string(.//div[@class="job-detail"])'),
            'address': re.sub(r'[\s|查看地图]', '', job_detail.xpath('string(.//div[@class="work_addr"])')),
            'position_lng': job_detail.xpath('.//input[@name="positionLng"]/@value')[0],
            'position_lat': job_detail.xpath('.//input[@name="positionLat"]/@value')[0],
        }
        job_id = page.xpath('.//input[@id="jobid"]/@value')[0]
        print('详情页获得数据:', item)
        self.mongo_db['jobs'].update_one({'job_id': job_id}, {'$set': item})

    def is_element_exist(self, by, value):
        """
        用来判断标签元素是否存在
        :param by: 条件类型
        :param value: 条件内容
        :return:
        """
        try:
            self.browser.find_element(by=by, value=value)

        except NoSuchElementException as e:
            # 发生了NoSuchElementException异常,说明页面中未找到该元素,返回False
            return False
        else:
            return True


if __name__ == '__main__':
    WantYou().start()

获得的数据

mongodb_lagou.png

最后

以上内容是偏基础的练习性操作,可能并非最优解,如有补充,欢迎留言。


Jeffid
214 声望10 粉丝

新世界的开发者;