在入坑爬虫的学习中,第一个接触的爬虫框架就是 Scrapy 。在使用了相比于其他的框架而言,如 PySpider、Crawley 等,个人觉得 Scrapy 在使用上更简洁、体系上更完善。
因为平时也是用业余的时间,写写比较简单的爬虫或者 github 上找一些比较有意思的项目研究一下之外,在主要的工作时间里也是比较少接触到这一块。所以有时隔了段时间再去看的话,多少都会有点陌生或者遗忘。
碰巧,最近因为公司的唯一爬虫同事走了,项目组中唯一比较熟悉 python 的我顺其而然就接这个盘了 -->_-->
于是决定再把文档重新撸一遍,做点笔记加深印象,也方便后面温习。
快速开始
-
创建工程
scrapy startproject tutorial
创建一个 tutorial 的工程工程的目录如下:
tutorial/ scrapy.cfg # deploy configuration file tutorial/ # project's Python module, you'll import your code from here __init__.py items.py # project items definition file middlewares.py # project middlewares file pipelines.py # project pipelines file settings.py # project settings file spiders/ # a directory where you'll later put your spiders __init__.py
-
编写spider
在
tutorial/spiders
目录下新建quotes_spider.py
:import scrapy class QuotesSpider(scrapy.Spider): name = "quotes" def start_requests(self): urls = [ 'http://quotes.toscrape.com/page/1/', 'http://quotes.toscrape.com/page/2/', ] for url in urls: yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): page = response.url.split("/")[-2] filename = 'quotes-%s.html' % page with open(filename, 'wb') as f: f.write(response.body) self.log('Saved file %s' % filename)
name
:spider 的唯一标识,每个 spider 都必须是唯一的start_requests()
: 爬取的地址parse()
:爬取内容结果的解析
-
运行
scrapy crawl quotes
quotes 为上文所说的 name 标识
基础概念
Spider
功能:
定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及分析某个网页(或者是有些网页)的地方。
爬取过程:
-
以初始的 URL 初始化 Request ,并设置回调函数。 当该 request 下载完毕并返回时,将生成 response,并作为参数传给该回调函数。
spider 中初始的 request 是通过调用
start_requests()
来获取的。start_requests()
读取start_urls
中的 URL, 并以parse
为回调函数生成 Request 。 -
在回调函数内分析返回的(网页)内容,返回 Item 对象、dict、 Request 或者一个包括三者的可迭代容器。 返回的Request 对象之后会经过 Scrapy 处理,下载相应的内容,并调用设置的
callback
函数(函数可相同)。 -
在回调函数内,您可以使用 选择器(Selectors) (您也可以使用 BeautifulSoup , lxml 或者您想用的任何解析器) 来分析网页内容,并根据分析的数据生成 item。
-
最后,由 spider 返回的item将被存到数据库(由某些 Item Pipeline 处理)或使用 Feed exports 存入到文件中。
常用属性:
-
name:
唯一标识
-
allowed_domains:
包含了spider允许爬取的域名(domain)列表(list)
-
custom_settings:
该设置是一个dict.当启动spider时,该设置将会覆盖项目级的设置. 由于设置必须在初始化(instantiation)前被更新,所以该属性 必须定义为class属性.
-
start_requests
():该方法必须返回一个可迭代对象(iterable)。该对象包含了spider用于爬取的第一个Request。
当spider启动爬取并且未制定URL时,该方法被调用
-
parse
(response)当response没有指定回调函数时,该方法是Scrapy处理下载的response的默认方法。
Selector
功能:
Selector(选择器)通过特定的 XPath 或者 CSS 表达式来“选择” HTML 文件中的某个部分。简单来说就是解析爬取的网页HTML数据。
使用:
>>> response.xpath('//span/text()').get()
'good'
>>> response.css('span::text').get()
'good'
解锁更多的使用方式和 API 可以去官网查阅
Items
功能:
为了从非结构性的数据源提取结构性数据,定义常用的输出数据
使用:
# 定义
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
# 创建
product = Product(name='Desktop PC', price=1000)
# 获取
>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC
# 赋值
>>> product['last_updated'] = 'today'
Item Loaders
功能:
Item Loaders 提供了一种便捷的方式填充抓取到的:Items。虽然 Items 可以使用自带的类字典形式的 API 填充,但是 Item Loaders 提供了更便捷的 API,可以分析原始数据并对 Item 进行填充。
Items 提供保存抓取数据的容器,而 Item Loaders 提供的是填充容器的机制。
Item Loaders 提供的是一种灵活,高效的机制,可以更方便的被 spider 或 source format (HTML,XML,etc)扩展,并 override 更易于维护的、不同的内容分析规则。
使用:
from scrapy.loader import ItemLoader
from myproject.items import Product
def parse(self, response):
l = ItemLoader(item=Product(), response=response)
l.add_xpath('name', '//div[@class="product_name"]')
l.add_xpath('name', '//div[@class="product_title"]')
l.add_xpath('price', '//p[@id="price"]')
l.add_css('stock', 'p#stock]')
l.add_value('last_updated', 'today') # you can also use literal values
return l.load_item()
简单说,数据通过用 add_xpath() 的方法,把XPath 位置提取的数据收集起来。
最终,当所有数据被收集起来后,调用 ItemLoader.load_item() 方法,实际上填充并返回了之前通过调用 add_xpath(),add_css(),和 add_value() 所提取和收集到的数据的 Item。
Input and Output processors
功能:
Item Loader 在每个(Item)字段中都包含了一个输入处理器和一个输出处理器。输入处理器收到数据时立刻提取数据(通过 add_xpath(),add_css(),和 add_value() 方法)之后输入处理器的结果被收集起来并且保存在 ItemLoader 内。收集到所有数据后,调用 ItemLoader.load_item() 方法来填充,并且得到填充后的 Item 对象。这是当输出处理器被和之前收集到的数据(和用输入处理器处理的)被调用,输出处理器的结果是被分配到 Item 的最终值。
使用:
# 自定义ItemLoader
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join
class ProductLoader(ItemLoader):
default_output_processor = TakeFirst()
name_in = MapCompose(unicode.title)
name_out = Join()
price_in = MapCompose(unicode.strip)
# ...
Pipeline
功能:
- 清理HTML数据
- 验证解析到的数据(检查Item是否包含必要的字段)
- 检查是否是重复数据(如果重复就删除)
- 将解析到的数据存储到数据库中
使用:
-
process_item(item, spider)
每一个 item 管道组件都会调用该方法,并且必须返回一个item对象实例或 raise DropItem 异常。被丢掉的 item 将不会在管道组件进行执行。此方法有两个参数,一个是 item ,即要处理的 Item 对象,另一个参数是 spider ,即爬虫。
此外,我们也可以在类中实现以下方法Scrapy会先通过 getattr 判断我们是否自定义了 from_crawler ,有则调它来完成实例化,早于
__init__
方法执行自己要的参数要去 settings.py 文件配置 -
open_spider(spider)
当 spider 执行的时候将调用该方法
-
close_spider(spider)
当 spider 关闭的时候将调用该方法
ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
每个 pipeline 后面有一个数值,这个数组的范围是 0-1000,这个数值是这些在 pipeline 中定义的类的优先级,越小越优先。
实践项目
笔者写了一个简单的爬虫例子,用于爬取表情包的。
普通的改变,将改变普通
我是宅小年,一个在互联网低调前行的小青年
关注公众号「宅小年」,个人博客 📖 edisonz.cn,阅读更多分享文章