spider

############################ requests的使用
import requests
# 發送請求, 使用的函數按照HTTP訪問的方法,主要:get、post、put、delete、head、options,patch
# 最終都是調用了request函數,這個函數的原型是: def request(method, url, **kwargs):
# kwargs能夠填入:headers, params, data, json, proxies
response = requests.get('https://www.baidu.com')
# 返回的對象是 requests.models.Response
# response對象一般有如下幾個方法:
response.status_code # 狀態碼
response.request.headers # 對應請求的請求頭
response.headers # 響應頭
response.encoding # 默認解碼方式,修改以前默認是ISO-8859-1
response.text # 按照response.encoding 解碼的str
response.content # bytes
print(response.content.decode('utf-8'))
######## 保持會話進行爬取,每次請求都會帶上cookie
session = requests.session() # requests.sessions.Session類型
response = session.get(url,headers)
####### 小技巧
cookie轉爲字典 requests.util.dict_from_cookiejar
https請求不進行ssl驗證 requests.get('https://www.baidu.com', verify=False)
設置超時 requests.get(url, 1)css

################################### 正向代理和反向代理
代理爲誰服務就隱藏誰的信息。html

正向代理:代理爲客戶端服務,產生的結果是:服務端不知道真正的客戶端
反向代理:代理爲服務端服務,並對服務集羣作負載均衡,將請求均衡分配到空閒服務器,
產生的結果是:客戶端不知道真正的服務器。node

nginx服務器的做用
1.C-S架構模式解耦,他是一箇中間層,能夠隨時更改綁定的服務器
2.負載均衡,避免請求擁塞,提高併發量
3.反向代理,配合服務器集羣提供了按照配置、服務地區的最有服務python

CDN都是反向代理的應用,但他們都不表明反向代理
1. 內容分發網絡CDN, 經過反向代理把請求分發到世界各地的服務器去, 提高訪問速度.
2. 大公司不想暴露本身主服務器的位置.nginx

阿里雲CDN配置------
https://help.aliyun.com/document_detail/27112.html
1.到CDN控制檯添加域名
指定業務類型:(多選)全站加速、圖片小文件、大文件、音視頻點播、直播流媒體、移動加速
指定源站類型:OSS域名 IP 源站域名
2.域名管理:配置CNAME
複製CNAME, 到DNS服務商控制檯,添加一條CNAME記錄web

################################ 關於反爬
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),並在爬蟲程序中經過對應關係還原。ajax

############################## 數據提取過程, 由容易到複雜
############# 數據提取概述
非結構化的數據: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模式下,匹配全部字符,包括換行符redis

# 經過正則表達式匹配內涵段子的段子
#  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()')
# 一、爬取糗事百科段子,頁面的URL是
#  http://www.qiushibaike.com/8hr/page/1
# 二、動手把上述的爬蟲改成多線程爬蟲

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/all

4.幾種常見的頁面數據組織形式:
初始頁 + 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 mySpider
2.生成一個爬蟲
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 = True
3.最大併發數
#CONCURRENT_REQUESTS = 32 默認是16
4.自動限流配置 autothrottle, 下載延遲
DOWNLOAD_DELAY = 3
5.是否使用cookie 默認是enable
#COOKIES_ENABLED = False
6.是否使用telnet控制檯 默認是enable
#TELNETCONSOLE_ENABLED = False
7.重寫默認的請求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 = False

13.打開、配置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登陸)
-----2
allowed_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 pymongo

class 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
--------------另外,能夠在數據入庫以前添加去重的pipeline
from scrapy.exceptions import DropItem

class 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 IgnoreRequest
2.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的header
DownloadTimeoutMiddleware 依據settings中的DOWNLOAD_TIMEOUT設置超時
HttpAuthMiddleware 該中間件完成某些使用 Basic access authentication (或者叫HTTP認證)的spider生成的請求的認證過程,
HttpCacheMiddleware 該中間件爲全部HTTP request及response提供了底層(low-level)緩存支持。 其由cache存儲後端及cache策略組成。
HttpCompressionMiddleware 壓縮支持 經過settings中的設置COMPRESSION_ENABLED,默認True
HttpProxyMiddleware 代理支持,經過建立Request時傳遞代理ip+port
RedirectMiddleware 該中間件根據response的狀態處理重定向的request。
MetaRefreshMiddleware 該中間件根據meta-refresh html標籤處理request重定向。
RetryMiddleware 該中間件將重試可能因爲臨時的問題,例如鏈接超時或者HTTP 500錯誤致使失敗的頁面 settings:RETRY_TIMES RETRY_HTTP_CODES
RobotsTxtMiddleware 該中間件過濾全部robots.txt eclusion standard中禁止的request。settings: ROBOTSTXT_OBEY
UserAgentMiddleware 用於**覆蓋**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_LIMIT
2.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_domain
2.生成的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 logging
2.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-Redis
scarpy_redis是一個基於redis的scarpy的組件
主要做用:
1.持久化`待爬取`的request對象,通過序列化存入redis中,能夠實現暫停、斷點續爬、分佈式爬蟲
2.已經看到過的request的指紋存入redis,做爲去重的依據
工做流程:
1.調度器的帶爬取的request對象,已經看到過的request的指紋保存在redis中
2.itempipeline的數據默認保存在redis中
和scrapy的區別:
settings多了5行
1.指定了調度器的類
2.指定了去重的類
3.指定了redis_url
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
SCHEDULER_PERSIST = True
REDIS_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 繼承自BaseDupeFilter
1.對象屬性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默認的BaseDupeFilter
1.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.Scheduler
def 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.Scheduler
def 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

相關文章
相關標籤/搜索