############################ requests的使用
import requests# 发送请求, 使用的函数按照HTTP访问的方法,主要:get、post、put、delete、head、options,patch# 最终都是调用了request函数,这个函数的原型是: def request(method, url, **kwargs):# kwargs可以填入:headers, params, data, json, proxiesresponse = requests.get('https://www.baidu.com')# 返回的对象是 requests.models.Response# response对象通常有以下几个方法:response.status_code # 状态码response.request.headers # 对应请求的请求头response.headers # 响应头response.encoding # 默认解码方式,修改之前默认是ISO-8859-1response.text # 按照response.encoding 解码的strresponse.content # bytesprint(response.content.decode('utf-8'))######## 保持会话进行爬取,每次请求都会带上cookiesession = requests.session() # requests.sessions.Session类型response = session.get(url,headers)####### 小技巧cookie转为字典 requests.util.dict_from_cookiejarhttps请求不进行ssl验证 requests.get('https://www.baidu.com', verify=False)设置超时 requests.get(url, 1)################################### 正向代理和反向代理
代理为谁服务就隐藏谁的信息。正向代理:代理为客户端服务,产生的结果是:服务端不知道真正的客户端
反向代理:代理为服务端服务,并对服务集群做负载均衡,将请求均衡分配到空闲服务器,产生的结果是:客户端不知道真正的服务器。nginx服务器的作用
1.C-S架构模式解耦,他是一个中间层,可以随时更改绑定的服务器2.负载均衡,避免请求拥塞,提升并发量3.反向代理,配合服务器集群提供了按照配置、服务地区的最有服务CDN都是反向代理的应用,但他们都不代表反向代理
1. 内容分发网络CDN, 通过反向代理把请求分发到世界各地的服务器去, 提升访问速度.2. 大公司不想暴露自己主服务器的位置.阿里云CDN配置------
https://help.aliyun.com/document_detail/27112.html1.到CDN控制台添加域名指定业务类型:(多选)全站加速、图片小文件、大文件、音视频点播、直播流媒体、移动加速指定源站类型:OSS域名 IP 源站域名2.域名管理:配置CNAME复制CNAME, 到DNS服务商控制台,添加一条CNAME记录################################ 关于反爬
1.拒绝请求:①User-Agent 反反爬: 准备User-Agent池②Host、Referer 反反爬:修改Header,复制浏览器访问时的内容③Cookie:反反爬:使用浏览器Cookie, 保持会话request使用session或直接使用selenium 需要登录:准备多个账号, 单独使用程序实现登录保存cookie, 爬虫程序读取cookie发送请求 不需要登录:单独使用程序实现登录保存cookie, 爬虫程序读取cookie发送请求④ip, 反反爬:使用ip池,使用代理2.数据干扰①伪装:如请求的明明是JSON,但返回image,再通过js解密数据,通常可以通过js代码得到解密方法;如果不能,只能爬取网页。②加密:如果不能轻易破解接口加密,只能通过爬取网页了。③自定义字体:通常用来干扰数字数据,下载字体下来,建立对应关系(手动或者fontmeta),并在爬虫程序中通过对应关系还原。############################## 数据提取过程, 由容易到复杂
############# 数据提取概述非结构化的数据:html等处理方法:正则表达式、xpath----->将提取的信息字符串构造Python类型结构化数据:json,xml等
处理方法:转化为python数据类型---->利用三方库直接转换为Python类型############# 具体的数据提取过程1.如果有ajax请求的JSON数据,可直接使用,通常这种数据有两种形式: ①pure JSON形式 ②带回调形式的JSON,通常是xxxx(JSON)的形式 可以通过使用正则进一步提取,有的可以直接将请求参数中的callback参数直接去掉,去掉之后直接返回pure JSON ③接口请求过来一段字符串,类似于字典格式,但是不是标准JSON,其中某个字段的值是JSON,可以通过re提取。 手机页面的数据经常使用JSON # re.compile(编译) # pattern.match(从头找一个 # pattern.search(找一个) # pattern.findall(找所有) # pattern.sub(替换 # 元字符“.”在默认模式下,匹配除换行符外的所有字符。在DOTALL模式下,匹配所有字符,包括换行符# 通过正则表达式匹配内涵段子的段子
# http://neihanshequ.com/ # 获取http://36kr.com/网站首页的所有新闻2.处理HTML页面:
①xpath: xpath 使用的lxml这个HTML/XML解析器实现,可以使用xpath的语法定位特定元素以及获取节点信息,常用语法 nodename: 节点名称 / 从根节点选取 // 当前节点 . 选取当前节点 .. 当前节点的父节点 @ 选取属性//title[@lang] 拥有属性lang的title元素
//title[@lang='eng'] 拥有值为eng的lang属性的title元素 ### 数组操作 /bookstore/book[1] 第一个book元素 /bookstore/book[last()] 最后一个book元素 /bookstore/book[position()<3] 前2个 /bookstore/book[price>35.00] # price标签文本值>35.00的book元素# 通配符
*任何元素节点 @*任何属性节点 node() 任何节点 相当于 * 和 @* 之和 ############## xpath 配合Python使用 from lxml import etree element = etree.HTML(html_str) script = element.xpath( '//div[@class="scontent" and @id="bofqi"]/script/text()') # 1、爬取糗事百科段子,页面的URL是 # http://www.qiushibaike.com/8hr/page/1 # 2、动手把上述的爬虫改为多线程爬虫3.动态页面处理,通常是根据登录用户的信息不同而提供不同的页面数据。
这类信息的爬取,首先要进行登录:登录过程分为以下几种: ⑴没有验证码: 情况一:登录字段没有加密或者非常容易看出加密手段:直接向表单提交地址发送请求(GitHub) 情况二:登录字段加密手段无法破解:selenium ⑵验证码刷新不改变: 主要原理是:获取验证码图片的接口,判断携带的cookie相同就返回相同的验证码 情况一:requests + 识图工具 情况二:使用selenium + 识图工具(如云打码)可直接传递验证码图片的url ⑶验证码刷新改变:(百度、钱多多 http://qian.sicent.com/Login/code.do ) 原理: 如果是动态获取的图片:ajax请求参数或者header会不同,这个可以通过对比参数查看(百度) 如果是header和参数都相同,两次请求的图片依然不同,那么能够将登陆操作和验证码关联的就只有ip、 session等信息了。大部分情况是使用了缓存服务器存储了session-id对应的code,但每次获取验证码都会更改它。 情况一:requests + 识图工具,不能直接使用图片的url,必须下载下来再上传识图 情况二:selenium + 识图工具,不能直接使用图片的url,必须下载下来再上传识图登录之后如何保持登录状态:
①requests直接使用session保持会话爬取 ②每次请求都携带登录之后传递的cookie,这样无法使用动态cookie, 有错误的情况,可能在某个页面请求之后cookie更改 ③使用Scrapy如何保持登录状态 下载器中间件CookiesMiddleware class scrapy.downloadermiddlewares.cookies.CookiesMiddleware 该中间件使得爬取需要cookie(例如使用session)的网站成为了可能。 其追踪了web server发送的cookie,并在之后的request中发送回去, 就如浏览器所做的那样。https://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/downloader-middleware.html#module-scrapy.downloadermiddlewares.cookies####### selenium使用和注意点
①基本使用 from selenium import webdriver driver = webdriver.Chrome() tag = driver.find_elements_by_xpath('') # list 或者 selenium.webdriver.remote.webelement.WebElement # driver.switch_to.frame() # driver.save_screenshot # driver.delete_cookie("CookieName") # driver.delete_all_cookies() 2.定位元素 find_element_by_id(返回一个) find_elements_by_xpath (返回一个列表) find_elements_by_link_text find_elements_by_partial_link_text find_elements_by_tag_name find_elements_by_class_name find_elements_by_css_selector 3.查看页面相关信息 # driver.page_source # driver.get_cookies() # driver.current_url 4.关闭 # driver.close() #退出当前页面 # driver.quit() #退出浏览器②获取登陆后的cookie
{cookie[‘name’]: cookie[‘value’] for cookie in driver.get_cookies()} ③页面等待 time.sleep(10) ######## # 1.豆瓣登陆 # 2.爬取斗鱼直播平台的所有房间信息 # https://www.douyu.com/directory/all4.几种常见的页面数据组织形式:
初始页 + ajax请求的JSON (有道词典) 初始页 + ajax请求的HTML片段 (百度贴吧首页下拉、京东搜索下拉) 初始页 + iframe (QQ邮箱登录,网易云音乐)################################ 数据清洗
数据的完整性----例如人的属性中缺少性别、籍贯、年龄等
数据的唯一性----例如不同来源的数据出现重复的情况数据的权威性----例如同一个指标出现多个来源的数据,且数值不一样数据的合法性----例如获取的数据与常识不符,年龄大于150岁数据的一致性----例如不同来源的不同指标,实际内涵是一样的,或是同一指标内涵不一致##### 技术实现
################################ 数据去重
url去重的场景:一次性爬取的数据,短期内同一url数据不会改变,可以通过url去重。长期爬取的数据,如果网站url本身是静态的,要注意筛选是否是伪静态url的网站,也可以直接通过url去重。其它数据去重场景:
①数据入库阶段去重:单个字段:比如url通过数据本身:建立联合索引(用户id,发帖日期,数据来源)②数据入库前去重:
①请求去重: 思路一:url去重(使用情况:url对应一条数据不会变的情况): 方法一:把url地址存在redis的集合中,后续根据url是否存在于redis的集合中,来判断数据是否抓过 方法二:布隆过滤器(适用于文章内容去重)是一种位操作,使用10个算法给每个url地址生成10个值,每个值代表一个位置,拿到一个url地址,先取10个位置的值,如果全为1,表示url地址存在,如果不全为1,表示url地址不存在,对应的往该位置置位1 思路二:url+headers去重(Scrapy-redis实现) ②数据去重: 思路一:Hash去重 1.选择数据中的关键字段,使用hash方法(md5,sha1,)把关键字段进行加密,生成字符串 2.把字符串存入redis的集合中 如果抓过,更新对应的数据,没有抓过,插入########################################## Scrapy 概况
1.「Scrapy Engine(引擎)
总指挥: 负责数据和信号的在不同模块间的传递scrapy已经实现,2.Scheduler(调度器)一个队列,存放引擎发过来的request请求scrapy 已经实现3.Downloader (下载器)下载把引擎发过来的requests请求,并返回给引擎scrapy 已经实现4.Spider (爬虫)处理引擎发来来的response,提取数据,提取url,并交给引擎需要手写5.Item Pipeline(管道)处理引擎传过来的数据,比如存储需要手写6.DownloaderMiddlewares(下载中间件)可以自定义的下载扩展,比如设置代理一般不用手写7.(SpiderMiddlewaresSpider(中间件)可以自定义requests请求和进行response过滤一般不用手写################ 请求和数据流向
调度器 ==> 获取request对象(遍历spider通过yield产生的Request对象) ===> 引擎 ===> 下载中间件 ==> 下载器下载器发送请求,获取响应 ==> 下载中间件 ==> 引擎 ==> 爬虫中间件 ==> 爬虫(交给设定的callback或默认初始请求对应的parse方法)爬虫 提取数据 ==> 交给引擎(遍历spider通过yield产生的dict、item对象) ==> 管道爬虫 url 组成发request对象 ==> 爬虫中间件 ==> 引擎 ==> 调度器################ Scrapy创建项目
1.创建一个项目scrapy startproject mySpider2.生成一个爬虫scrapy genspider tieba "tieba.baidu.com"3.提取数据,创建新请求,构建数据完善spider,使用xpath等方法4.保存数据pipeline中保存数据########################################## Scrapy 具体使用
项目结构├── justspider│ ├── __init__.py│ ├── items.py│ ├── middlewares.py│ ├── pipelines.py│ ├── settings.py│ └── spiders│ ├── __init__.py│ └── tieba.py└── scrapy.cfg############ settings文件分析
BOT_NAME = 'justspider'SPIDER_MODULES = ['justspider.spiders']NEWSPIDER_MODULE = 'justspider.spiders'### 其他主要配置1.User-Agent#USER_AGENT = 'justspider (+http://www.yourdomain.com)'2.是否遵守robots协议ROBOTSTXT_OBEY = True3.最大并发数#CONCURRENT_REQUESTS = 32 默认是164.自动限流配置 autothrottle, 下载延迟DOWNLOAD_DELAY = 35.是否使用cookie 默认是enable#COOKIES_ENABLED = False6.是否使用telnet控制台 默认是enable#TELNETCONSOLE_ENABLED = False7.重写默认的请求headers#DEFAULT_REQUEST_HEADERS = { # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',# 'Accept-Language': 'en',#}8.配置默认的爬虫中间件#SPIDER_MIDDLEWARES = { # 'justspider.middlewares.JustspiderSpiderMiddleware': 543,#}9.配置默认的下载中间件#DOWNLOADER_MIDDLEWARES = { # 'justspider.middlewares.JustspiderDownloaderMiddleware': 543,#}10.是否使用扩展#EXTENSIONS = { # 'scrapy.extensions.telnet.TelnetConsole': None,#}11.配置item使用的pipeline#ITEM_PIPELINES = { # 'justspider.pipelines.JustspiderPipeline': 300,#}12.自动限流的扩展,默认不使用① 打开扩展#AUTOTHROTTLE_ENABLED = True②初始下载延迟#AUTOTHROTTLE_START_DELAY = 5③最大的下载延迟#AUTOTHROTTLE_MAX_DELAY = 60④平均并行请求数#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0⑤是否为每个接收到的响应显示流量状态#AUTOTHROTTLE_DEBUG = False13.打开、配置HTTP缓存(默认禁用)
①是否打开配置#HTTPCACHE_ENABLED = True②缓存过期的时长 秒为单位#HTTPCACHE_EXPIRATION_SECS = 0③ 缓存的目录#HTTPCACHE_DIR = 'httpcache'④对哪些状态码的请求不缓存#HTTPCACHE_IGNORE_HTTP_CODES = []⑤实现缓存存储后端的类#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'######################## Spider重写,控制数据提取和继续请求的流程
启动爬虫start_url地址的响应===>交给parse函数传入response===>parse函数提取数据--------提取方法response的选择器(response是一个scrapy.http.response.html.HtmlResponse对象)①response.xpath()、②response.css()、③response.selector.re()--------拿到数据列表之后再次提取extract()提取数据,结果是列表,没有是空列表extract_first()提取结果中第一个字符串,没有就是None可以对获取的对象继续使用xpath,css和re--------继续操作情况一:需要构造新的请求(下一页,详情页等),指定callback,以及为response设置的meta yield scrapy.Request(item['detail_url'], callback=self.parse_detail, meta={'item': item}) # dont_filter:默认False,url会被去重;设置为True,可以在当前爬虫中对相同url请求多次 所有的callback和初始parse方法只有一个response参数,meta会作为response的属性传递情况二:产生数据,这个数据会通过引擎交给pipeline 产生的数据可以是Item(scrapy.Item子类)、dict类型,具体代码为: item = XXItem() item.name = xx yield item 其中XXItem类的实现为Item: class SuningItem(scrapy.Item): name = scrapy.Field()-------scrapy.Spider类一些其他注意点-----1方法start_requests的默认实现为def start_requests(self): ###...... for url in self.start_urls: yield Request(url, dont_filter=True)会遍历spider的start_urls这个list,并以此构建Request对象开始爬虫。有一些场景需要重写这个方法,比如在爬虫开始直接导入用户登录的cookie,这个时候不要忘记yield一个初始Request,否则爬虫无法开始。(Renren登录)-----2allowed_domains要添加可能爬取的所有url的domain,否则将无法爬取######################## pipeline重写,数据持久化或者消费处理可以不重写,那么产生的数据不会进行任何处理通常重写以下几个方法:
1.open_spider(self, spider) 爬虫开始的时候执行一次2.close_spider(self, spider) 爬虫结束的时候执行一次可以在open_spdier中建立和数据库的连接,在close_spide关闭和数据库的连接3.from_crawler(cls, crawler)如果重写了,这个类方法会在Crawler中创建pipeline实例的时候调用。它必须返回一个新的pipeline实例。Crawler对象提供了对所有Scrapy核心组件的访问权限,如settings何signals,对pipeline来说,这个方法是访问和hook Scrapy基础设施的手段。4.process_item(self, item, spider)每个item pipeline组件都需要调用该方法,这个方法必须返回一个具有数据的dict,或是 Item (或任何继承类)对象, 或是抛出 DropItem 异常,被丢弃的item将不会被之后的pipeline组件所处理。---例子:
import pymongoclass MongoPipeline(object):
collection_name = 'scrapy_items'
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_DATABASE', 'items') )def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri) self.db = self.client[self.mongo_db]def close_spider(self, spider):
self.client.close()def process_item(self, item, spider):
self.db[self.collection_name].insert(dict(item)) return item--------------另外,可以在数据入库之前添加去重的pipelinefrom scrapy.exceptions import DropItemclass DuplicatesPipeline(object):
def __init__(self):
self.ids_seen = set()def process_item(self, item, spider):
if item['id'] in self.ids_seen: raise DropItem("Duplicate item found: %s" % item) else: self.ids_seen.add(item['id']) return item----只需要在settings设置去重pipeline的权重小于入库pipeline即可。######################## 中间件 及 配置############### 下载中间件如果您想禁止内置的(在 DOWNLOADER_MIDDLEWARES_BASE 中设置并默认启用的)中间件,您必须在项目的 DOWNLOADER_MIDDLEWARES 设置中定义该中间件,并将其值赋为 None 。例如,如果您想要关闭user-agent中间件:DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.CustomDownloaderMiddleware': 543, 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,}##### 经常重写的下载中间件方法1.process_request(request, spider) 处理请求返回 None 、返回一个 Response 对象、返回一个 Request 对象或raise IgnoreRequest2.process_response(request, response, spider)返回一个 Response 对象、 返回一个 Request 对象或raise一个 IgnoreRequest 异常。3.process_exception(request, exception, spider)当下载处理器(download handler)或 process_request() (下载中间件)抛出异常(包括 IgnoreRequest 异常)时, Scrapy调用 process_exception() 。process_exception() 应该返回以下之一: 返回 None 、 一个 Response 对象、或者一个 Request 对象。如果其返回 None,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception() 方法,直到所有中间件都被调用完毕,则调用默认的异常处理。如果其返回一个 Response 对象,则已安装的中间件链的 process_response() 方法被调用。Scrapy将不会调用任何其他中间件的 process_exception() 方法。如果其返回一个 Request 对象, 则返回的request将会被重新调用下载。这将停止中间件的 process_exception() 方法执行,就如返回一个response的那样----- 常用的内置下载中间件CookiesMiddleware 默认使用,可以通过settings的COOKIES_ENABLED设置是否禁用DefaultHeadersMiddleware 指定request的headerDownloadTimeoutMiddleware 依据settings中的DOWNLOAD_TIMEOUT设置超时HttpAuthMiddleware 该中间件完成某些使用 Basic access authentication (或者叫HTTP认证)的spider生成的请求的认证过程,HttpCacheMiddleware 该中间件为所有HTTP request及response提供了底层(low-level)缓存支持。 其由cache存储后端及cache策略组成。HttpCompressionMiddleware 压缩支持 通过settings中的设置COMPRESSION_ENABLED,默认TrueHttpProxyMiddleware 代理支持,通过创建Request时传递代理ip+portRedirectMiddleware 该中间件根据response的状态处理重定向的request。MetaRefreshMiddleware 该中间件根据meta-refresh html标签处理request重定向。RetryMiddleware 该中间件将重试可能由于临时的问题,例如连接超时或者HTTP 500错误导致失败的页面 settings:RETRY_TIMES RETRY_HTTP_CODESRobotsTxtMiddleware 该中间件过滤所有robots.txt eclusion standard中禁止的request。settings: ROBOTSTXT_OBEYUserAgentMiddleware 用于**覆盖**spider的默认user agent的中间件。AjaxCrawlMiddleware 根据meta-fragment html标签查找 ‘AJAX可爬取’ 页面的中间件############### Spider中间件---- 经常重写的Spider中间件1.process_spider_input(response, spider)当response通过spider中间件时,该方法被调用,处理该response。process_spider_input() 应该返回 None 或者抛出一个异常。2.process_spider_output(response, result, spider)当Spider处理response返回result时,该方法被调用。process_spider_output() 必须返回包含 Request 、dict 或 Item 对象的可迭代对象3.process_spider_exception(response, exception, spider)当spider或(其他spider中间件的) process_spider_input() 跑出异常时, 该方法被调用。4.process_start_requests(start_requests, spider)该方法以spider 启动的request为参数被调用,执行的过程类似于 process_spider_output() ,只不过其没有相关联的response并且必须返回request(不是item)。---- 常用的内置Spider中间件1.DepthMiddleware DepthMiddleware是一个用于追踪每个Request在被爬取的网站的深度的中间件。其可以用来限制爬取深度的最大深度或类似的事情。 settings:DEPTH_LIMIT2.HttpErrorMiddleware过滤出所有失败(错误)的HTTP response,因此spider不需要处理这些request。处理这些request意味着消耗更多资源,并且使得spider逻辑更为复杂。3.OffsiteMiddleware过滤出所有URL不由该spider负责的Request。该中间件过滤出所有主机名不在spider属性 allowed_domains 的request。4.RefererMiddleware根据生成Request的Response的URL来设置Request Referer 字段。5.UrlLengthMiddleware过滤出URL长度比URLLENGTH_LIMIT的request。#################### Scrapy处理表单请求,如何构造Request用于提交表单(GitHub为例)1.带上cookie直接请求 1.构造cookie字典 2.添加到scrapy.Request(cookies=cookies) 3.cookie字符串不能够放到headers,没有效果2.scrapy.FormReuquest代码:scrapy.FormReuquest(url,formdata,callback)其中url是要post提交的url地址,formdata,请求体3.scrapy.FormReuquest.from_response(response,formdata,callback)根据当前页面的response自动选取表单,formdata只需填入Web页面要填写的字段即可。注意:如果有多个表单,可以使用formxpath或formcss这两个参数选取###################### CrawlSpider
1.创建的命令 scrapy genspider -t crawl spdier_name allow_domain2.生成的CrawSpider类继承自Spider,增加类属性rules,是一个Rule类型的元组,parse方法变为parse_item特点: 1.根据Rule元组定义的规则寻找url,构建请求,如此反复,通过最终的callback做数据提取 2.callback依然可以yield新的Request,继续使用Rule元组继续筛选url,缺点:crawlspider的解析函数中间不能传递数据,经常在详情页能够获取全部数据的时候使用crawlspider, parse_item就是为解析最终拿数据的页面准备的3.Rule构建,有几个重要的参数:①link_extractor链接提取器LinkExtractor(allow=r'/p/\d{10}') # restrict_xpaths、restrict_css都是元组类型②callback,回调函数,可用于yield Request或Item③follow,根据当前提取规则得到response之后,是否继续使用rules下的Rule继续提取###################### logger
scrapy中使用日志:settings:LOG_FILE = "./log.log" # 日志存储的位置,终端不会再展示日志scrapy中使用日志输出信息:方式①: 1.import logging 2. logger = logging.getLogger(__name__) # __name__ 用于format,通常显示为提示[__name__] 能够显示当前的输出来自于那个文件 3.logger.warning("....")方式②: 如果当前类是Spider,直接使用 self.logger.warning('....')可以在settings里面通过LOG_LEVEL设置日志级别,还可以配置其他设置:LOG_FILE、LOG_ENABLED、LOG_ENCODING、LOG_FORMAT、LOG_DATEFORMAT、LOG_STDOUT########### 一般项目使用系统的logging模块1.import logging2.logging.BasicConfig({}) 配置日志数据的样式3.实例化logger = logging.getLogger(__name__)4.导入logger,使用logger.info("") <=> logging.log(logging.INFO, "")################################# Scrapy Shell怎么使用 scrapy shell url能干什么 观察scarpy中各种对象的方法和属性 response.text 网页的html字符串str response.body 获取网页的响应的bytes resposne.request.url resposne.url response.request.url 测试xpath############ Scrapy部署############ telnet终端############ feed export########################################## Scrapy-Redisscarpy_redis是一个基于redis的scarpy的组件主要作用: 1.持久化`待爬取`的request对象,经过序列化存入redis中,可以实现暂停、断点续爬、分布式爬虫 2.已经看到过的request的指纹存入redis,作为去重的依据工作流程: 1.调度器的带爬取的request对象,已经看到过的request的指纹保存在redis中 2.itempipeline的数据默认保存在redis中和scrapy的区别: settings多了5行 1.指定了调度器的类 2.指定了去重的类 3.指定了redis_urlDUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'SCHEDULER = 'scrapy_redis.scheduler.Scheduler'SCHEDULER_PERSIST = TrueREDIS_URL = 'redis://192.168.199.131:6379/5'ITEM_PIPELINES = { 'book.pipelines.BookPipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400,}###### scrapy_reids如何实现--概述
1.去重 1.hashlib.sha1() 2.加密了request对象的请求方法,请求体,url 3.生成16进制字符串作为当前request的指纹 4.把指纹存在redis的集合中,后续的request的指纹判断是否存在该集合中 2.待爬取的request对象入队 1.url在start_urls中的时候 2.url地址之前没有见过的时候 3.dont_fileter=Ture,表示不过滤,会入队################################# 源码分析---------------计算指纹的方法:
from scrapy.utils.request import request_fingerprint这个函数默认会忽略cookie和其他header,如果想让一些header参与指纹计算使用include_headers参数,它是要包含的请求头列表。最终参数与计算hash的值按顺序为:请求method,url,body,include_header和对应的value,使用的是sha1算法----------------指纹过滤器RFPDupeFilter (Request Fingerprint duplicates filter) 请求指纹重复过滤器### scrapy默认的指纹去重过滤器
scrapy.dupefilters.RFPDupeFilter 继承自BaseDupeFilter1.对象属性fingerprints是set()类型,用于在内存中存储,已经访问过的request指纹2.如果对象实例化时传入了path参数,那么会在path路径下的生成requests.seen用于持久化存储,只需在settings指定DUPEFILTER_DEBUG即可,那么会使用下面这个类方法实例化这个类def from_settings(cls, settings): debug = settings.getbool('DUPEFILTER_DEBUG') return cls(job_dir(settings), debug)# job_dir 实现为def job_dir(settings): path = settings['JOBDIR'] if path and not os.path.exists(path): os.makedirs(path) return path可知:requests.seen 最终依赖于settings中JOBDIR这个配置,存储在JOBDIR指定的目录下3.是否已经爬取过的判断:def request_seen(self, request): fp = self.request_fingerprint(request) if fp in self.fingerprints: return True self.fingerprints.add(fp) if self.file: self.file.write(fp + os.linesep)可知:直接返回内存缓存fingerprints这个set里面是否有请求的指纹### scrapy-redis实现的指纹去重过滤器
scrapy_redis.dupefilter.RFPDupeFilter 继承自scrapy默认的BaseDupeFilter1.key属性:用于存储redis存储该次请求的指纹的key,dupefilter:%(timestamp)s % {'timestamp': int(time.time())}server属性:对redis的链接2.from_settings实例化对象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)可知:通过settings的配置创建redis连接3.是否已经爬取过的判断:def request_seen(self, request): fp = self.request_fingerprint(request) added = self.server.sadd(self.key, fp) return added == 0向redis插入指纹失败,则已经爬取过----------------将要爬取的request放到队列中----scrapy默认入队实现 scrapy.core.scheduler.Schedulerdef enqueue_request(self, request): if not request.dont_filter and self.df.request_seen(request): self.df.log(request, self.spider) return False dqok = self._dqpush(request) if dqok: self.stats.inc_value('scheduler/enqueued/disk', spider=self.spider) else: self._mqpush(request) self.stats.inc_value('scheduler/enqueued/memory', spider=self.spider) self.stats.inc_value('scheduler/enqueued', spider=self.spider) return True----scrapy-redis入队实现 scrapy_redis.scheduler.Schedulerdef enqueue_request(self, request): if not request.dont_filter and self.df.request_seen(request): self.df.log(request, self.spider) return False if self.stats: self.stats.inc_value('scheduler/enqueued/redis', spider=self.spider) self.queue.push(request) return True思路完全一致:
如果 request.dont_filter为True,或者,指纹过滤器验证没有重复,直接入队列########################################## 其他知识点
代理IP的类型:(透明、匿名、高匿)①透明代理的意思是客户端根本不需要知道有代理服务器的存在,但是它传送的仍然是真实的IP。你要想隐藏的话,不要用这个。②普通匿名代理能隐藏客户机的真实IP,但会改变我们的请求信息,服务器端有可能会认为我们使用了代理。不过使用此种代理时,虽然被访问的网站不能知道你的ip地址,但仍然可以知道你在使用代理,当然某些能够侦测ip的网页仍然可以查到你的ip。③高匿名代理不改变客户机的请求,这样在服务器看来就像有个真正的客户浏览器在访问它,这时客户的真实IP是隐藏的,服务器端不会认为我们使用了代理。一些其他情况的反反爬:http://www.shenjianshou.cn/blog/?p=292