1. 程式人生 > 其它 >今日內容, 爬蟲及Flask框架入門

今日內容, 爬蟲及Flask框架入門

  • 加代理,cookie,header,加入selenium

加代理

# 在爬蟲中介軟體中
   def get_proxy(self):
        import requests
        res=requests.get('http://192.168.1.143:5010/get/').json()
        if res.get('https'):
            return 'https://'+res.get('proxy')
        else:
            return 'http://' + res.get('proxy')
    def process_request(self, request, spider):
        # request 就是咱們在解析中yiedl的Request的物件
        # spider 就是爬蟲物件
        ####1 加代理--->配置檔案配置
        pro=self.get_proxy()
        request.meta['proxy'] = pro
        # 下載超時時間 download_timeout
        print(request.meta)
        return None
    
    
    
 ### 重點:如果中介軟體中出了異常,會呼叫中介軟體的process_exception
		-記錄日誌
    	-把當前爬取的request物件,return出去,會被引擎重新放回排程器,等待下次執行

加cookie,修改請求頭,隨機生成UserAgent

### 1  加cookie
print(request.cookies)
request.cookies['name']='pyy'
request.cookies=從cookie池中取出來的cookie



###2 修改請求頭
# print(request.headers)
# request.headers['referer']='http://127.0.0.1:8000'



### 3 請求頭中有user_agent,這個我們想每次都隨機一個,而不是使用寫死的
		-fake_useragent模組,可以隨機生成user-aget
	    from fake_useragent import UserAgent
        ua = UserAgent()
        print(ua.ie)   #隨機列印ie瀏覽器任意版本
        print(ua.firefox) #隨機列印firefox瀏覽器任意版本
        print(ua.chrome)  #隨機列印chrome瀏覽器任意版本
        print(ua.random)  #隨機列印任意廠家的瀏覽器 
        
        
        from fake_useragent import UserAgent
        ua = UserAgent()
        request.headers['User-Agent'] = ua.random
        print(request.headers)

整合selenium

# 使用scrapy,爬取網頁,本質跟使用requests模組是一樣的,模擬傳送http請求,有的網站,頁面可能不是一次http請求返回的所有資料,會執行js,再發ajax,得到的所有資料,所有有的網頁,咱們可以使用selenium去爬取



# 字串和bytes相互轉化
字串轉bytes  
	-方式一:lqz'.encode(encoding='utf-8')
    -方式二:bytest('字串',encoding='utf-8')
bytes轉字串 
	-方式一:b'lqz'.decode(encoding='utf-8')
    -方式二:str('bytes格式',encoding='utf-8')
    
    
    
    
# 使用步驟:只用selenium爬取cnblogs的首頁和下一頁  (一旦使用selenium速度就慢)
	-第一步:在爬蟲類的類屬性中寫
    class CnblogsSpider(scrapy.Spider):
        bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    -第二步:在中介軟體中使用selenium爬取
       if request.meta.get('user_selenium'): #有的用,有的不用
            spider.bro.get(request.url)
            from scrapy.http import HtmlResponse
            response = HtmlResponse(url=request.url, body=bytes(spider.bro.page_source, encoding='utf-8'))
            return response
        else:
            return None
        
    -第三步:在爬蟲類中關閉
     def close(self, spider, reason):
        self.bro.close()
  • 去重規則原始碼分析(布隆過濾器)

# scrapy 可以去重
# 研究去重的底層實現是如何實現的
	-我們想的話:把爬取過的網址,放在集合中,下次爬取之前,先看集合中有沒有,如果有,就不爬了
    -原始碼在哪去的重?排程器---》排程器原始碼

# 原始碼 排程器的類:from scrapy.core.scheduler import Scheduler
	# 這個方法如果return True表示這個request要爬取,如果return表示這個網址就不爬了(已經爬過了)
    def enqueue_request(self, request: Request) -> bool:
        # request當次要爬取的地址物件
        if not request.dont_filter and self.df.request_seen(request):
            # 有的請情況,在爬蟲中解析出來的網址,不想爬了,就就可以指定
            # yield Request(url=url, callback=self.detail_parse, meta={'item': item},dont_filter=True)
            # 如果符合這個條件,表示這個網址已經爬過了 
            return False
        return True
    
    -self.df 是去重類的物件 RFPDupeFilter
    -在配置檔案中如果配置了:DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'表示,使用它作為去重類,按照它的規則做去重
    -RFPDupeFilter的request_seen
       def request_seen(self, request: Request) -> bool:
        # request_fingerprint 生成指紋
        fp = self.request_fingerprint(request) #request當次要爬取的地址物件
        #判斷 fp 在不在集合中,如果在,return True
        if fp in self.fingerprints:
            return True
        #如果不在,加入到集合,return False
        self.fingerprints.add(fp)
        return False
    
    
    -生成指紋,指紋是什麼?
    	-www.cnblogs.com?name=lqz&age=19
        -www.cnblogs.com?age=19&name=lqz
        -上面的兩種地址生成的指紋是一樣的
        # 測試指紋
        from scrapy.utils.request import RequestFingerprinter
        from scrapy import Request

        fingerprinter = RequestFingerprinter()
        request1 = Request(url='http://www.cnblogs.com?name=lqz&age=20')
        request2 = Request(url='http://www.cnblogs.com?age=19&name=lqz')

        res1 = fingerprinter.fingerprint(request1).hex()
        res2 = fingerprinter.fingerprint(request2).hex()
        print(res1)
        print(res2)
        

        
 # 總結:scrapy的去重規則
	-根據配置的去重類RFPDupeFilter的request_seen方法,如果返回True,就不爬了,如果返回False就爬
    -後期咱們可以使用自己定義的去重類,實現去重
    
    
 # 更小記憶體實現去重
	-如果是集合:存的資料庫越多,佔記憶體空間越大,如果資料量特別大,可以使用布隆過濾器實現去重

 # 布隆過濾器:https://zhuanlan.zhihu.com/p/94668361
	#bloomfilter:是一個通過多雜湊函式對映到一張表的資料結構,能夠快速的判斷一個元素在一個集合內是否存在,具有很好的空間和時間效率。(典型例子,爬蟲url去重)

	# 原理: BloomFilter 會開闢一個m位的bitArray(位陣列),開始所有資料全部置 0 。當一個元素(www.baidu.com)過來時,能過多個雜湊函式(h1,h2,h3....)計算不同的在雜湊值,並通過雜湊值找到對應的bitArray下標處,將裡面的值 0 置為 1 。

    
    
    
   
