Heim  >  Artikel  >  Backend-Entwicklung  >  爬虫框架Scrapy实战之批量抓取招聘信息

爬虫框架Scrapy实战之批量抓取招聘信息

高洛峰
高洛峰Original
2016-10-17 13:49:341000Durchsuche

所谓网络爬虫,就是一个在网上到处或定向抓取数据的程序,当然,这种说法不够专业,更专业的描述就是,抓取特定网站网页的HTML数据。不过由于一个网站的网页很多,而我们又不可能事先知道所有网页的URL地址,所以,如何保证我们抓取到了网站的所有HTML页面就是一个有待考究的问题了。一般的方法是,定义一个入口页面,然后一般一个页面会有其他页面的URL,于是从当前页面获取到这些URL加入到爬虫的抓取队列中,然后进入到新页面后再递归的进行上述的操作,其实说来就跟深度遍历或广度遍历一样。

Scrapy是一个基于Twisted,纯Python实现的爬虫框架,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便~

Scrapy 使用 Twisted这个异步网络库来处理网络通讯,架构清晰,并且包含了各种中间件接口,可以灵活的完成各种需求。整体架构如下图所示:

1.jpg

绿线是数据流向,首先从初始URL 开始,Scheduler 会将其交给 Downloader 进行下载,下载之后会交给 Spider 进行分析,Spider分析出来的结果有两种:一种是需要进一步抓取的链接,例如之前分析的“下一页”的链接,这些东西会被传回 Scheduler ;另一种是需要保存的数据,它们则被送到Item Pipeline 那里,那是对数据进行后期处理(详细分析、过滤、存储等)的地方。另外,在数据流动的通道里还可以安装各种中间件,进行必要的处理。


我假定你已经安装了Scrapy。假如你没有安装,你可以参考这篇文章。

在本文中,我们将学会如何使用Scrapy建立一个爬虫程序,并爬取指定网站上的内容

1. 创建一个新的Scrapy Project

2. 定义你需要从网页中提取的元素Item

3.实现一个Spider类,通过接口完成爬取URL和提取Item的功能

4. 实现一个Item PipeLine类,完成Item的存储功能

我将会用腾讯招聘官网作为例子。

Github源码:https://github.com/maxliaops/scrapy-itzhaopin

1.jpg

目标:抓取腾讯招聘官网职位招聘信息并保存为JSON格式。

新建工程

首先,为我们的爬虫新建一个工程,首先进入一个目录(任意一个我们用来保存代码的目录),执行:

scrapy startprojectitzhaopin

 

最后的itzhaopin就是项目名称。这个命令会在当前目录下创建一个新目录itzhaopin,结构如下:

.

├── itzhaopin

│   ├── itzhaopin

│   │   ├── __init__.py

│   │   ├── items.py

│   │   ├── pipelines.py

│   │   ├── settings.py

│   │   └── spiders

│   │      └── __init__.py

│   └── scrapy.cfg

 

scrapy.cfg: 项目配置文件

items.py: 需要提取的数据结构定义文件

pipelines.py:管道定义,用来对items里面提取的数据做进一步处理,如保存等

settings.py: 爬虫配置文件

spiders: 放置spider的目录


定义Item

在items.py里面定义我们要抓取的数据:

from scrapy.item import Item, Field
class TencentItem(Item):
    name = Field()                # 职位名称
    catalog = Field()             # 职位类别
    workLocation = Field()        # 工作地点
    recruitNumber = Field()       # 招聘人数
    detailLink = Field()          # 职位详情页链接
    publishTime = Field()         # 发布时间

实现Spider

Spider是一个继承自scrapy.contrib.spiders.CrawlSpider的Python类,有三个必需的定义的成员

name: 名字,这个spider的标识

start_urls:一个url列表,spider从这些网页开始抓取

parse():一个方法,当start_urls里面的网页抓取下来之后需要调用这个方法解析网页内容,同时需要返回下一个需要抓取的网页,或者返回items列表

所以在spiders目录下新建一个spider,tencent_spider.py:

import re
import json
from scrapy.selector import Selector
try:
    from scrapy.spider import Spider
except:
    from scrapy.spider import BaseSpider as Spider
