我把我知道Scrapy都告诉你

作者: Rokkia | 来源:发表于2017-03-19 23:39 被阅读1416次

最近在找工作,发现每天智联上招聘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

Paste_Image.png

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
第三步,爬取的网页

接下来我们来看一下我们要爬取的网站。

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
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

我们离成功更近一步了。

接下来第五步,我们获取城市还没完,我们还要查看招聘信息。

这之前需要说一个问题,就是之前说的parse这个方法的问题。其实我们看文档会发现,我们本可以不使用parse这个方法。

Paste_Image.png

这是文档中的一段,我们不难发现这里调用了
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.png

Ok来执行以下

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的方法,貌似可以拿来用一下。

Paste_Image.png

我们需要对position列进行分组,然后获取price的平均价格
这里是获取平均值的方法,http://www.aichengxu.com/python/16058.htm

Paste_Image.png

同时我们还需要显示个地区的所有招聘数量,这个可以使用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()就显示图了,为什么我没有?

Paste_Image.png

然后找了半天资料,发现需要执行一下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

这个是招聘平均工资,澳门香港最高。

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

完!

相关文章

  • 我把我知道Scrapy都告诉你

    最近在找工作,发现每天智联上招聘iOS的并不多,于是我就很好奇,是我投递的位置不对嘛?于是本着好奇心,于是决定爬取...

  • 我把我知道的MVVM都告诉你(iOS)

    写在文章之前,这是我第一次用MVVM来写小Demo。研究一年多才敢写MVVM,一来:从初次使用RAC到认识RAC就...

  • 孩子,我把实话都告诉你

    很多婴幼儿,在看到周边的人突然离他远去,尤其是妈妈,大都会哭闹。看到他们哭,歇斯底里的哭,谁不心疼呢?可是如果不方...

  • 战友,我把知道的告诉你

    绿骏马 1.《象与骑象人》 2.《思维的进化》 3.《有事您说话》 上面三篇,是《自我发展心理学》前三部分,下面是...

  • 自媒体平台都有哪些?我把我知道的都告诉你

    很多人都听说过靠自媒体可以赚钱,靠写作、发视频可以挣钱,但知道归知道,如何才能把知道的变为现实呢? 想要把知道变为...

  • 520

    我把喜欢你的秘密告诉了风 风又告诉了林木湖海 于是啊 全世界都知道了 只有你还装作不知道

  • 我知道你都知道

    街角有人祝福 巷口有人哭 是不是感情就该有个胜负 你用浓妆来保护 想开口却忍住 我已没了退路 你却认输 我知道 你...

  • 我知道,你都知道

    很亲幸在放肆的年纪遇见了你,而如今看着微信好友列表里的你却没勇气再给你发一句简单的问候。 听一首伤感的歌会想起...

  • 我知道你都知道

    去年的时候大概也是这个季节,心情十分低落,自己一个人躲在家里无意间看到谦谦的电视剧,并且还在剧中献唱了插曲,还有自...

  • 你知道,我都知道

    我在22:51给你发微信:“士之耽兮,尤可说也。女之耽兮,不可说也。”其实我有犹豫过要不要按下发送键。早上7:32...

网友评论

  • Chinesszz:github地址留一份呗
    Chinesszz:@__鲸_ 厉害:+1:🏻
    Rokkia:@Chinesszz https://github.com/hzj7510/Zhilian_Spider.git
  • 08c5f92d64ec:非常感谢作者的劳动,我英语基本功不好,看官方文档很困难,阅读作者的文章后,学习了很多。
    Rokkia:@王子超超超 一起加油
    Rokkia: @王子超超超 嗯 ,谢谢支持 ,我会继续努力。
  • treeyang:学习

本文标题:我把我知道Scrapy都告诉你

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