scrapy运行机制
安装
直接pip install scrapy
即可
使用
命令行
scrapy startproject name
命令创建一个新的Scrapy项目scrapy crawl SpiderName
命令运行爬虫scrapy runspider SpiderName
命令运行脚本。
更多命令直接查Command line tool
概述
- 编写Spider,返回Request或自定义的Item对象
- 所有Request对象都会被scrapy调度请求,并在请求后调用设定的callback函数
- Item对象则是提取请求回的数据,结构化数据并交由item pipeline处理
- 定义好Item对象,存储必要的信息(个人认为以文本数据为主,二进制数据应保存链接后续pipeline处理)
- 定义item pipeline,处理Item对象,例如存储到数据库或文件
编写Spider
-
在
project/spider
下创建Spider文件(scrapy genspider SpiderName website.com
可快速创建示例)
定义Spider类,必须继承scrapy.Spider
类,并且必须指定类参数name
,scrapy crawl
通过这个name
来启动爬虫。 -
实现
start_requests
方法,定义爬虫入口,返回一个Request对象。
如果只是简单的get请求,可以设定类参数start_urls = [url list]
以使用默认方法(默认回调是parse
方法) -
实现
parse(self, response)
方法,用于处理请求返回的响应,返回Item对象或者Request对象。
经常有请求完初始链接后要从响应拿到下一步的链接,这时可以在parse
方法中返回一个Request对象,callback指定另一个函数,且可以通过kw_args
传递额外参数(如本次处理的数据)给callback。
返回Item则会交给pipeline处理
from scrapy.http import Request, JsonRequest
...
# 一般的Request请求构建
yield Request(
url=url,
method="GET", # 默认get,一般不写
headers={}, # 看需要写,Spider middleware好像也能操作
callback=self.parse, # 自定义回调函数
cb_kwargs={"additional_args": args},
)
# post请求
yield Request(
url=url,
method="POST",
headers={"Content-Type": "application/json"}, # 看需要写
callback=self.parse,
body=json.dumps(payload), # 注意转成JSON字符串
)
yield JsonRequest( # 省略content-type,基本就是post请求
url=url,
data=payload, # 注意这个直接传dict
callback=self.parse,
)
def parse(self, response, if_additional_args):
response.text
response.body # 取二进制数据,注意不是requests的content
response.json() # 如果是json数据,可以直接使用
# scrapy可以直接使用xpath和css选择器,非常方便
for quote in response.xpath("//div[@class='quote']"): # 返回的是SelectorList对象,可以遍历
yield {
"quote": quote.xpath("./span[1]/text()").get(), # 注意用get返回第一个结果值
"author": quote.xpath("span[2]/small/text()").get(),
"tags": quote.css("a.tag::text").getall(), # 注意用getall返回所有结果的列表
}
定义Item
在project/item
下定义Item类
Item类必须继承scrapy.Item
类,Item其实就是个字典,但是key只能是定义了的字段名
class CspProblemItem(scrapy.Item):
# contest info
contest_id = scrapy.Field()
contest_title = scrapy.Field()
contest_date = scrapy.Field()
# problem info
title = scrapy.Field()
description_url = scrapy.Field()
description_filepath = scrapy.Field()
description = scrapy.Field()
attachment_urls = scrapy.Field() # 最后下载的文件一般不保存在Item字段里,
attachment_filepaths = scrapy.Field() # 而是记录文件的url和路径,保存后记录在Item里
# flags to indicate whether the problem has been processed
done = scrapy.Field()
编写pipeline
- 在
project/pipeline
下定义pipeline类,不需继承,要实现process_item(self, item, spider)
方法 - 启用pipeline需要在
settings.py
中设置ITEM_PIPELINES
,将需要的pipeline类加上,并赋上priority(越小优先级越高) - 每个pipeline都必须返回自己处理的item,item之后会流经其它pipeline
- pipeline处理处理Item时,该Item就会被锁定该pipeline中直到被return
- 对于不需要的Item对象可以丢弃,通过
raise DropItem("drop item info")
丢弃 - item在这里其实是鸭子类型,scrapy提供了
ItemAdapter
来统一它们
# 因为Item是鸭子类型,在使用时需要判断类型和字段
from itemadapter import ItemAdapter
class MyPipeline:
def process_item(self, item, spider):
adapter = ItemAdapter(item)
adapter = ItemAdapter(item)
adapter.get("key") # 其实和dict.get()是一样的
adapter.is_item_class(MyItem) # 用于判断是否是需要的Item类型
if adapter.get("done"): # 对于非目标对象,要把Item返回
return item
- 有时需要下载item里的url,直接使用response的返回值,使用callback就很不方便,这时就要用到deferred了
# https://docs.scrapy.org/en/latest/topics/coroutines.html#inline-requests
from itemadapter import ItemAdapter
from scrapy.exceptions import DropItem
from scrapy import Request
from scrapy.http.request import NO_CALLBACK
from scrapy.utils.defer import maybe_deferred_to_future
from twisted.internet.defer import DeferredList
class MyPipeline:
async def process_item(self, item, spider): # 注意要使用async
adapter = ItemAdapter(item)
# 单个请求
request = Request(adapter["url"], callback=NO_CALLBACK) # 不使用callback
response = await maybe_deferred_to_future(
spider.crawler.engine.download(request)
)
if response.status != 200: # 一般要判断一下下载是否成功,并做一些处理
raise DropItem(f"Could not download {adapter['url']}")
# 多个请求,就是比单个的多了DeferredList包装
deferred_list = []
for url in url_list_:
request = Request(url, callback=NO_CALLBACK)
deferred = spider.crawler.engine.download(request)
deferred_list.append(deferred)
result = await maybe_deferred_to_future(DeferredList(deferred_list))
# result变量将包含一个列表,该列表中的每个元素是一个元组,表示每个Deferred的结果。这个元组通常有两种形式:
# (True, result):如果对应的Deferred成功完成,result是回调链最终返回的结果。
# (False, failure):如果对应的Deferred因错误而终止,failure是一个Failure实例,代表发生的异常。
for i, (success, response) in enumerate(result):
if not success:
continue
file_path.write_bytes(response.body)
关于settings.py
- 如果全局使用固定的cookie(在浏览器复制),需要在
settings.py
中注释修改DEFAULT_REQUEST_HEADERS
,并且将COOKIES_ENABLED
设置为False
,否则这里的headers不能使用到cookie。或者也可以在middleware中进行自定义 - 在settings可以定义自己需要的任何参数,使用Spider对象的
spider.settings["key"]
即可取到参数值
That's all
其实还有middleware等可以定制更丰富的功能,这些都可以直接查询官方文档,根据自己需要进行定制
标签:pipeline,框架,item,url,Request,笔记,Item,scrapy From: https://www.cnblogs.com/faf4r/p/18459114