Scrapy终端是一个交互终端,我们可以在未启动spider的情况下尝试及调试代码,也可以用来测试XPath或CSS表达式,查看他们的工作方式,方便我们爬取的网页中提取的数据。
如果安装了 IPython ,Scrapy终端将使用 IPython (替代标准Python终端)。 IPython 终端与其他相比更为强大,提供智能的自动补全,高亮输出,及其他特性。(推荐安装IPython)
1 启动Scrapy Shell
进入项目的根目录,执行下列命令来启动shell:
scrapy shell "https://hr.tencent.com/position.php?&start=0#a"
Scrapy Shell根据下载的页面会自动创建一些方便使用的对象,例如 Response 对象,以及Selector 对象 (对HTML及XML内容)
。
- 当shell载入后,将得到一个包含response数据的本地 response 变量,输入
response.body
将输出response的包体,输出response.headers
可以看到response的包头。 - 输入
response.selector
时, 将获取到一个response 初始化的类 Selector 的对象,此时可以通过使用response.selector.xpath()
或response.selector.css()
来对 response 进行查询。 - Scrapy也提供了一些快捷方式, 例如
response.xpath()
或response.css()
同样可以生效(如之前的案例)。
2 Selectors选择器
Scrapy Selectors 内置 XPath 和 CSS Selector 表达式机制
Selector有四个基本的方法,最常用的还是xpath:
- xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
- extract(): 序列化该节点为Unicode字符串并返回list
- css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表,语法同 BeautifulSoup4
- re(): 根据传入的正则表达式对数据进行提取,返回Unicode字符串list列表
response.xpath('//title')
3 Item Pipeline
当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理Item。
每个Item Pipeline都是实现了简单方法的Python类,比如决定此Item是丢弃而存储。以下是item pipeline的一些典型应用:
- 验证爬取的数据(检查item包含某些字段,比如说name字段)
- 查重(并丢弃)
- 将爬取结果保存到文件或者数据库中
编写item pipeline很简单,item pipiline组件是一个独立的Python类,其中process_item()方法必须实现:
import something
class SomethingPipeline(object):
def __init__(self):
# 可选实现,做参数初始化等
# doing something
def process_item(self, item, spider):
# item (Item 对象) – 被爬取的item
# spider (Spider 对象) – 爬取该item的spider
# 这个方法必须实现,每个item pipeline组件都需要调用该方法,
# 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。
return item
def open_spider(self, spider):
# spider (Spider 对象) – 被开启的spider
# 可选实现,当spider被开启时,这个方法被调用 (也可以放在__init__方法中)
def close_spider(self, spider):
# spider (Spider 对象) – 被关闭的spider
# 可选实现,当spider被关闭时,这个方法被调用 (也可放入析构函数__del__方法中)
Pipeline实现文件的写入打开操作
以下pipeline将所有(从所有’spider’中)爬取到的item,存储到一个独立地txt文件
class TianyaPipeline(object):
def __init__(self):
self.f = open("tianya.txt", "w", encoding="utf-8")
def process_item(self, item, spider):
self.f.write(str(item))
# return item
def __del__(self):
self.f.close()
#附Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列
#replace用于替换指定字符
#join用于合并列表 元组等
启用一个Item Pipeline组件
为了启用Item Pipeline组件,必须将它的类添加到 settings.py文件ITEM_PIPELINES 配置,就像下面这个例子:
ITEM_PIPELINES = {
'tianya.pipelines.TianyaPipeline': 300,
}
分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内(0-1000随意设置,数值越低,组件的优先级越高)
4 Spider
Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及分析某个网页(或者是有些网页)的地方。
class scrapy.Spider
是最基本的类,所有编写的爬虫必须继承这个类。
主要用到的函数及调用顺序为:
__init__()
: 初始化爬虫名字和start_urls列表
start_requests() 调用make_requests_from url()
:生成Requests对象交给Scrapy下载并返回response
parse()
: 解析response,并返回Item或Requests(需指定回调函数)。Item传给Item pipline持久化 , 而Requests交由Scrapy下载,并由指定的回调函数处理(默认parse()),一直进行循环,直到处理完所有的数据为止。
源码参考
#所有爬虫的基类,用户定义的爬虫必须从这个类继承
class Spider(object_ref):
#定义spider名字(string)。spider的名字定义了Scrapy如何定位(并初始化)spider,所以其必须是唯一的。
#name是spider最重要的属性,而且是必须的。
#一般做法是以该网站(domain)(加或不加 后缀 )来命名spider。例如,爬取百度命名为: baidu
name = None
#初始化,提取爬虫名字,start_ruls
def __init__(self, name=None, **kwargs):
if name is not None:
self.name = name
# 如果爬虫没有名字,中断后续操作则报错
elif not getattr(self, 'name', None):
raise ValueError("%s must have a name" % type(self).__name__)
# python 对象或类型通过内置成员__dict__来存储成员信息
self.__dict__.update(kwargs)
#URL列表。当没有指定的URL时,spider将从该列表中开始进行爬取。
#因此,第一个被获取到的页面的URL将是该列表之一。后续的URL将会从获取到的数据中提取。
if not hasattr(self, 'start_urls'):
self.start_urls = []
# 打印Scrapy执行后的log信息
def log(self, message, level=log.DEBUG, **kw):
log.msg(message, spider=self, level=level, **kw)
# 判断对象object的属性是否存在,不存在做断言处理
def set_crawler(self, crawler):
assert not hasattr(self, '_crawler'), "Spider already bounded to %s" % crawler
self._crawler = crawler
@property
def crawler(self):
assert hasattr(self, '_crawler'), "Spider not bounded to any crawler"
return self._crawler
@property
def settings(self):
return self.crawler.settings
#该方法将读取start_urls内的地址,并为每一个地址生成一个Request对象,交给Scrapy下载并返回Response
#该方法仅调用一次
def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url)
#start_requests()中调用,实际生成Request的函数。
#Request对象默认的回调函数为parse(),提交的方式为get
def make_requests_from_url(self, url):
return Request(url, dont_filter=True)
#默认的Request对象回调函数,处理返回的response。
#生成Item或者Request对象。用户必须实现这个类
def parse(self, response):
raise NotImplementedError
@classmethod
def handles_request(cls, request):
return url_is_from_spider(request.url, cls)
def __str__(self):
return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self))
__repr__ = __str__
主要属性和方法
-
name
定义spider名字的字符串。
例如,如果spider爬取 mywebsite.com ,该spider通常会被命名为 mywebsite
-
allowed_domains
包含了spider允许爬取的域名(domain)的列表,可选。
-
start_urls
初始URL元祖/列表。当没有制定特定的URL时,spider将从该列表中开始进行爬取。
-
start_requests(self)
该方法必须返回一个可迭代对象(iterable)。该对象包含了spider用于爬取(默认实现是使用 start_urls 的url)的第一个Request。
当spider启动爬取并且未指定start_urls时,该方法被调用。
-
parse(self, response)
当请求url返回网页没有指定回调函数时,默认的Request对象回调函数。用来处理网页返回的response,以及生成Item或者Request对象。
-
log(self, message[, level, component])
使用 scrapy.log.msg() 方法记录(log)message。 更多数据请参见logging
5 案例:腾讯招聘网自动翻页采集
- 创建一个新的爬虫:
scrapy genspider tencent "tencent.com"
- 编写items.py
获取职位名称、详细信息、
class TencentItem(scrapy.Item):
# 定义需要爬取的字段
jobTitle = scrapy.Field()
jobCategories = scrapy.Field()
number = scrapy.Field()
location = scrapy.Field()
releasetime = scrapy.Field()
- 编写tencent.py
# -*- coding: utf-8 -*-
import re
import scrapy
from Tencent import items
class MytencentSpider(scrapy.Spider):
name = 'myTencent'
allowed_domains = ['hr.tencent.com']
start_urls = ['https://hr.tencent.com/position.php?lid=2218&start=0#a']
def parse(self, response):
for data in response.xpath("//tr[@class=\"even\"] | //tr[@class=\"odd\"]"):
item = items.TencentItem()
item["jobTitle"] = data.xpath("./td[1]/a/text()")[0].extract()
item["jobLink"] = data.xpath("./td[1]/a/@href")[0].extract()
item["jobCategories"] = data.xpath("./td[1]/a/text()")[0].extract()
item["number"] = data.xpath("./td[2]/text()")[0].extract()
item["location"] = data.xpath("./td[3]/text()")[0].extract()
item["releasetime"] = data.xpath("./td[4]/text()")[0].extract()
yield item
for i in range(1, 200):
newurl = "https://hr.tencent.com/position.php?lid=2218&start=%d#a" % (i*10)
yield scrapy.Request(newurl, callback=self.parse)
- 编写pipeline.py文件
class TencentPipeline(object):
def __init__(self):
self.file = open("tencent.txt", "w", encoding="utf-8") #初始化即打开
def process_item(self, item, spider):
line = str(item) + "\r\n"
self.file.write(line)
self.file.flush()
return item
def __del__(self): #数据清除时关闭
self.file.close()
- 在 setting.py 里设置ITEM_PIPELINES
ITEM_PIPELINES = {
"mySpider.pipelines.TencentJsonPipeline":300
}
-
执行爬虫:
scrapy crawl tencent.py