今日內容, 爬蟲及Flask框架入門
阿新 • • 發佈:2022-12-09
-
加代理,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)