# Python中使用布隆過濾器
# 測試布隆過濾器
# 可以自動擴容指定錯誤率,底層陣列如果大於了錯誤率會自動擴容
# from pybloom_live import ScalableBloomFilter
# bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
# url = "www.cnblogs.com"
# url2 = "www.liuqingzheng.top"
# bloom.add(url)
# bloom.add(url2)
# print(url in bloom)
# print(url2 in bloom)

from pybloom_live import BloomFilter

bf = BloomFilter(capacity=10)
url = 'www.baidu.com'
bf.add(url)
bf.add('aaaa')
bf.add('ggg')
bf.add('deww')
bf.add('aerqaaa')
bf.add('ae2rqaaa')
bf.add('aerweqaaa')
bf.add('aerwewqaaa')
bf.add('aerereweqaaa')
bf.add('we')


print(url in bf)
print("wa" in bf)


# 重寫scrapy的過濾類
  • scrapy-redis實現分散式爬蟲

# 什麼是分散式爬蟲
	-原來使用一臺機器爬取cnblogs整站
    -現在想使用3臺機器爬取cnblogs整站
# 如果變成分散式,面臨的問題
	-1 去重集合,我們要使用同一個----》redis集合
    -2 多臺機器使用同一個排程器:Scheduler,排隊爬取,使用同一個佇列
    
    
# scrapy-redis 已經解決這個問題了,我只需要在我們單機基礎上,改動一點,就變成了分散式爬蟲
# 使用步驟
	第一步:安裝scrapy-redis  ---》pip3 install scrapy-redis
    第二步:改造爬蟲類
    from scrapy_redis.spiders import RedisSpider
    class CnblogSpider(RedisSpider):
        name = 'cnblog_redis'
        allowed_domains = ['cnblogs.com']
        # 寫一個key:redis列表的key,起始爬取的地址
        redis_key = 'myspider:start_urls'
        
    第三步:配置檔案配置
    # 分散式爬蟲配置
    # 去重規則使用redis
    REDIS_HOST = 'localhost'                            # 主機名
    REDIS_PORT = 6379                                   # 埠
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" #看了原始碼
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"   # 先進先出:佇列,先進後出:棧
    # 持久化:檔案,mysql,redis
    ITEM_PIPELINES = {
       'cnblogs.pipelines.CnblogsFilePipeline': 300,
       'cnblogs.pipelines.CnblogsMysqlPipeline': 100,
       'scrapy_redis.pipelines.RedisPipeline': 400,  #簡單看
    }
    第四步:在多臺機器上啟動scrapy專案,在一臺機器起了多個scrapy爬蟲程序,就相當於多臺機器
    	-程序,執行緒,協程。。。
        -程序間資料隔離 IPC
    
    第五步:把起始爬取的地址放到redis的列表中
    lpush mycrawler:start_urls http://www.cnblogs.com/
    
  • Flask介紹

# 目前python界,比較出名的web框架
	-django:大而全,web開發用的東西,它都有
    -Flask:小而精,只能完成請求與響應,session,cache,orm,admin。。。統統沒有
    	-很多第三方框架,flask完全可以變成django
    -----同步框架----- django從3.x 改成了非同步框架
    
    ----以下是非同步框架--------
    -Tornado:非常少了,ptyhon2.x上,公司裡用的多一些
    -Sanic : python 3.6 及以上
    -FastAPI
# Flask
Flask是一個基於Python開發並且依賴jinja2模板(模板語言)和Werkzeug WSGI服務的一個微型框架,對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,然後觸發Flask框架,開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給使用者,如果要返回給使用者複雜的內容時,需要藉助jinja2模板來實現對模板的處理,即:將模板和資料進行渲染,將渲染後的字串返回給使用者瀏覽器。

“微”(micro) 並不表示你需要把整個 Web 應用塞進單個 Python 檔案(雖然確實可以 ),也不意味著 Flask 在功能上有所欠缺。微框架中的“微”意味著 Flask 旨在保持核心簡單而易於擴充套件。Flask 不會替你做出太多決策——比如使用何種資料庫。而那些 Flask 所選擇的——比如使用何種模板引擎——則很容易替換。除此之外的一切都由可由你掌握。如此,Flask 可以與您珠聯璧合。

預設情況下,Flask 不包含資料庫抽象層、表單驗證,或是其它任何已有多種庫可以勝任的功能。然而,Flask 支援用擴充套件來給應用新增這些功能,如同是 Flask 本身實現的一樣。眾多的擴充套件提供了資料庫整合、表單驗證、上傳處理、各種各樣的開放認證技術等功能。Flask 也許是“微小”的,但它已準備好在需求繁雜的生產環境中投入使用
  • Flask快速使用

# pip3 install flask     # 最新2.2.2


from flask import Flask

app = Flask(__name__)

# 註冊路由
@app.route('/index')
def index():
    return '你看到我了'


if __name__ == '__main__':
    app.run(host='127.0.0.1',port=8080)