摘要:一般用進程池維護,的設(shè)為數(shù)量。多線程爬蟲多線程版本可以在單進程下進行異步采集,但線程間的切換開銷也會隨著線程數(shù)的增大而增大。異步協(xié)程爬蟲引入了異步協(xié)程語法。
Welcome to the D-age
對于網(wǎng)絡(luò)上的公開數(shù)據(jù),理論上只要由服務(wù)端發(fā)送到前端都可以由爬蟲獲取到。但是Data-age時代的到來,數(shù)據(jù)是新的黃金,毫不夸張的說,數(shù)據(jù)是未來的一切。基于統(tǒng)計學(xué)數(shù)學(xué)模型的各種人工智能的出現(xiàn),離不開數(shù)據(jù)驅(qū)動。數(shù)據(jù)采集、清洗是最末端的技術(shù)成本,網(wǎng)絡(luò)爬蟲也是基礎(chǔ)采集腳本。但是有幾個值得關(guān)注的是:
對于實時變化的網(wǎng)絡(luò)環(huán)境,爬蟲的持續(xù)有效性如何保證
數(shù)據(jù)采集、清洗規(guī)則的適用范圍
數(shù)據(jù)采集的時間與質(zhì)量--效率
爬與反爬的恩怨
爬蟲的法律界限
法律的邊界,技術(shù)無罪對于上面幾個關(guān)注點,我最先關(guān)注的便是爬蟲的法律界限 ,我曾經(jīng)咨詢過一個律師:
Q: 老師,我如果用爬蟲爬取今日頭條這種類型網(wǎng)站的千萬級公開數(shù)據(jù),算不算違法呢?
A: 爬取的公開數(shù)據(jù)不得進行非法使用或者商業(yè)利用
簡單的概括便是爬蟲爬取的數(shù)據(jù)如果進行商業(yè)出售或者有獲利的使用,便構(gòu)成了“非法使用”。而一般的爬蟲程序并不違法,其實這是從法律專業(yè)的一方來解讀,如果加上技術(shù)層面的維度,那么應(yīng)該從這幾方面考慮:
爬取的數(shù)據(jù)量
爬取數(shù)據(jù)的類型(數(shù)據(jù)具有巨大的商業(yè)價值,未經(jīng)對方許可,任何人不得非法獲取其數(shù)據(jù)并用于經(jīng)營行為)
爬取的數(shù)據(jù)用途 (同行競爭?出售?經(jīng)營?分析?實驗?...)
是否遵循網(wǎng)站的robots.txt 即 機器人協(xié)議
爬取行為是否會對對方網(wǎng)站造成不能承受的損失(大量的爬取請求會把一個小型網(wǎng)站拖垮)
其實爬蟲構(gòu)成犯罪的案例是開始增多的,相關(guān)新聞:
當(dāng)爬蟲遇上法律會有什么風(fēng)險?
程序員爬蟲竟構(gòu)成犯罪?
爬蟲相關(guān)法律知識
如果你的上級或公司要求你爬取某些網(wǎng)站的大量公開數(shù)據(jù),你會怎么辦呢?可以參考第2條新聞。法律矛盾點關(guān)鍵在于前面考慮的前三點,如果是個人隱私數(shù)據(jù),是不能爬取的,如果是非公開數(shù)據(jù),是不能爬取的,而對于其他大量的公開數(shù)據(jù)爬取,看人家查不查的到你,要不要起訴你。技術(shù)在你的手上,非法與否在于你怎么去用。最好的爬取道德原則是:
減少并發(fā)請求
延長請求間隔
不進行公開出售數(shù)據(jù)
遵循網(wǎng)站 robots協(xié)議
當(dāng)然,反爬最有效的便(目的均在于攔截爬蟲進入網(wǎng)站數(shù)據(jù)范圍)是:
工欲善其事要求用戶密碼+驗證碼
加密數(shù)據(jù)
js混淆
css混淆
針對IP請求頻率封鎖
針對cookie、session單個賬戶請求頻率封鎖單日請求次數(shù)
對關(guān)鍵數(shù)據(jù)進行拆分合并
對爬蟲投毒(返回假數(shù)據(jù))
完善robots.txt
識別點擊九宮圖中沒有包含xxx的圖片等(終極驗證碼)
設(shè)置黑白名單、IP用戶組等
針對網(wǎng)站的公開數(shù)據(jù)進行爬取,我們一般都要先對網(wǎng)站數(shù)據(jù)進行分析,定位,以確定其采集規(guī)則,如果網(wǎng)站設(shè)置了訪問權(quán)限,那么便不屬于我們的爬蟲采集范圍了:)
分析好采集規(guī)則,寫好了采集數(shù)據(jù)持久化(存入數(shù)據(jù)庫、導(dǎo)出為word、excel、csv、下載等)的相關(guān)代碼,整個爬蟲運行正常。那么怎樣才能提高采集速度呢?
多進程采集
多線程采集
異步協(xié)程采集
多進程 + 多線程采集
多進程 + 異步協(xié)程采集
分布式采集
異步爬蟲是同步爬蟲的升級版,在同步爬蟲中,無論你怎么優(yōu)化代碼,同步IO的阻塞是最大的致命傷。同步阻塞會讓采集任務(wù)一個個排著長隊領(lǐng)票等待執(zhí)行。而異步采集不會造成IO阻塞,充分利用了IO阻塞任務(wù)的等待時間去執(zhí)行其他任務(wù)。
在IO 模型中,只有IO多路復(fù)用(I/O multiplexing){在內(nèi)核處理IO請求結(jié)果為可讀或可寫時調(diào)用回調(diào)函數(shù)} 不阻塞 “內(nèi)核拷貝IO請求數(shù)據(jù)到用戶空間”這個過程,實現(xiàn)異步IO操作。同步爬蟲
一般的同步爬蟲,我們可以寫一個,(以爬取圖片網(wǎng)站圖片為例),我們來看看其下載該網(wǎng)址所有圖片所花費的時間:
以下代碼為后面多個例程的共同代碼:
#coding:utf-8 import time from lxml import etree import urllib.request as request #目標(biāo)網(wǎng)址 url = "http://www.quanjing.com/creative/SearchCreative.aspx?id=7" def download_one_pic(url:str,name:str,suffix:str="jpg"): #下載單張圖片 path = ".".join([name,suffix]) response = request.urlopen(url) wb_data = response.read() with open(path,"wb") as f: f.write(wb_data) def download_many_pic(urls:list): #下載多張圖片 start = time.time() for i in urls: ts = str(int(time.time() * 1000)) download_one_pic(i, ts) end = time.time() print(u"下載完成,%d張圖片,耗時:%.2fs" % (len(urls), (end - start))) def get_pic_urls(url:str)->list: #獲取頁面所有圖片鏈接 response = request.urlopen(url) wb_data = response.read() html = etree.HTML(wb_data) pic_urls = html.xpath("http://a[@class="item lazy"]/img/@src") return pic_urls def allot(pic_urls:list,n:int)->list: #根據(jù)給定的組數(shù),分配url給每一組 _len = len(pic_urls) base = int(_len / n) remainder = _len % n groups = [pic_urls[i * base:(i + 1) * base] for i in range(n)] remaind_group = pic_urls[n * base:] for i in range(remainder): groups[i].append(remaind_group[i]) return [i for i in groups if i]
同步爬蟲:
def crawler(): #同步下載 pic_urls = get_pic_urls(url) download_many_pic(pic_urls)
執(zhí)行同步爬蟲,
crawler()
輸出(時間可能不一樣,取決于你的網(wǎng)速):
下載完成,196張圖片,耗時:49.04s
在同一個網(wǎng)絡(luò)環(huán)境下,排除網(wǎng)速時好時壞,可以下載多幾次取平均下載時間,在我的網(wǎng)絡(luò)環(huán)境下,我下載了5次,平均耗時約55.26s
多進程爬蟲所以為了提高采集速度,我們可以寫一個多進程爬蟲(以爬取圖片網(wǎng)站圖片為例):
為了對應(yīng)多進程的進程數(shù)n,我們可以將圖片鏈接列表分成n組,多進程爬蟲:
from multiprocessing.pool import Pool def multiprocess_crawler(processors:int): #多進程爬蟲 pool = Pool(processors) pic_urls = get_pic_src(url) #對應(yīng)多進程的進程數(shù)processors,我們可以將圖片鏈接列表分成processors組 url_groups = allot(pic_urls,processors) for i in url_groups: pool.apply_async(func=download_many_pic,args=(i,)) pool.close() pool.join()
執(zhí)行爬蟲,進程數(shù)設(shè)為4,一般是cpu數(shù)量:
multiprocess_crawler(4)
輸出:
下載完成,49張圖片,耗時:18.22s 下載完成,49張圖片,耗時:18.99s 下載完成,49張圖片,耗時:18.97s 下載完成,49張圖片,耗時:19.51s
可以看出,多進程比原先的同步爬蟲快許多,整個程序耗時19.51s,為什么不是同步爬蟲的55s/4 ≈ 14s呢?因為進程間的切換需要耗時。
如果把進程數(shù)增大,那么:
進程數(shù):10 , 耗時:12.3s 進程數(shù):30 , 耗時:2.81s 進程數(shù):40 , 耗時:11.34s
對于多進程爬蟲來說,雖然實現(xiàn)異步爬取,但也不是越多進程越好,進程間切換的開銷不僅會讓你崩潰,有時還會讓你的程序崩潰。一般用進程池Pool維護,Pool的processors設(shè)為CPU數(shù)量。進程的數(shù)量設(shè)置超過100個便讓我的程序崩潰退出。使用進程池可以保證當(dāng)前在跑的進程數(shù)量控制為設(shè)置的數(shù)量,只有池子沒滿才能加新的進程進去。
多線程爬蟲多線程版本可以在單進程下進行異步采集,但線程間的切換開銷也會隨著線程數(shù)的增大而增大。當(dāng)線程間需要共享變量內(nèi)存時,此時會有許多不可預(yù)知的變量讀寫操作發(fā)生,python為了使線程同步,給每個線程共享變量加了全局解釋器鎖GIL。而我們的爬蟲不需要共享變量,因此是線程安全的,不用加鎖。多線程版本:
import random from threading import Thread def run_multithread_crawler(pic_urls:list,threads:int): begin = 0 start = time.time() while 1: _threads = [] urls = pic_urls[begin:begin+threads] if not urls: break for i in urls: ts = str(int(time.time()*10000))+str(random.randint(1,100000)) t = Thread(target=download_one_pic,args=(i,ts)) _threads.append(t) for t in _threads: t.setDaemon(True) t.start() for t in _threads: t.join() begin += threads end = time.time() print(u"下載完成,%d張圖片,耗時:%.2fs" % (len(pic_urls), (end - start))) def multithread_crawler(threads:int): pic_urls = get_pic_src(url) run_multithread_crawler(pic_urls,threads)
并發(fā)線程數(shù)太多會讓我們的系統(tǒng)開銷越大,使程序花費時間越長,同時也會增大目標(biāo)網(wǎng)站識別爬蟲機器行為的幾率。因此設(shè)置好一個適當(dāng)?shù)木€程數(shù)以及爬取間隔是良好的爬蟲習(xí)慣。
執(zhí)行多線程爬蟲,設(shè)置線程數(shù)為50
multithreads_crawler(50)
輸出:
下載完成,196張圖片,耗時:3.10s
增大線程數(shù),輸出:
線程數(shù):50,耗時:3.10s 線程數(shù):60,耗時:3.07s 線程數(shù):70,耗時:2.50s 線程數(shù):80,耗時:2.31s 線程數(shù):120,耗時:3.67s
可以看到,線程可以有效的提高爬取效率,縮短爬取時間,但必須是一個合理的線程數(shù),越多有時并不是越好的,一般是幾十到幾百個之間,數(shù)值比多進程進程數(shù)大許多。
異步協(xié)程爬蟲Python3.5引入了async/await 異步協(xié)程語法。詳見PEP492
由于asyncio提供了基于socket的異步I/O,支持TCP和UDP協(xié)議,但是不支持應(yīng)用層協(xié)議HTTP,所以需要安裝異步http請求的aiohttp模塊
單進程下的異步協(xié)程爬蟲:
import asyncio from asyncio import Semaphore from aiohttp import ClientSession,TCPConnector async def download(session:ClientSession,url:str,name:str,sem:Semaphore,suffix:str="jpg"): path = ".".join([name,suffix]) async with sem: async with session.get(url) as response: wb_data = await response.read() with open(path,"wb") as f: f.write(wb_data) async def run_coroutine_crawler(pic_urls:list,concurrency:int): # 異步協(xié)程爬蟲,最大并發(fā)請求數(shù)concurrency tasks = [] sem = Semaphore(concurrency) conn =TCPConnector(limit=concurrency) async with ClientSession(connector=conn) as session: for i in pic_urls: ts = str(int(time.time() * 10000)) + str(random.randint(1, 100000)) tasks.append(asyncio.create_task(download(session,i,ts,sem))) start = time.time() await asyncio.gather(*tasks) end = time.time() print(u"下載完成,%d張圖片,耗時:%.2fs" % (len(pic_urls), (end - start))) def coroutine_crawler(concurrency:int): pic_urls = get_pic_src(url) loop = asyncio.get_event_loop() loop.run_until_complete(run_coroutine_crawler(pic_urls,concurrency)) loop.close()
執(zhí)行異步協(xié)程爬蟲,設(shè)置最大并發(fā)請求數(shù)為100:
coroutine_crawler(100)
輸出:
下載完成,196張圖片,耗時:2.27s
可以看出,異步多協(xié)程的下載請求效率并不比多線程差,由于磁盤IO讀寫阻塞,所以還可以進一步優(yōu)化,使用aiofiles。
針對比較大的多媒體數(shù)據(jù)下載,異步磁盤IO可以使用aiofiles,以上述例子download可以改為:
import aiofiles async def download(session:ClientSession,url:str,name:str,sem:Semaphore,suffix:str="jpg"): path = ".".join([name,suffix]) async with sem: async with session.get(url) as response: async with aiofiles.open(path,"wb") as fd: while 1: wb_data_chunk = await response.content.read(1024) if not wb_data_chunk: break await fd.write(wb_data_chunk)多進程 + 多線程 爬蟲
實際采集大量數(shù)據(jù)的過程中,往往是多種手段來實現(xiàn)爬蟲,這樣可以充分利用機器CPU,節(jié)省采集時間。
下面使用多進程(進程數(shù)為CPU數(shù),4)+ 多線程 (線程數(shù)設(shè)為50)來對例子進行更改(上面各個例子導(dǎo)入的模塊默認使用):
def mixed_process_thread_crawler(processors:int,threads:int): pool = Pool(processors) pic_urls = get_pic_src(url) url_groups = allot(pic_urls,processors) for group in url_groups: pool.apply_async(run_multithread_crawler,args=(group,threads)) pool.close() pool.join()
執(zhí)行爬蟲:
mixed_process_thread_crawler(4,50)
輸出:
下載完成,49張圖片,耗時:2.73s 下載完成,49張圖片,耗時:2.76s 下載完成,49張圖片,耗時:2.76s 下載完成,49張圖片,耗時:2.76s
采集時間與異步協(xié)程和多線程并無多大的差異,可以使用更大數(shù)據(jù)量做實驗區(qū)分。因為多進程+多線程,CPU切換上下文也會造成一定的開銷,所以進程數(shù)與線程數(shù)不能太大,并發(fā)請求的時間間隔也要考慮進去。
多進程 + 異步協(xié)程 爬蟲使用多進程(進程數(shù)為CPU數(shù),4)+ 異步協(xié)程(最大并發(fā)請求數(shù)設(shè)為50)來對例子進行更改(上面各個例子導(dǎo)入的模塊默認使用):
def _coroutine_crawler(pic_urls:list,concurrency:int): loop = asyncio.get_event_loop() loop.run_until_complete(run_coroutine_crawler(pic_urls, concurrency)) loop.close() def mixed_process_coroutine_crawler(processors:int,concurrency:int): pool = Pool(processors) pic_urls = get_pic_src(url) url_groups = allot(pic_urls, processors) for group in url_groups: pool.apply_async(_coroutine_crawler, args=(group, concurrency)) pool.close() pool.join()
執(zhí)行爬蟲 :
mixed_process_coroutine_crawler(4,50)
輸出:
下載完成,49張圖片,耗時:2.56s 下載完成,49張圖片,耗時:2.54s 下載完成,49張圖片,耗時:2.56s 下載完成,49張圖片,耗時:2.62s
效果與多進程 + 多線程 爬蟲差不多,但是CPU減少了切換線程上下文的開銷,而是對每一個協(xié)程任務(wù)進行監(jiān)視回調(diào)喚醒。使用IO多路復(fù)用的底層原理實現(xiàn)。
分布式采集關(guān)于分布式采集將會多帶帶寫一章,使用Map-Reduce+redis來實現(xiàn)分布式爬蟲。
輪子們,你們辛苦了現(xiàn)實生活中的爬蟲不止上面那些,但是基本的骨架是一樣的,對于特定的網(wǎng)站需要制定特定的采集規(guī)則,所以通用的數(shù)據(jù)采集爬蟲很難實現(xiàn)。所以針對某個網(wǎng)站的數(shù)據(jù)采集爬蟲是需要定制的,但是在不同之中包含著許多的相同、重復(fù)性的過程,比如說采集流程,或者對請求頭部的偽造,數(shù)據(jù)持久化的處理等,采集框架應(yīng)運而生。Scrapy就是目前比較成熟的一個爬蟲框架。它可以幫助我們大大減少重復(fù)性的代碼編寫,可以更好的組織采集流程。而我們只需要喝一杯咖啡,編寫自己的采集規(guī)則,讓Scrapy去給我們管理各種各樣的爬蟲,做些累活。如果你是一個爬蟲愛好者,那么scrapy是你的不錯選擇。由于好奇scrapy的實現(xiàn)流程,所以我才開始打開他的源碼學(xué)習(xí)。
有些人覺得scrapy太重,他的爬蟲只需要簡單的采集,自己寫一下就可以搞定了。但如果是大量的爬蟲采集呢?怎么去管理這些爬蟲呢?怎樣才能提高采集效率呀?
Scrapy helps~??!
另外還有另一個Python采集框架:pyspider。國人編寫的,cool~
感謝輪子們的父母,還有那些辛苦工作的輪子們,你們辛苦了~
本文所用代碼 均在GitHub上,地址:這里
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/44993.html
摘要:最簡單的說法,即是在最原始的集合論樸素集合論中的定義,集合就是一堆東西。若然是集合的元素,記作。這里對被數(shù)學(xué)家們稱為直觀的或樸素的集合論進行一個簡短而基本的介紹更詳細的分析可見樸素集合論。對集合進行嚴(yán)格的公理推導(dǎo)可見公理化集合論。 回顧一下已經(jīng)了解的數(shù)據(jù)類型:int/str/bool/list/dict/tuple 還真的不少了. 不過,python是一個發(fā)展的語言,沒準(zhǔn)以后還出別...
摘要:前端之前端之前言前言昨天學(xué)習(xí)了標(biāo)記式語言,也就是無邏輯語言。今天學(xué)習(xí),被稱之為網(wǎng)頁的化妝師。為前端頁面的樣式,由選擇器作用域與樣式塊組成。年初,組織負責(zé)的工作組開始討論第一版中沒有涉及到的問題。其討論結(jié)果組成了年月出版的規(guī)范第二版。前端之 CSS 前言 昨天學(xué)習(xí)了標(biāo)記式語言,也就是無邏輯語言。了解了網(wǎng)頁的骨架是什么構(gòu)成的,了解了常用標(biāo)簽,兩個指令以及轉(zhuǎn)義字符;其中標(biāo)簽可以分為兩大類: 一類...
摘要:最早由公司提出的,后來由采納為關(guān)系型數(shù)據(jù)庫行業(yè)國際標(biāo)準(zhǔn),先后推出了多個版本,如目前各大數(shù)據(jù)庫廠家所支持。非空約束聲明為非空的列,不能出現(xiàn),但可以出現(xiàn)重復(fù)值。自增列無需手工賦值,會自動采用數(shù)列,在當(dāng)前最大值基礎(chǔ)上。 后端知識點總結(jié)——MYSQL 1.軟件工程 IBM => DOS(Bill Gates) => MicroSoft => Windows 軟件工程學(xué)科包含: (...
摘要:最早由公司提出的,后來由采納為關(guān)系型數(shù)據(jù)庫行業(yè)國際標(biāo)準(zhǔn),先后推出了多個版本,如目前各大數(shù)據(jù)庫廠家所支持。非空約束聲明為非空的列,不能出現(xiàn),但可以出現(xiàn)重復(fù)值。自增列無需手工賦值,會自動采用數(shù)列,在當(dāng)前最大值基礎(chǔ)上。 后端知識點總結(jié)——MYSQL 1.軟件工程 IBM => DOS(Bill Gates) => MicroSoft => Windows 軟件工程學(xué)科包含: (...
閱讀 2545·2021-10-12 10:12
閱讀 1723·2019-08-30 15:52
閱讀 2464·2019-08-30 13:04
閱讀 1747·2019-08-29 18:33
閱讀 971·2019-08-29 16:28
閱讀 459·2019-08-29 12:33
閱讀 2067·2019-08-26 13:33
閱讀 2369·2019-08-26 11:36