https://blog.csdn.net/fenglepeng/article/details/100539847 https://blog.csdn.net/fenglepeng/article/details/100541603
scrapy 框架中各组件的工作流程?
生成初始的 Requests 来爬取第一个 URLS,并且标识一个回调函数。第一个请求定义在 start_requests()
方法内默认从 start_urls
列表中获得 url 地址来生成 Request
请求,默认的回调函数是 parse
方法。回调函数在下载完成返回response时自动触发
在回调函数中,解析 response 并且返回值,返回值可以4种:
包含解析数据的字典
Item对象
新的Request对象(新的Requests也需要指定一个回调函数)
或者是可迭代对象(包含Items或Request)
在回调函数中解析页面内容,通常使用 Scrapy 自带的 Selectors,但很明显你也可以使用 Beutifulsoup,lxml 或其他你爱用的。
最后,针对返回的 Items 对象将会被持久化到数据库,通过 Item Pipeline 组件存到数据库,或者导出到不同的文件(通过 Feed exports)
在 scrapy 框架中如何设置代理(两种方法)? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import osimport scrapyfrom scrapy.http import Request class ChoutiSpider (scrapy.Spider) : name = 'chouti' allowed_domains = ['chouti.com' ] start_urls = ['https://dig.chouti.com/' ] def start_requests (self) : os.environ['HTTP_PROXY' ] = "http://192.168.11.11" for url in self.start_urls: yield Request(url=url,callback=self.parse) def parse (self, response) : print(response) import randomimport base64import sixdef to_bytes (text, encoding=None, errors='strict' ) : """Return the binary representation of `text`. If `text` is already a bytes object, return it as-is.""" if isinstance(text, bytes): return text if not isinstance(text, six.string_types): raise TypeError('to_bytes must receive a unicode, str or bytes ' 'object, got %s' % type(text).__name__) if encoding is None : encoding = 'utf-8' return text.encode(encoding, errors)class MyProxyDownloaderMiddleware (object) : def process_request (self, request, spider) : proxy_list = [ {'ip_port' : '111.11.228.75:80' , 'user_pass' : 'xxx:123' }, {'ip_port' : '120.198.243.22:80' , 'user_pass' : '' }, {'ip_port' : '111.8.60.9:8123' , 'user_pass' : '' }, {'ip_port' : '101.71.27.120:80' , 'user_pass' : '' }, {'ip_port' : '122.96.59.104:80' , 'user_pass' : '' }, {'ip_port' : '122.224.249.122:8088' , 'user_pass' : '' }, ] proxy = random.choice(proxy_list) if proxy['user_pass' ] is not None : request.meta['proxy' ] = to_bytes("http://%s" % proxy['ip_port' ]) encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass' ])) request.headers['Proxy-Authorization' ] = to_bytes('Basic ' + encoded_user_pass) else : request.meta['proxy' ] = to_bytes("http://%s" % proxy['ip_port' ]) DOWNLOADER_MIDDLEWARES = { }
scrapy 框架中如何实现大文件的下载? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from twisted.web.client import Agent, getPage, ResponseDone, PotentialDataLoss from twisted.internet import defer, reactor, protocol from twisted.web._newclient import Response from io import BytesIO class _ResponseReader (protocol .Protocol ): def __init__ (self , finished, txresponse, file_name) : self ._finished = finished self ._txresponse = txresponse self ._bytes_received = 0 self .f = open(file_name, mode='wb' ) def dataReceived (self , bodyBytes) : self ._bytes_received += len(bodyBytes) self .f.write(bodyBytes) self .f.flush() def connectionLost (self , reason) : if self ._finished.called: return if reason.check(ResponseDone): self ._finished.callback((self ._txresponse, 'success' )) elif reason.check(PotentialDataLoss): self ._finished.callback((self ._txresponse, 'partial' )) else: self ._finished.errback(reason) self .f.close()
scrapy 中如何实现限速? http://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/autothrottle.html
scrapy 中如何实现暂停爬虫? https://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/jobs.html?highlight=item
有些情况下,例如爬取大的站点,我们希望能暂停爬取,之后再恢复运行。Scrapy通过如下工具支持这个功能:
一个把调度请求保存在磁盘的调度器
一个把访问请求保存在磁盘的副本过滤器[duplicates filter]
一个能持续保持爬虫状态(键/值对)的扩展
Job 路径
要启用持久化支持,你只需要通过 JOBDIR 设置 job directory 选项。 这个路径将会存储所有的请求数据来保持一个单独任务的状态(例如:一次spider爬取(a spider run))。 必须要注意的是,这个目录不允许被不同的spider 共享,甚至是同一个 spider 的不同 jobs/runs 也不行。 也就是说,这个目录就是存储一个单独 job 的状态信息。
scrapy 中如何进行自定制命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from scrapy.commands import ScrapyCommand from scrapy.utils.project import get_project_settings class Command (ScrapyCommand ): requires_project = True def syntax (self ) : return '[options]' def short_desc (self ) : return 'Runs all of the spiders' def run (self , args, opts) : spider_list = self .crawler_process.spiders.list() for name in spider_list: self .crawler_process.crawl(name, **opts.__dict__ ) self .crawler_process.start()
scrapy 中如何实现的记录爬虫的深度? DepthMiddleware 是一个用于追踪每个Request在被爬取的网站的深度的中间件。其可以用来限制爬取深度的最大深度或类似的事情。 DepthMiddleware 可以通过下列设置进行配置(更多内容请参考设置文档):
‘DEPTH_LIMIT’:爬取所允许的最大深度,如果为0,则没有限制。
‘DEPTH_STATS’:是否收集爬取状态。
‘DEPTH_PRIORITY’:是否根据其深度对requet安排优先
scrapy 中的 pipelines 工作原理? Scrapy 提供了 pipeline 模块来执行保存数据的操作。 在创建的 Scrapy 项目中自动创建了一个 pipeline.py 文件,同时创建了一个默认的 Pipeline 类。 我们可以根据需要自定义 Pipeline 类,然后在 settings.py 文件中进行配置即可
scrapy 的 pipelines 如何丢弃一个 item 对象? 通过 raise DropItem()
方法
简述 scrapy 中爬虫中间件和下载中间件的作用?
https://blog.csdn.net/fenglepeng/article/details/100541603
scrapy-redis 组件的作用? 实现了分布式爬虫,url去重、调度器、数据持久化
‘scheduler’调度器
‘dupefilter’URL去重规则(被调度器使用)
‘pipeline’数据持久化
scrapy-redis 组件中如何实现的任务的去重?
内部进行配置,连接 Redis
去重规则通过 Redis 的集合完成,集合的 Key 为:key = defaults.DUPEFILTER_KEY % {'timestamp': int(time.time())}
默认配置:DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
去重规则中将url转换成唯一标示,然后在 Redis 中检查是否已经在集合中存在
1 2 3 4 5 from scrapy.utils import request from scrapy.http import Request req = Request(url ='http :/ / www .cnblogs .com / wupeiqi .html ') result = request.request_fingerprint(req ) print(result) # 8 ea4fd67887449313ccc12e5b6b92510cc53675c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 class RFPDupeFilter (BaseDupeFilter) : """Request Fingerprint duplicates filter""" def __init__ (self, path=None, debug=False) : self.fingerprints = set() @classmethod def from_settings (cls, settings) : debug = settings.getbool('DUPEFILTER_DEBUG' ) return cls(job_dir(settings), debug) def request_seen (self, request) : fp = self.request_fingerprint(request) if fp in self.fingerprints: return True self.fingerprints.add(fp) def request_fingerprint (self, request) : return request_fingerprint(request) class RFPDupeFilter (BaseDupeFilter) : """Redis-based request duplicates filter. This class can also be used with default Scrapy's scheduler. """ logger = logger def __init__ (self, server, key, debug=False) : self.server = server self.key = key @classmethod def from_settings (cls, settings) : server = get_redis_from_settings(settings) key = defaults.DUPEFILTER_KEY % {'timestamp' : int(time.time())} debug = settings.getbool('DUPEFILTER_DEBUG' ) return cls(server, key=key, debug=debug) @classmethod def from_crawler (cls, crawler) : return cls.from_settings(crawler.settings) def request_seen (self, request) : fp = self.request_fingerprint(request) added = self.server.sadd(self.key, fp) return added == 0 def request_fingerprint (self, request) : return request_fingerprint(request) def close (self, reason='' ) : self.clear() def clear (self) : """Clears fingerprints data.""" self.server.delete(self.key)
scrapy-redis 的调度器如何实现任务的深度优先和广度优先?