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

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

先从cjol来看吧。

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

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第1张图片
image

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

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第2张图片
image

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

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第3张图片
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("","") #去掉高亮显示

text = text.replace("","")

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的,我们就以此来判断是否为最后一页。

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第4张图片
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)

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

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第5张图片
image

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

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第6张图片
image

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

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


def main_spider(self):

p = Process(target=self.zhipin)

p.start()

a = Process(target=self.cjol_list)

a.start()

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

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第7张图片
image

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

这是sql结构

python爬虫学习之路,爬取招聘网站招聘信息-第四章_第8张图片
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爬虫学习之路,爬取招聘网站招聘信息-第四章)