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 https://legacy.gitbook.com/book/fategithub/pythonspider/edit#
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
来源: https://www.cnblogs.com/why957/p/9271795.html