from scrapy.utils.response import get_base_url
from scrapy.utils.url import urljoin_rfc
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor as sle
from itzhaopin.items import *
from itzhaopin.misc.log import *
class TencentSpider(CrawlSpider):
    name = "tencent"
    allowed_domains = ["tencent.com"]
    start_urls = [
        "http://hr.tencent.com/position.php"
    ]
    rules = [ # 定义爬取URL的规则
        Rule(sle(allow=("/position.php\?&start=\d{,4}#a")), follow=True, callback='parse_item')
    ]
    def parse_item(self, response): # 提取数据到Items里面,主要用到XPath和CSS选择器提取网页数据
        items = []
        sel = Selector(response)
        base_url = get_base_url(response)
        sites_even = sel.css('table.tablelist tr.even')
        for site in sites_even:
            item = TencentItem()
            item['name'] = site.css('.l.square a').xpath('text()').extract()
            relative_url = site.css('.l.square a').xpath('@href').extract()[0]
            item['detailLink'] = urljoin_rfc(base_url, relative_url)
            item['catalog'] = site.css('tr > td:nth-child(2)::text').extract()
            item['workLocation'] = site.css('tr > td:nth-child(4)::text').extract()
            item['recruitNumber'] = site.css('tr > td:nth-child(3)::text').extract()
            item['publishTime'] = site.css('tr > td:nth-child(5)::text').extract()
            items.append(item)
            #print repr(item).decode("unicode-escape") + '\n'
        sites_odd = sel.css('table.tablelist tr.odd')
        for site in sites_odd:
            item = TencentItem()
            item['name'] = site.css('.l.square a').xpath('text()').extract()
            relative_url = site.css('.l.square a').xpath('@href').extract()[0]
            item['detailLink'] = urljoin_rfc(base_url, relative_url)
            item['catalog'] = site.css('tr > td:nth-child(2)::text').extract()
            item['workLocation'] = site.css('tr > td:nth-child(4)::text').extract()
            item['recruitNumber'] = site.css('tr > td:nth-child(3)::text').extract()
            item['publishTime'] = site.css('tr > td:nth-child(5)::text').extract()
            items.append(item)
            #print repr(item).decode("unicode-escape") + '\n'
        info('parsed ' + str(response))
        return items
    def _process_request(self, request):
        info('process ' + str(request))
        return request

实现PipeLine 

PipeLine用来对Spider返回的Item列表进行保存操作,可以写入到文件、或者数据库等。

PipeLine只有一个需要实现的方法:process_item,例如我们将Item保存到JSON格式文件中:

pipelines.py

from scrapy import signals
import json
import codecs
class JsonWithEncodingTencentPipeline(object):
    def __init__(self):
        self.file = codecs.open('tencent.json', 'w', encoding='utf-8')
    def process_item(self, item, spider):
        line = json.dumps(dict(item), ensure_ascii=False) + "\n"
        self.file.write(line)
        return item
    def spider_closed(self, spider):
        self.file.close(
)

到现在,我们就完成了一个基本的爬虫的实现,可以输入下面的命令来启动这个Spider:

scrapy crawl tencent

爬虫运行结束后,在当前目录下将会生成一个名为tencent.json的文件,其中以JSON格式保存了职位招聘信息。

部分内容如下:

{"recruitNumber": ["1"], "name": ["SD5-资深手游策划(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15626&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["产品/项目类"], "workLocation": ["深圳"]}

{"recruitNumber": ["1"], "name": ["TEG13-后台开发工程师(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15666&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技术类"], "workLocation": ["深圳"]}

{"recruitNumber": ["2"], "name": ["TEG12-数据中心高级经理(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15698&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技术类"], "workLocation": ["深圳"]}

{"recruitNumber": ["1"], "name": ["GY1-微信支付品牌策划经理(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15710&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["市场类"], "workLocation": ["深圳"]}

{"recruitNumber": ["2"], "name": ["SNG06-后台开发工程师(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15499&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技术类"], "workLocation": ["深圳"]}

{"recruitNumber": ["2"], "name": ["OMG01-腾讯时尚视频策划编辑(北京)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15694&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["内容编辑类"], "workLocation": ["北京"]}

{"recruitNumber": ["1"], "name": ["HY08-QT客户端Windows开发工程师(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=11378&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技术类"], "workLocation": ["深圳"]}

{"recruitNumber": ["5"], "name": ["HY1-移动游戏测试经理(上海)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15607&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["技术类"], "workLocation": ["上海"]}

{"recruitNumber": ["1"], "name": ["HY6-网吧平台高级产品经理(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=10974&keywords=&tid=0&lid=0", "publishTime": ["2014-04-25"], "catalog": ["产品/项目类"], "workLocation": ["深圳"]}

{"recruitNumber": ["4"], "name": ["TEG14-云存储研发工程师(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=15168&keywords=&tid=0&lid=0", "publishTime": ["2014-04-24"], "catalog": ["技术类"], "workLocation": ["深圳"]}

{"recruitNumber": ["1"], "name": ["CB-薪酬经理(深圳)"], "detailLink": "http://hr.tencent.com/position_detail.php?id=2309&keywords=&tid=0&lid=0", "publishTime": ["2013-11-28"], "catalog": ["职能类"], "workLocation": ["深圳"]}


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn