最近准备找工作了,但也是明年的事,先爬取一些相关的招聘的信息来看看,了解下行业行情,了解自身价值,也顺便通过招聘分析一下公司。
先从cjol来看吧。
看到他的翻页虽然是js执行的,但是也是能看到他也是请求了服务器
image这里搜索也是一样,都是通过异步执行js请求的服务器,我们找到这个http请求就行了。
image直接返回json的数据,这样更好,直接取这数据就完了
imagecjol的是这种形式,翻页后面加上&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看吧。
网友评论