请求传参
我们在spider中经常使用了两个或多个方法来用作数据解析,且每个都是解析不同的数据内容,那我们的item又只能实例化一次,我们要怎么把item类型对象分配到不同的方法中呢?
这就要用到请求传参。
yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item})可以将meta字典传递给请求对应的回调函数
回调函数怎么接收item?
通过response来接收item
item = response.meta['item']
然后就是正常的将要存储的内容封装到item对象中,最后提交给管道进行持久化存储的操作。
使用selenium
当我们使用scrapy爬取的数据是动态加载的怎么办呢?
之前使用requests时,遇到动态加载的数据我们可以使用selenium,那scrapy我们能用吗,答案是可以的。
我们把request请求给下载器时,不是要经过下载中间件吗,然后下载器在把下载好的页面数据返回给引擎,但这里的页面数据是没有包含动态加载数据的,那这些页面数据对我们来说是不是没用,所以我们就要在下载器中间件中重写下载中间件的process_response方法,让该方法对没有包含动态加载数据的响应对象进行拦截,并篡改response中存储的页面数据。
首先在spider中写一个方法实例化一个浏览器对象。
def __init__(self): self.bro = webdriver.Edge()
写一个 方法关闭浏览器,且是在爬虫结束时。
def closed(self,spider): self.bro.quit()
然后在Middlewares中需要导入 HtmlResponse类, 这个类是用来将响应数据包装成符合HTTP协议形式。
from scrapy.http import HtmlResponse
再看看 下载中间件中process_response方法
def process_request(self, request, spider): return None
其中request: 响应对象所对应的请求对象
response: 拦截到的响应对象
spider: 爬虫文件中对应的爬虫类,可以通过这个参数拿到爬虫类中的一些属性或方法
我们要获取在爬虫类定义的浏览器对象
bro = spider.bro
然后进行请求发送
bro.get(spider.request.url) sleep(2) # 等待加载, 可以用显示等待来优化。 page_text = bro.page_source #包含了动态加载的数据。
再实例化一个新的响应对象(包含动态加载数据),代替原来旧的响应对象(不包含动态加载数据)
new_reponse = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request) return new_reponse
最后再配置文件中开启下载中间件DOWNLOADER_MIDDLEWARES
CrawlSpider
CrawlSpider,继承自Spider
, 爬取网站常用的爬虫,其定义了一些规则(rule)方便追踪或者是过滤连接。
创建CrawlSpider输入 scrapy genspider -t crawl <爬虫文件名> <域名>。
初始化和spider差不多,多了个rules,里面有个Rule是规则解析器,LinkExtractor是连接提取器。
连接提取器:根据指定规则进行指定连接的提取,这里的规则就是正则表达式,写在allow中。
规则解析器:将连接提取器提取到的连接进行指定规则的解析操作,这里的规则就是callback。
当连接提取器提取到连接之后会自动的进行request请求发送,这个链接所对应的页面源码数据,都会由callback所指定的回调函数进行数据解析。
follow:指定了根据该规则从response提取的链接是否需要跟进,默认为True。
就是连接提取器提取到到的连接,然后在这个连接对应的页面源码中可以继续用连接提取器提取连接,有点套娃的意思,不过这样能实现对整个网站数据爬虫的操作。
其中parse_item中的response参数就是连接进行request请求之后的响应对象。
如果有不同的连接要提取就写两个规则解析器,两个连接提取器,一个规则解析器只能对应一个连接提取器。
link = LinkExtractor(allow=r'id=1&page=\d+') link_detail = LinkExtractor(allow=r'index?id=\d+') rules = ( Rule(link, callback='parse_item', follow=True), Rule(link_detail, callback='parse_detail'), )
CrawlSpider中不能请求传参,因为这里url都是自动发请求的。所以只能不同方法中解析到的数据放到不同的item中,在items中要写多个class item类。
但是最后我们提交了两个item给管道,管道怎么区分是哪个item呢?
我们可以使用一个if语句来判断
if item.__class__.__name__=='DetailItem'
这里只是介绍了一些常用的操作和方法,还有很多的方法和操作,大家感兴趣的话可以去看看文档。