最近在找工作,发现每天智联上招聘iOS的并不多,于是我就很好奇,是我投递的位置不对嘛?于是本着好奇心,于是决定爬取一下智联上相关的信息。同时也是第一次使用Scrapy,来这里也算是记录一下学习经过。
github地址:https://github.com/hzj7510/Zhilian_Spider
言归正传,开始我们的表演。
这里关与Scrapy、numpy、matplotlib的安装就不介绍了,可以去网上查一下。
第一步,我们来创建一个Scrapy项目
我们来看一下Scrapy文档
https://doc.scrapy.org/en/1.3/intro/tutorial.html#how-to-run-our-spider
OK,搞起。
bogon:Scrapy mac$ cd /.../文件夹/ #项目存放的文件夹
#项目名称为zhilian_job
bogon:Scrapy mac$ scrapy startproject zhilian_job
Paste_Image.png
如果你看到这些那么就已经成功了,是不是很简单。
第二步,我们创建一个Spider
我们进入到zhilian_job文件夹下的spider文件夹下。
#进入到spider文件夹
bogon:Scrapy mac$ cd zhilian_job/
bogon:zhilian_job mac$ cd zhilian_job/
bogon:zhilian_job mac$ cd spiders/
Paste_Image.png
我们会发现这里只有一个init.py文件
#创建spider文件
bogon:spiders mac$ vim zhilian_spider.py
这里需要说一下,一个spider类的几个关键因素
第一,需要继承自scrapy.Spider
第二,我们需要给他一个name 这个name当我们执行scrapy crawl的时候会用到
第三,我们需要给他一个start_urls,这是一个列表,也就意味着我们可以有多个start_urls
第四,需要有一个参数是response的parse方法,其实这个方法名字可改变,这个后面在说。
大概是这个样子。
1 import scrapy
2
3 class ZhilianSpider(scrapy.Spider):
#排从需要的名字
4 name = 'zhilian_spider'
#开始的url
5 start_urls = [
6 ''
7 ]
8
9 def parse(self,response):
10 print (response)
Paste_Image.png
第三步,爬取的网页
接下来我们来看一下我们要爬取的网站。
我们可以看到城市智联有个更多,因为要获取所有城市的iOS的招聘数量,所以我们需要先爬取这个网http://www.zhaopin.com/citymap.html
我们将这个网址放到我们的start_urls中。
5 start_urls = [
6 'http://www.zhaopin.com/citymap.html'
7 ]
Paste_Image.png
这样我们的第一个Scrapy就算是写完了,这就完了?这才是开始,不要着急,我们可以先跑起来看看,给自己一些继续的动力。
我们先回到第一个子目录也就是包含scrapy.cfg这个文件夹下。
bogon:zhilian_job mac$ ls
__init__.py middlewares.py settings.py
items.py pipelines.py spiders
bogon:zhilian_job mac$ cd ..
bogon:zhilian_job mac$ ls
scrapy.cfg zhilian_job
Paste_Image.png
然后我们执行 scrapy crawl zhilian_spider
zhilian_spider ? 好像在哪见过,是的,在我们ZhilianSpider类中的name 之前也有提过
bogon:zhilian_job mac$ scrapy crawl zhilian_spider
Paste_Image.png
然后我们发现出来一大推东西,不要方,我们来找一下我们答应的response,额。。。 有点难找,我们来加点标记
bogon:zhilian_job mac$ vi zhilian_job/spiders/zhilian_spider.py
9 def parse(self,response):
10 print ('--------------')
11 print (response)
Paste_Image.png
再来运行一下
bogon:zhilian_job mac$ scrapy crawl zhilian_spider
Paste_Image.png
OK~看到这个就放心了。
第四步,解析数据
根据文档,我们可以使用xpath 或者 css来解析,其实貌似还可以使用BeautifulSoup,文档中极力推荐使用xpath,于是我们来用一下xpath。
https://doc.scrapy.org/en/1.3/topics/selectors.html#using-selectors
可以来这里学习一下。
然后在看一下结构。 Paste_Image.png
Paste_Image.png
可以看到这是一个list嵌套一个list
OK,让我们来解析一下数据。
回到我们的spider中
bogon:zhilian_job mac$ vi zhilian_job/spiders/zhilian_spider.py
9 def parse(self,response):
10 for citys in response.xpath('//div[@id="letter_choose"]/dl/dd'):
11 for city in citys.xpath('.//a'):
#因为有可能有加粗的情况所以需要加个判断
12 if city.xpath('.//strong').extract_first() is not None:
13 city = city.xpath('.//strong')
14 ct = city.xpath('.//text()').extract_first()
15 print (ct)
Paste_Image.png
如果你觉着xpath不好写,这里有方法,是我查资料是不经意间发现的。
比如想知道id = letter_choose这个标签,又不想写他的xpath,我们可以使用右键然后copy Xpath来获取
Paste_Image.png
OK,解析弄完之后我们看一下我们有没有获取到结果
回到包含scrapy.cfg的文件夹下执行
bogon:zhilian_job mac$ scrapy crawl zhilian_spider
查看结果,发现确实打印了,我们需要的城市。
Paste_Image.png
我们离成功更近一步了。
接下来第五步,我们获取城市还没完,我们还要查看招聘信息。
Paste_Image.png这之前需要说一个问题,就是之前说的parse这个方法的问题。其实我们看文档会发现,我们本可以不使用parse这个方法。
这是文档中的一段,我们不难发现这里调用了
scrapy.Request(url = url ,callback=self.parse)
第一个参数就是我们想要请求的url。
第二个参数是我们需要回调的方法。
通过这个方法我们可以做什么呢?我们来看一下搜索济南iOS的url是什么。
http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%E6%B5%8E%E5%8D%97&kw=ios&sm=0&p=1
这里需要四个参数
jl 城市名称
kw 搜索关键字
sm = 0
p page第几页,如果搜索结果又多页可修改此参数
所以我们只需要修改城市就可以获取到每个城市的iOS的招聘情况。
让我们来试一下。回到spider中。
vi zhilian_job/spiders/zhilian_spider.py
先修改一下parse方法,方法中这里我们也用到了scrapy.Request方法,然后我们在添加一个爬取每个城市的parse方法
9 def parse(self,response):
10 for citys in response.xpath('//div[@id="letter_choose"]/dl/dd'):
11 for city in citys.xpath('.//a'):
12 if city.xpath('.//strong').extract_first() is not None:
13 city = city.xpath('.//strong')
14 ct = city.xpath('.//text()').extract_first()
15 url_city = 'http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%s& kw=ios&sm=0&p=1' % ct
16 yield scrapy.Request(url=url_city, callback=self.parse_jobs)
18 def parse_jobs(self,response):
19 pass
Paste_Image.png
跑一下结果.
bogon:zhilian_job mac$ scrapy crawl zhilian_spider
这里我终止了爬取,因为我们可以看到parse_job方法已经执行了,成功了一半了!
Paste_Image.png
第六步,我们来解析一下搜索结果的网页
Paste_Image.png这里面就是我们需要的东西,放在<tr>中,放在<table>里,很明显是一个list。
继续更新一下parse_jobs方法。
#导入头文件
2 from zhilian_job.items import ZhilianJobItem
#修改parse方法
19 def parse_jobs(self,response):
20 job_position = response.xpath('//*[@id="JobLocation"]/@value').extract_first() 20
for job in response.xpath('//table[@class="newlist"]/tr'):
21 kword = job.xpath('.//td[@class="zwmc"]/div/a/b/text()').extract_first()
22 if kword is not None:
23 item = ZhilianItem()
24 item["job_company"] = job.xpath('.//td[@class="gsmc"]/a/text()').extract_first()
25 item["job_price"] = job.xpath('.//td[@class="zwyx"]/text()') .extract_first()
26 item["job_date"] = job.xpath('.//td[@class="gxsj"]/span/text ()').extract_first()
27 item["job_name"] = job.xpath('.//td[@class="zwmc"]/div/a/tex t()').extract_first()
28 item["job_position"] = job_position
29 item["job_is"] = 1
30 yield item
Paste_Image.png
关于item的使用还请各位看一下文档,不是很难。
https://doc.scrapy.org/en/1.3/topics/items.html
这里贴一下我的items.py
8 from scrapy.item import Item,Field
9
11 class ZhilianJobItem(Item):
12 job_name = Field()
13 job_company = Field()
14 job_price = Field()
15 job_position = Field()
16 job_date = Field()
17 job_is = Field()
Paste_Image.png
运行一下:
scrapy crawl zhilian_spider
Paste_Image.png
这里我中断了,因为已经看到了想要的数据。
第七步 处理下一页问题
如果仔细分析,不难发现,我们只获取到了每个城市的第一页的数据,这很尴尬,如果有下一页怎么办,我们来处理一下。
先看一下网页,找到下一页
Paste_Image.png
让我们回答parse_jobs方法
#后去下一页的网址
33 job_next = response.xpath('//a[@class="next-page"]/@href').extract_first()
#打印一下结果,因为之前并不知道job_next是什么值,所以打印一下看一下结果,发现如果没有就是None
34 print ("********",job_next)
#如果有下一页就爬取下一页
35 if job_next is not None:
#发送网络请求,并callback自己
36 yield scrapy.Request(url=job_next, callback=self.parse_jobs)
Paste_Image.png
关键在yield那句 callback我们回调的还是parse_jobs()方法
Ok,这样我们可以看一下结果。
scrapy crawl zhilian_spider
Paste_Image.png
没有问题。
那我们如何保存成文件呢。
只需要一句话
bogon:zhilian_job mac$ scrapy crawl zhilian_spider -o items.json
Paste_Image.png
OK,到这第一部分就完成了。
补充,当你点开json文件,你会发现怎么全是unicode编码,说句实话 ,我也不知道怎么处理,使用item就是看到可以通过item解决,自己试了一下,并没有成功,但是这不影响后面的操作,如果有知道处理方式的可以共享一下。
下面我们会处理一下数据然后显示出来,敬请期待。
-----------------------有待更新-------------------------
OK,让我们继续下面的表演。
经过了大概半个小时,我终于看到了这个界面。恭喜你,你成功了,恭喜你,你被我坑了,哈哈,为什么这么说呢,下面给你解释。
Paste_Image.png可以看到item_scraped_count是326153,也就是说你有一个32W+的一个数据文件了。
我们的目的是将其显示成图,哇,要显示这么多嘛?不是的,我们还需要处理,于是我们就需要使用pandas,做图必然要使用matplotlib。
这里我们只需要使用ipython就Ok了.
In [1]: import pandas as pd
#注意这里我用用到的是pyplot 这里一定要引入matplotlib,后面会解释
In [2]: import matplotlib.pyplot as plt
In [3]: import numpy as np
In [4]: items = pd.read_json('items.json')
#回车后你会发现有好多数据
In [5]: items
Paste_Image.png
当时当我看到全国两个字的时候,我方了,为什么会有全国呢,我们不需要啊,而且你看到后面你会发现,我们估计花儿一半多时间才将全国爬取完成。也对不住各位坑你们了,让你们花了这么长时间,不过我感觉看着屏幕一直刷的感觉,像是在看骇客帝国,哈哈。其次,这样才有意思不是嘛?各种突发情况才更有趣。
Paste_Image.png下面再说一下我们的需求
我们需要获取到每个城市的平均简历工资,我们需要获取到每个城市的招聘数量。
下面开始我们的数据处理的第一步,去重
接下来我们要做的就是去重,依然使用pandas,我是在这篇文章中看到的。https://zhuanlan.zhihu.com/p/25473751?group_id=819874677425053696
Paste_Image.pngOk来执行以下
In [7]: items.drop_duplicates(['job_name','job_position'],keep='last',inplace=Tr
...: ue)
In [8]: items
神奇的事情发生了,32W + 变成了4k+,看到这里,我貌似明白了什么。
Paste_Image.png我们不难发现在处理工资这里的时候会有一些问题,有的是面议,其次就是xxxx - xxxx这样肯定算不出平均工资吧,我们还要处理一下。
第二步,数据再次处理
当我纠结怎么办的时候,我突然发现文档里有一个叫做item pipeline的东西,貌似可以在里面处理再次处理数据。
我们在我们的Scrapy项目中找一下,发现在items.py同级目录下,有一个pipelines.py,既然加s说明可以有多个pipeline。
进去看一下,建议不要关闭ipython,重新打开一个终端。
#导入re是因为发现工资是以字符串返回的,同事我们需要匹配格式为xxx-xxx的工资
8 import re
9 from scrapy.exceptions import DropItem
10
11 class ZhilianJobPipeline(object):
12 def process_item(self, item, spider):
13 price = item["job_price"]
14 ma = re.match(r'\d+-\d+',price)
15 if ma is not None:
#通过字符串的split方法将字符串分割获取得到最低与最高工资
16 prices = price.split('-')
#这里需要转换一下,转换成int类型,然后算一下平均值
17 price = (int(prices[0]) + int(prices[1])) / 2
18 item['job_price'] = price
#这样我们就获取到了每个招聘信息的平均价格。
19 return item
20 else:
21 raise DropItem("Missing price in %s" % item)
Paste_Image.png
这里还需要配置一个地方,我们需要设置setting.py里的ITEM_PIPELINE这个地方。文档最下面可以看到。因为我们这只有一个,所以我们只需要打开注释就好。
Paste_Image.png Paste_Image.png
这样就ok,保存一下,我们再来执行一下crawl zhilian_spider吧,不是吧,又要30分钟,是的,我当时就这样做的。想想也很无奈,不过第一次也是难免的。
开始前最好将之前的items.json删除。
Sheldon:zhilian_job mac$ rm -rf items.json
Sheldon:zhilian_job mac$ scrapy crawl zhilian_spider -o items.json
Paste_Image.png
没问题,这正是我们想要的。
第三步,数据整理
回到第一步使用ipython对items进行去重,这里就不重复了。
因为我们需要的是每个地区的所有招聘数量与平均工资,所以我们还需要对每个地区进行处理,还好在https://zhuanlan.zhihu.com/p/25473751?group_id=819874677425053696
这篇文章中我发现了一个叫做groupby的方法,貌似可以拿来用一下。
我们需要对position列进行分组,然后获取price的平均价格
这里是获取平均值的方法,http://www.aichengxu.com/python/16058.htm
同时我们还需要显示个地区的所有招聘数量,这个可以使用count()方法。
Paste_Image.png
In [13]: job_price = items.groupby('job_position')['job_price'].mean()
In [14]: job_num = items.groupby('job_position')['job_price'].count()
In [15]: job_num
Paste_Image.png
数据我们算是初步处理完成,下面就是显示问题了,这里遇到一个坑,花了半天时间,真的是半天。
去看文档,上面写绘制图需要用到plot方法
http://pandas.pydata.org/pandas-docs/stable/visualization.html
然后我就试了一下
Paste_Image.png然后呢?我没看到图啊 ,文档输入完plot()就显示图了,为什么我没有?
然后找了半天资料,发现需要执行一下plt.show()这也是为什么一开始带入头文件时说一定要有plt。
In [17]: plt.show()
Paste_Image.png
看到这张图的时候差点泪崩,文档有时候也骗人。不过仔细一看,就真的泪崩了,这是些啥?搞了这么就,就弄出来这个?其实细想其实是地区太多导致,显示不开。
于是我们可以改一下,只获取每个省的数量,这样的好处就是数量不多,同时,可以显示的开。于是回到spider中。
11 def parse(self,response):
12
13 citys = ['北京','天津','上海','重庆','河北','山西','辽宁','吉林','黑
龙江','江苏','浙江','安徽','福建','江西','山东','河南','湖北','湖南','广东', '海南','四川','贵州','云南','陕西','甘肃','青海','台湾','内蒙古','广西','西> 藏','宁夏','新疆','香港','澳门']
14 for ct in citys:
15 #for citys in response.xpath('//div[@id="letter_choose"]/dl/dd'):
16 #for city in citys.xpath('.//a'):
17 #if city.xpath('.//strong').extract_first() is not None:
18 #city = city.xpath('.//strong')
19 #ct = city.xpath('.//text()').extract_first()
20 url_city = 'http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%s& kw=ios&sm=0&p=1' % ct
21 yield scrapy.Request(url=url_city, callback=self.parse_jobs)
Paste_Image.png
抓取结果:
Paste_Image.png不难发现,确实少了很多。
然后去重,分租,就不重复了
看一下最终结果:
这个是招聘平均工资,澳门香港最高。
Paste_Image.png看到整个山东加起来都不到150,我也就放心了。
-------------------------- 补充 ---------------------------------
scrapy 文档:https://doc.scrapy.org/en/1.3/intro/tutorial.html#how-to-run-our-spider
scrapy xpath文档:https://doc.scrapy.org/en/1.3/topics/selectors.html#topics-selectors
scrapy items文档:http://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/items.html
scrapy与mongoDB的使用:https://realpython.com/blog/python/web-scraping-with-scrapy-and-mongodb/
scrapy 去重:https://zhuanlan.zhihu.com/p/25473751?group_id=819874677425053696
pandas使用:http://pandas.pydata.org/pandas-docs/stable/10min.html#min
pandas with matplotlib:http://pandas.pydata.org/pandas-docs/version/0.13.1/visualization.html
pandas 读取json错误处理:http://stackoverflow.com/questions/36837663/reading-json-file-as-pandas-dataframe-error
网友评论