美文网首页
python爬虫学习之路,爬取招聘网站招聘信息-第四章

python爬虫学习之路,爬取招聘网站招聘信息-第四章

作者: closefrien_d1c2 | 来源:发表于2018-01-06 11:48 被阅读0次

    最近准备找工作了,但也是明年的事,先爬取一些相关的招聘的信息来看看,了解下行业行情,了解自身价值,也顺便通过招聘分析一下公司。

    先从cjol来看吧。

    看到他的翻页虽然是js执行的,但是也是能看到他也是请求了服务器

    image

    这里搜索也是一样,都是通过异步执行js请求的服务器,我们找到这个http请求就行了。

    image

    直接返回json的数据,这样更好,直接取这数据就完了

    image

    cjol的是这种形式,翻页后面加上&page=2

    
    [http://s.cjol.com/service/joblistjson.aspx?KeyWord=php&Location=2008&SearchType=3&ListType=2](http://s.cjol.com/service/joblistjson.aspx?KeyWord=php&Location=2008&SearchType=3&ListType=2)
    
    

    如果有新的要爬取的条件,直接找到地址后在列表里加上地址就行。

    这是爬取每页数据的,爬取完之后把返回的json交给cjol_list_tomysql方法处理

    
    #cjol的列表读取
    
    def cjol_list(self):
    
    url_dict = [
    
    'http://s.cjol.com/service/joblistjson.aspx?KeyWord=php&Location=2008&SearchType=3&ListType=2', #php
    
    'http://s.cjol.com/service/joblistjson.aspx?KeyWord=python&Location=2008&SearchType=3&ListType=2' #python
    
    ]
    
    for a in range(len(url_dict)):
    
    url = url_dict[a]
    
    f = request.urlopen(url)
    
    html = f.read() #取页返回json
    
    html1 = str(html, encoding = "utf-8") #转成str
    
    html2 = json.loads(html1) #转为字典
    
    print(url)
    
    self.cjol_list_tomysql(html2) #返回进来的json通过这个方法整理后写入数据库
    
    #print(down_url)
    
    #我们看到他们每页显示40个,这里如果超过40个就读取翻页
    
    if html2['RecordSum'] > 40:
    
    page_num = html2['RecordSum'] / 40
    
    page_num = int(page_num) + 2 #这里是为了让循环里面取到正确的页数,上面的除数转为整形的时候会去掉小数位
    
    for x in range(2,page_num):
    
    page_url = '%s%s%s' % (url,'&page=',x)
    
    f = request.urlopen(page_url)
    
    print(page_url)
    
    html = f.read() #取页返回json
    
    #print(html)
    
    html1 = str(html, encoding = "utf-8") #转成str
    
    html2 = json.loads(html1) #转为字典
    
    #print('down_page')
    
    self.cjol_list_tomysql(html2)
    
    time.sleep(1)
    
    time.sleep(1)
    
    

    cjol_list_tomysql这个方法就是通过xpath定位数据后,该整理的整理,我这是里把时间转换成了unix时间戳格式,因为后续想做写数据分析。unix时间戳格式方便点

    
    #输入进来爬取到的json数据,整理后写入数据库
    
    def cjol_list_tomysql(self,html_str):
    
    job_list = Selector(text=html_str['JobListHtml']).xpath('//*[@id="searchlist"]/ul').extract()
    
    #print(len(job_list))
    
    vs = MySQLHelper()
    
    for a in range(len(job_list)):
    
    text = job_list[a]
    
    text = text.replace("<strong>","") #去掉高亮显示
    
    text = text.replace("</strong>","")
    
    title = Selector(text=text).xpath('//ul/li[2]/h3/a/text()').extract()
    
    pay = Selector(text=text).xpath('//ul/li[7]/text()').extract()
    
    url = Selector(text=text).xpath('//ul[1]/li[2]/h3/a//@href').extract()
    
    area = Selector(text=text).xpath('//ul[1]/li[4]/text()').extract()
    
    page_time = Selector(text=text).xpath('//ul[1]/li[8]/text()').extract()
    
    company_url = Selector(text=text).xpath('//ul[1]/li[3]/a//@href').extract()
    
    company = Selector(text=text).xpath('//ul[1]/li[3]/a/text()').extract()
    
    try:
    
    company_id = self.company_id(company[0],company_url[0],area[0]) #具体说明看方法备注
    
    #print(company[0])
    
    except Exception as e:
    
    print('break')
    
    continue
    
    #print(text)
    
    #时间转成unix时间戳
    
    current_time = time.gmtime(int(time.time()))
    
    page_time_split = page_time[0].split('-')
    
    if page_time_split[0] != '01':
    
    curr_year = '2017'
    
    else:
    
    curr_year = current_time.tm_year
    
    page_time = '%s%s%s' % (curr_year,'-',page_time[0])
    
    page_time = int(time.mktime(time.strptime(page_time, '%Y-%m-%d')))
    
    data = {
    
    'web_type': 'cjol',
    
    'url': url[0],
    
    'title': title[0],
    
    'company_id': company_id,
    
    'pay': pay[0],
    
    'time': page_time
    
    }
    
    #SELECT list.id FROM list WHERE list.company_id = 1 AND list.title = 'ssss' AND list.time = 2222 LIMIT 0, 1
    
    query = '%s%s%s%s%s%s%s%s%s' % ('SELECT list.id FROM list WHERE list.company_id = ',company_id,' AND list.title = "',title[0],'" AND list.time = ',page_time,' AND list.url = "',url[0],'" LIMIT 0, 1')
    
    text = vs.queryAll(query)
    
    if len(text) == 1:
    
    print('list_break')
    
    else:
    
    vs.insert('list',data) #把信息写入数据库
    
    vs.commit()
    
    vs.close()
    
    

    这二个是保存公司名和地区的,没必要写入list里面,而且可能一个公司对应多条招聘,其实写入list表里面倒是没什么,量小倒是看不出来什么,量大后优化就麻烦,前期设计个好的数据库结构比后期优化好,公司和地区这块如果量大后优化的,可以设置mysql缓存,量在大用redis配合mysql做读写分离。

    这里其实还可以在公司里面加个字段,来控制爬取的列表,当我们知道这个公司其实是培训公司就把他此字段设置,就不爬取该公司招聘信息。

    
    #输入公司名称,url,和地区,如果有就返回id,如果没有就自动创建
    
    def company_id(self,company_name,company_url,area):
    
    area_id = self.area_id(area)
    
    vs = MySQLHelper()
    
    query = '%s%s%s%s%s%s%s' % ('SELECT company.id FROM company WHERE company.`area` = "',area_id,'" AND company.`name` = "',company_name,'" AND company.company_url = "',company_url,'" LIMIT 0, 1')
    
    text = vs.queryAll(query)
    
    if len(text) == 1:
    
    return text[0]['id']
    
    else:
    
    company_data = {
    
    'name': company_name,
    
    'area': area_id,
    
    'company_url': company_url
    
    }
    
    vs.insert('company',company_data)
    
    vs.commit()
    
    return_id = vs.getLastInsertId()
    
    vs.close()
    
    return return_id
    
    #输入地区名称,有就返回id,没有就自动创建并返回自增id
    
    def area_id(self,area):
    
    vs = MySQLHelper()
    
    query = '%s%s%s' % ('SELECT area.id FROM area WHERE area.`name` = "',area,'" LIMIT 0, 1;')
    
    text = vs.queryAll(query)
    
    if len(text) == 1:
    
    return text[0]['id']
    
    else:
    
    area_data = {
    
    'name': area
    
    }
    
    vs.insert('area',area_data)
    
    vs.commit()
    
    return_id = vs.getLastInsertId()
    
    vs.close()
    
    return return_id
    
    

    上面主要是cjol的list读取,然后在看看boss直聘的。他们的是全部条件直接显示在链接上了

    
    [https://www.zhipin.com/c101280600/h_101280600/?query=php&page=1&ka=page-1](https://www.zhipin.com/c101280600/h_101280600/?query=php&page=1&ka=page-1)
    
    

    看到下面翻页,我这是翻到没有了然后直接改的地址,看到第21页如果没有这一页就返回个空白,页面上肯定是没有class='page'这个div的,我们就以此来判断是否为最后一页。

    image
    
    def zhipin(self):
    
    url_dict = [
    
    'https://www.zhipin.com/c101280600/h_101280600/?query=php', #php
    
    'https://www.zhipin.com/c101280600/h_101280600/?query=python' #python
    
    ]
    
    for a in range(len(url_dict)):
    
    for x in range(1,999):
    
    page = '%s%s%s%s' % ('&page=',x,'&ka=page-',x)
    
    url = '%s%s' % (url_dict[a],page)
    
    print(url)
    
    f = request.urlopen(url)
    
    html = f.read()
    
    #检查页面page是否存在,如果不存在就跳出
    
    title = Selector(text=html).xpath('//*[@id="main"]/div[3]/div[2]/div[2]').extract()
    
    try:
    
    page = title[0]
    
    except Exception as e:
    
    break
    
    #读取当页面list
    
    page_list = Selector(text=html).xpath('//*[@id="main"]/div[3]/div[2]/ul/li').extract()
    
    vs = MySQLHelper()
    
    for b in range(len(page_list)):
    
    list_text = page_list[b]
    
    title = Selector(text=list_text).xpath('//div[1]/div[1]/h3/a/text()').extract()
    
    pay = Selector(text=list_text).xpath('//div[1]/div[1]/h3/a/span/text()').extract()
    
    url = Selector(text=list_text).xpath('//div[1]/div[1]/h3/a//@href').extract()
    
    area = Selector(text=list_text).xpath('//div[1]/div[1]/p/text()[1]').extract()
    
    company_title = Selector(text=list_text).xpath('//div[1]/div[2]/div/h3/a/text()').extract()
    
    company_url = Selector(text=list_text).xpath('//div[1]/div[2]/div/h3/a//@href').extract()
    
    page_time_text = Selector(text=list_text).xpath('//*[@class="time"]/text()').extract()
    
    #print(page_time_text)
    
    try:
    
    page_time_text = page_time_text[0]
    
    except Exception as e:
    
    page_time_text = ''
    
    current_time = time.gmtime(int(time.time()))
    
    page_time = '%s%s%s%s%s' % (current_time.tm_year,'-',current_time.tm_mon,'-',current_time.tm_mday)
    
    page_time = int(time.mktime(time.strptime(page_time, '%Y-%m-%d')))
    
    company_url = '%s%s' % ('https://www.zhipin.com',company_url[0])
    
    url = '%s%s' % ('https://www.zhipin.com',url[0])
    
    try:
    
    company_id = self.company_id(company_title[0],company_url,area[0]) #具体说明看方法备注
    
    #print(company[0])
    
    except Exception as e:
    
    print('break')
    
    continue
    
    data = {
    
    'web_type': 'zhipin',
    
    'url': url,
    
    'title': title[0],
    
    'company_id': company_id,
    
    'pay': pay[0],
    
    'time': page_time,
    
    'remark': page_time_text
    
    }
    
    #SELECT list.id FROM list WHERE list.company_id = 1 AND list.title = 'ssss' AND list.time = 2222 LIMIT 0, 1
    
    query = '%s%s%s%s%s%s%s%s%s' % ('SELECT list.id FROM list WHERE list.company_id = ',company_id,' AND list.title = "',title[0],'" AND list.time = ',page_time,' AND list.url = "',url,'" LIMIT 0, 1')
    
    #print(query)
    
    text = vs.queryAll(query)
    
    #print(len(text))
    
    if len(text) == 1:
    
    print('list_break')
    
    continue
    
    else:
    
    vs.insert('list',data) #把信息写入数据库
    
    #print(data)
    
    vs.commit()
    
    vs.close()
    
    time.sleep(1)
    
    time.sleep(1)
    
    

    这里先测试了下,运行还算顺畅

    image

    刚刚执行到第三页第六个列表的时候出了点问题,网页输出的div位置不知道怎么改变了,我就改成直接查找这个class,不是找第几个div了,这里写了验证了,不会重复插入数据库,存在就跳过,就算程序出问题也不用担心。

    image

    然后主要执行方法,二个线程同时执行,后续如果在有更多网站在写规则

    原理上就是先查看要爬取数据的规则,如果有些网站需要登录或者是会判断header的就要做些额外的处理。

    
    def main_spider(self):
    
    p = Process(target=self.zhipin)
    
    p.start()
    
    a = Process(target=self.cjol_list)
    
    a.start()
    
    

    二个网站同时爬取,有相同的则跳过。

    image

    每天定时执行一次就行了。

    这是sql结构

    image
    
    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    
    -- Table structure for area
    
    -- ----------------------------
    
    DROP TABLE IF EXISTS `area`;
    
    CREATE TABLE `area` (
    
    `id` int(11) NOT NULL AUTO_INCREMENT,
    
    `name` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    PRIMARY KEY (`id`)
    
    ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    -- ----------------------------
    
    -- Table structure for company
    
    -- ----------------------------
    
    DROP TABLE IF EXISTS `company`;
    
    CREATE TABLE `company` (
    
    `id` int(11) NOT NULL AUTO_INCREMENT,
    
    `name` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    `area` int(11) DEFAULT NULL,
    
    `company_url` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    PRIMARY KEY (`id`)
    
    ) ENGINE=InnoDB AUTO_INCREMENT=601 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    -- ----------------------------
    
    -- Table structure for list
    
    -- ----------------------------
    
    DROP TABLE IF EXISTS `list`;
    
    CREATE TABLE `list` (
    
    `id` int(11) NOT NULL AUTO_INCREMENT,
    
    `web_type` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    `url` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    `title` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    `company_id` int(11) DEFAULT NULL,
    
    `pay` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    `time` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    `status` tinyint(2) DEFAULT NULL,
    
    `remark` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
    
    PRIMARY KEY (`id`)
    
    ) ENGINE=InnoDB AUTO_INCREMENT=1949 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    

    依赖包

    
    aniso8601==1.3.0
    
    api==0.0.7
    
    asn1crypto==0.23.0
    
    attrs==17.3.0
    
    Automat==0.6.0
    
    beautifulsoup4==4.5.3
    
    certifi==2017.11.5
    
    cffi==1.11.2
    
    chardet==3.0.4
    
    click==6.7
    
    constantly==15.1.0
    
    cryptography==2.1.4
    
    cssselect==1.0.1
    
    Django==2.0
    
    django-blog-zinnia==0.19
    
    django-contrib-comments==1.8.0
    
    django-js-asset==0.1.1
    
    django-mptt==0.9.0
    
    django-tagging==0.4.6
    
    django-xmlrpc==0.1.8
    
    Flask==0.12.2
    
    Flask-RESTful==0.3.6
    
    html5lib==0.999999999
    
    hyperlink==17.3.1
    
    idna==2.6
    
    incremental==17.5.0
    
    itsdangerous==0.24
    
    Jinja2==2.10
    
    lxml==3.7.1
    
    Markdown==2.6.10
    
    MarkupSafe==1.0
    
    mots-vides==2015.5.11
    
    mysqlclient==1.3.12
    
    nose==1.3.7
    
    olefile==0.44
    
    parsel==1.2.0
    
    Pillow==4.3.0
    
    pyasn1==0.4.2
    
    pyasn1-modules==0.2.1
    
    pycparser==2.18
    
    pycryptodome==3.4.7
    
    PyDispatcher==2.0.5
    
    Pygments==2.2.0
    
    PyMySQL==0.7.11
    
    pyOpenSSL==17.5.0
    
    pyparsing==2.2.0
    
    python-apt==1.1.0b5
    
    python-dateutil==2.6.1
    
    pytz==2017.3
    
    queuelib==1.4.2
    
    redis==2.10.6
    
    regex==2017.12.12
    
    requests==2.18.4
    
    RPi.GPIO==0.6.3
    
    Scrapy==1.4.0
    
    selenium==3.8.0
    
    service-identity==17.0.0
    
    six==1.11.0
    
    Twisted==17.9.0
    
    ufw==0.35
    
    urllib3==1.22
    
    virtualenv==15.1.0
    
    w3lib==1.18.0
    
    webencodings==0.5
    
    Werkzeug==0.12.2
    
    zope.interface==4.4.3
    
    

    setup有点懒了,就没写,把依赖包安装,数据库执行job.sql就行了。

    看到代码烦的童鞋直接git吧

    https://github.com/vtesoho/job_spider

    上面的代码由于是从有道里面复制过来,tab格式出了问题,直接上github看吧。

    相关文章

      网友评论

          本文标题:python爬虫学习之路,爬取招聘网站招聘信息-第四章

          本文链接:https://www.haomeiwen.com/subject/evqynxtx.html