Scrapy 是一个基于 Twisted 的异步处理框架,是纯 Python 实现的开源爬虫框架,其架构清晰,模块之间的耦合程度低,可扩展性极强,可以灵活完成各种需求。
Scrapy 中的数据流由引擎控制,其过程如下:
pip install scrapy
scrapy startproject tutorial
cd tutorial
tree /f .
scrapy genspider quotes quotes.toscrape.com
执行完成之后,可以发现 spiders 文件夹中多了一个 quotes.py 文件,其中文件内容如下图所示:
可以发现这里有三个属性和一个方法:
Item 是保存爬取数据的容器,创建 Item 需要继承 scrapy.Item 类,并且定义类型为 scrapy.Field 的字段。观察目标网站定义要爬取的内容:
parse 方法的参数 response 是 start_urls 里面的链接爬取后的结果。所以在 parse 方法中,可以直接对 response 变脸包含的内容进行解析,比如浏览请求结果的网页源代码,或者进一步分析源代码内容,或者找出结果中的链接而得到下一个请求。
scrapy crawl quotes -o quotes.json
Downloader Middlewares 即下载中间件,它是处于 Scrapy Engine 和 Downloader 之间的组件,主要是处理引擎与下载器之间的请求及响应。
在引擎传递请求给下载器的过程中,下载中间件可以对请求进行处理(例如,增加 headers 信息,增加 proxy 信息等)。在下载器完成网络请求,传递响应给引擎的过程中,下载中间件可以对响应进行处理(例如,进行 gzip 压缩等)。
编写下载中间件只需要实现以下的一个或全部即可:
Request 被 Scrapy 引擎调度给 Downloader 之前,process_request 方法就会被调用,也就是在 Request 从队列里调度出来到 Downloader 下载执行之前,可以用 process_request 方法对 Request 进行处理。方法的返回值必须为 None、Response 对象、Request 对象之一,或者抛出 IgnoreRequest 异常。该方法的参数包括:
该方法的返回结果包括:
Downloader 执行 Request 下载之后,会得到对应的 Response。Scrapy 引擎便会将 Response 发送给 Spider 进行解析。在发送之前,我们都可以用 process_response() 方法来对 Response 进行处理。方法的返回值必须为 Request 对象、Response 对象之一,或者抛出 IgnoreRequest 异常。该方法的参数包括:
该方法的返回结果包括:
当 Downloader 或 process_request 方法抛出异常时,例如抛出 IgnoreRequest 异常,process_exception() 方法就会被调用。方法的返回值必须为 None、Response 对象、Request 对象之一。该方法的参数包括:
该方法的返回结果包括:
scrapy genspider httpbin
将 start_urls 修改为 http://httpbin.org/get 结果如下:
scrapy crawl httpbin --nolog
结果如下:
可以发现 Request 使用的 User-Agent 是 Scrapy/2.9.0 (+https://scrapy.org),这其实是由 Scrapy 内置的 UserAgentMiddleware 设置的,UserAgentMiddleware源码如下:
在 from_crawler 方法中,首先尝试获取 settings 里面的 USER_AGENT,然后把 USER_AGENT 传递给 init 方法进行初始化,其参数就是 user_agent。如果没有传递 USER_AGENT 参数就默认设置为 Scrapy 字符串。接下来,在 process_request 方法中,将 user_agent 变量设置为 headers 变量的一个属性,这样就成功设置了 User-Agent。
如果需要设置随机 User-Agent 就需要借助 Downloader Middleware,在 middlewares.py 里面添加一个 RandomUserAgentMiddleware 的类,如下所示:
class RandomUserAgentMiddleware:
def __init__(self):
self.user_agents = [
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11'
]
def process_request(self, request, spider):
request.headers['User-Agent'] = random.choice(self.user_agents)
接下来在 settings.py 中,将 DOWNLOADER_MIDDLEWARES 设置成如下内容:
DOWNLOADER_MIDDLEWARES = {
"tutorial.middlewares.RandomUserAgentMiddleware": 543,
}
重新运行 Spider,就可以看到 Uset-Agent 被成功修改为列表中所定义的随机的一个 User-Agent,如下图所示:
当 Item 在 Spiders 中被收集之后,会被传递到 Item Pipeline。用户可以在 Scrapy 项目中定义多个管道,这些管道按定义的顺序依次处理 Item。
每个管道都是一个 Python 类,在这个类中实现了一些操作 Item 的方法。其中,有的方法用于丢弃重复的 Item,有的方法用于将 Item 存储到数据库或文件等。以下是 Item Pipeline 的一些典型应用:
自定义 Item Pipeline 很简单,每个 Item Pipeline 组件都是一个独立的 Python 类,该类中的 process_item 方法必须实现,每个 Item Pipeline 组件都需要调用 process_item 方法。
process_item 方法必须返回一个 Item 对象,或者抛出 DropItem 异常,被丢弃的 Item 将不会被之后的 Pipeline 组件所处理。该方法的定义如下:
process_item(self, item, spider)
该方法的 2 个参数分别是:
scrapy genspider imagesnetbian pic.netbian.com
class MongoPipeline:
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DB'))
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def process_item(self, item, spider):
self.db[item.collection].insert_one(dict(item))
return item
def close_spider(self, spider):
self.client.close()
这里需要用到两个变量,MONGO_URI 和 MONGO_DB,即存储到 MONGODB 的链接地址和数据库名称。可以在 settings.py 里添加这两个变量,如下所示:
scrapy crawl imagesnetbian --nolog
使用 MongoDB 客户端查看数据库可以发现结果已经保存到 MongoDB 中,结果如下: