openCV入門學(xué)習(xí)基礎(chǔ)教程第三篇
前文復(fù)習(xí):
一、Canny邊緣檢測(cè)
該邊緣檢測(cè)法步驟如下:
- 使用高斯濾波器,以平滑圖像,濾除噪聲。
- 計(jì)算圖像中每個(gè)像素點(diǎn)的梯度強(qiáng)度和方向。
- 應(yīng)用非極大值(Non-Maximum Suppression)抑制,以消除邊緣檢測(cè)帶來(lái)的雜散響應(yīng)。
- 應(yīng)用雙閾值(Double-Threshold)檢測(cè)來(lái)確定真實(shí)的和潛在的邊緣。
- 通過抑制孤立的弱邊緣最終完成邊緣檢測(cè)。
高斯濾波
我們要進(jìn)行邊緣檢測(cè)過程中肯定要進(jìn)行梯度計(jì)算,計(jì)算梯度時(shí)那些噪音點(diǎn)也會(huì)影響梯度的變化,因此進(jìn)行去噪。
梯度和方向
使用sobel算子,進(jìn)行梯度大小和方向的計(jì)算。
非極大值抑制 NMS
算完梯度后,有些可能大點(diǎn),有些可能小點(diǎn),在一個(gè)核中,把一些小的梯度值抑制掉,只保留大的,即明顯的。
如人臉識(shí)別,能夠把臉框起來(lái)(有多個(gè)框框到臉了,只保留最好的。)
法1:
藍(lán)線表示方向,dTmp1和dTmp2都是亞像素點(diǎn),g1g2g3g4都是知道的,可求dTmp1和dTmp2。 在c比dTmp1和dTmp2都大的情況下,保留c,否則就被抑制掉了。
法2:
比如我們選擇傾斜45°,就過了g1和g4了(上上圖),就不用插值了。當(dāng)前方向離哪個(gè)角度近就用選擇哪個(gè)。
右圖中如果A比BC都大,那么A就保存下來(lái)了。
雙閾值
對(duì)一些所有可能邊界進(jìn)行過濾,只保留最真實(shí)的。
A點(diǎn)大于maxVal 就是邊界 如果有小于minVale的,那就省略掉。
他倆之間的B C分別進(jìn)行討論,如果c和邊界是連著的,就保留。B沒連著,舍棄。
v1=cv2.Canny(img,80,150)
80 150為下上閾值,一定范圍內(nèi),下閾值越小,上閾值越小越容易被當(dāng)作邊緣。
img=cv2.imread("./data/gd06.jpg",cv2.IMREAD_GRAYSCALE) v1=cv2.Canny(img,80,150) v2=cv2.Canny(img,50,100) res = np.hstack((v1,v2)) cv_show('res',res)
img=cv2.imread("./data/gd05.jpg",cv2.IMREAD_GRAYSCALE) v1=cv2.Canny(img,120,250) v2=cv2.Canny(img,50,100) res = np.hstack((v1,v2)) cv_show('res',res)
二、 圖像輪廓
上面說(shuō)的是邊緣,隨便一個(gè)線段也能被檢測(cè)成邊緣。而輪廓是一個(gè)整體,正常是封閉的連在一起的。
2.1 輪廓收集
cv2.findContours(img,mode,method)
- RETR_EXTERNAL :只檢索最外面的輪廓;
- RETR_LIST:檢索所有的輪廓,并將其保存到一條鏈表當(dāng)中;
- RETR_CCOMP:檢索所有的輪廓,并將他們組織為兩層:頂層是各部分的外部邊界,第二層是空洞的邊界;
- RETR_TREE:檢索所有的輪廓,并重構(gòu)嵌套輪廓的整個(gè)層次;(常用,推薦)
method:輪廓逼近方法
- CHAIN_APPROX_NONE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點(diǎn)的序列)。
- CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數(shù)只保留他們的終點(diǎn)部分。
上圖就是兩種method,一個(gè)是整個(gè)框,一個(gè)是點(diǎn),都能起到定位作用。
為了更高的準(zhǔn)確率,我們選擇使用二值圖像:
img = cv2.imread('contours.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 127為分割閾值,超過的部分取255 cv_show('thresh',thresh)
這里用到了cv2.threshold 是上一篇文章學(xué)的。
# contours輪廓信息 hierarchy層級(jí) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
其返回值有兩個(gè):
- 輪廓的信息:比如上面5個(gè)圖案,10個(gè)邊緣(內(nèi)邊緣外邊緣),這10個(gè)邊緣信息保存在了contours元組里。
- 層級(jí):hierarchy 暫時(shí)用不上,用到了再說(shuō)。
2.2 輪廓繪制
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
參數(shù)為:圖像,輪廓,輪廓索引(-1默認(rèn)所有),顏色模式(BGR),線條厚度(不宜太大)
img = cv2.imread('contours.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 127為分割閾值,超過的部分取255 # contours輪廓信息 hierarchy層級(jí) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) #傳入 圖像,輪廓,輪廓索引(-1默認(rèn)所有),顏色模式(BGR),線條厚度(不宜太大) # 注意需要copy,不然原圖會(huì)變。。。 draw_img = img.copy() res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) cv_show('res',res)
仔細(xì)看它把10個(gè)(內(nèi)外)輪廓都畫出來(lái)了,這與我們傳入的參數(shù) -1 有關(guān)。
當(dāng)然我們也可以選擇其中某個(gè):
draw_img = img.copy() # 三角的外輪廓 res = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2) cv_show('res',res)
draw_img = img.copy() # 三角的內(nèi)輪廓 res = cv2.drawContours(draw_img, contours, 1, (0, 0, 255), 2) cv_show('res',res)
draw_img = img.copy() # 六邊形外輪廓 res = cv2.drawContours(draw_img, contours, 2, (0, 0, 255), 2) cv_show('res',res)
2.3 輪廓特征
這里就介紹個(gè)周長(zhǎng)和面積吧,后面用到別的再說(shuō)。
cnt = contours[0] # 第0個(gè)輪廓 三角的外輪廓 #周長(zhǎng),True表示輪廓是閉合的 cv2.arcLength(cnt,True) # 8500.5 #面積 cv2.contourArea(cnt) # 437.9482651948929
2.4 輪廓近似
什么是輪廓近似呢,比如下圖左側(cè)圖案很麻煩,我們可以把它近似看成右邊的,就是輪廓近似。
這個(gè)的原理其實(shí)也很簡(jiǎn)單,我發(fā)揮我的繪畫功能給你們展示一下:
把曲線AB近似成直線AB,至于要d<T(閾值)即可。
我們近似一下下圖:
邊緣繪制:
img = cv2.imread('contours2.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = contours[0] draw_img = img.copy() res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2) cv_show('res',res)
epsilon = 0.02*cv2.arcLength(cnt,True) # 周長(zhǎng)百分比 0.02 # 參數(shù):輪廓 閾值,一般用周長(zhǎng)百分比 # 返回:輪廓 approx = cv2.approxPolyDP(cnt,epsilon,True) draw_img = img.copy() res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2) cv_show('res',res)
approx = cv2.approxPolyDP(cnt,epsilon,True)
- 參數(shù):輪廓 閾值,一般用周長(zhǎng)百分比
- 返回:輪廓
把閾值變大一點(diǎn):
epsilon = 0.1*cv2.arcLength(cnt,True) approx = cv2.approxPolyDP(cnt,epsilon,True) draw_img = img.copy() res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2) cv_show('res',res)
2.5 外接圖形
img = cv2.imread('contours.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = contours[0] # 外接矩形 得到x,y,w,h就能把矩形畫出來(lái)了 x,y,w,h = cv2.boundingRect(cnt) img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) cv_show('img',img)
x,y,w,h = cv2.boundingRect(cnt)
三、圖像金字塔
3.1 高斯金字塔
高斯金字塔:向下采樣(縮小)
上面金字塔最底層44 往上一層變成22 長(zhǎng)寬變?yōu)樵瓉?lái)一半面積變?yōu)樵瓉?lái)1/4,去除所有偶數(shù)行和列就是為了這一操作。
高斯金字塔:向上采樣(擴(kuò)大)
用0填充后,用卷積核把那幾個(gè)值分布在0上,獲得近似值。
img=cv2.imread("./data/gd01.jpg") cv_show('img',img) up=cv2.pyrUp(img) cv_show('up',up) up2=cv2.pyrUp(up) cv_show('up2',up2) down=cv2.pyrDown(img) cv_show('down',down)
上面其實(shí)就是圖片的放大與縮小。
下面要注意,縮小放大后的圖片會(huì)變得模糊,畢竟之前我們是用0填充,卷積核計(jì)算獲得的近似值。
# 先擴(kuò)大后縮小后,由于當(dāng)時(shí)是用0填充的,會(huì)損失信息。 up=cv2.pyrUp(img) up_down=cv2.pyrDown(up) # 顯然變得模糊了 cv_show('up_down',np.hstack((img,up_down)))
明顯右邊模糊了,我們把兩張圖片做個(gè)差值:
# 做差就能看出區(qū)別 up=cv2.pyrUp(img) up_down=cv2.pyrDown(up) cv_show('img-up_down',img-up_down)
這就是不同的部分。
3.2 拉普拉斯金字塔
和上面最后縮小放大后的圖片一樣,拉普拉斯這個(gè)每一層都是 原始-縮小(放大了的圖片) 第二層用第一層的結(jié)果去減了。
down=cv2.pyrDown(img) down_up=cv2.pyrUp(down) l_1=img-down_up cv_show('l_1',l_1)
四、直方圖
4.1 像素直方圖繪制
左側(cè)灰度圖像素點(diǎn) 右側(cè)像素點(diǎn)直方圖
cv2.calcHist(images,channels,mask,histSize,ranges)
- images: 原圖像圖像格式為 uint8 或 ?oat32。當(dāng)傳入函數(shù)時(shí)應(yīng) 用中括號(hào) [] 括來(lái)例如[img]
- channels: 同樣用中括號(hào)括來(lái)它會(huì)告函數(shù)我們統(tǒng)幅圖像的直方圖。如果入圖像是灰度圖它的值就是 [0]如果是彩+ 色圖像 的傳入的參數(shù)可以是 [0][1][2] 它們分別對(duì)應(yīng)著 BGR。
- mask: 掩模圖像。統(tǒng)計(jì)整幅圖像的直方圖就mask = None。但是如果你想統(tǒng)計(jì)圖像某一分的直方圖的你就制作一個(gè)掩模圖像并使用它。
- histSize:BIN 的數(shù)目。也應(yīng)用中括號(hào)括來(lái)如0-10是一個(gè)柱子 11-20是一個(gè)柱
- ranges: 像素值范圍常為 [0256]
img = cv2.imread('./data/gd01.jpg',0) #0表示灰度圖 hist = cv2.calcHist([img],[0],None,[256],[0,256]) plt.hist(img.ravel(),256); plt.show()
img = cv2.imread('./data/gd01.jpg') color = ('b','g','r') for i,col in enumerate(color): histr = cv2.calcHist([img],[i],None,[256],[0,256]) plt.plot(histr,color = col) plt.xlim([0,256])
mask操作
我們?nèi)∫恍K區(qū)域,然后獲得圖片中這一塊區(qū)域的圖像
# 創(chuàng)建mast mask = np.zeros(img.shape[:2], np.uint8) print (mask.shape) mask[100:300, 100:400] = 255 cv_show('mask',mask)
masked_img = cv2.bitwise_and(img, img, mask=mask)#與操作 cv_show('masked_img',masked_img)
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256]) hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256]) plt.subplot(221), plt.imshow(img, 'gray') plt.subplot(222), plt.imshow(mask, 'gray') plt.subplot(223), plt.imshow(masked_img, 'gray') plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0, 256]) plt.show()
4.2 直方圖均衡化
如上圖,像素點(diǎn)都集中在一個(gè)地方,我們?cè)诓黄茐钠涮卣鞯那疤嵯伦尭呤莸姆植甲兊冒忠稽c(diǎn),亮度什么的也就都會(huì)發(fā)生些變化。
注:這里的映射是 累積概率*取值范圍(255-0)
img = cv2.imread('./data/gd01.jpg',0) #0表示灰度圖 #clahe plt.hist(img.ravel(),256); plt.show()
equ = cv2.equalizeHist(img) plt.hist(equ.ravel(),256) plt.show()
res = np.hstack((img,equ)) cv_show('res',res)
圖片比之前亮一些了。
4.3 自適應(yīng)直方圖均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) res_clahe = clahe.apply(img) res = np.hstack((img,equ,res_clahe)) cv_show('res',res)
五、傅里葉變換
我們生活在時(shí)間的世界中,早上7:00起來(lái)吃早飯,8:00去擠地鐵,9:00開始上班...
以時(shí)間為參照就是時(shí)域分析,但是在頻域中一切都是靜止的!
傅里葉變換的作用
- 高頻:變化劇烈的灰度分量,例如邊界
- 低頻:變化緩慢的灰度分量,例如一片大海
濾波
- 低通濾波器:只保留低頻,會(huì)使得圖像模糊
- 高通濾波器:只保留高頻,會(huì)使得圖像細(xì)節(jié)增強(qiáng)
opencv中主要就是cv2.dft()和cv2.idft(),輸入圖像需要先轉(zhuǎn)換成np.float32 格式
得到的結(jié)果中頻率為0的部分會(huì)在左上角,通常要轉(zhuǎn)換到中心位置,通過shift變換。
cv2.dft()返回的結(jié)果是雙通的(實(shí)部,虛部),通常還需要轉(zhuǎn)換成圖像格式才能展示(0,255)。
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('./data/gd06.jpg',0) img_float32 = np.float32(img) dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1])) plt.subplot(121),plt.imshow(img, cmap = 'gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray') plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([]) plt.show()
img = cv2.imread('./data/gd06.jpg',0) img_float32 = np.float32(img) dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) rows, cols = img.shape crow, ccol = int(rows/2) , int(cols/2) # 中心位置 # 低通濾波 mask = np.zeros((rows, cols, 2), np.uint8) mask[crow-30:crow+30, ccol-30:ccol+30] = 1 # IDFT fshift = dft_shift*mask f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1]) plt.subplot(121),plt.imshow(img, cmap = 'gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(img_back, cmap = 'gray') plt.title('Result'), plt.xticks([]), plt.yticks([]) plt.show()
img = cv2.imread('./data/gd06.jpg',0) img_float32 = np.float32(img) dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) rows, cols = img.shape crow, ccol = int(rows/2) , int(cols/2) # 中心位置 # 高通濾波 mask = np.ones((rows, cols, 2), np.uint8) mask[crow-30:crow+30, ccol-30:ccol+30] = 0 # IDFT fshift = dft_shift*mask f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1]) plt.subplot(121),plt.imshow(img, cmap = 'gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(img_back, cmap = 'gray') plt.title('Result'), plt.xticks([]), plt.yticks([]) plt.show()
六、模板匹配
模板匹配和卷積原理很像,模板在原圖像上從原點(diǎn)開始滑動(dòng),計(jì)算模板與(圖像被模板覆蓋的地方)的差別程度,這個(gè)差別程度的計(jì)算方法在opencv里有6種,然后將每次計(jì)算的結(jié)果放入一個(gè)矩陣?yán)铮鳛榻Y(jié)果輸出。
假如原圖形是AxB大小,而模板是axb大小,則輸出結(jié)果的矩陣是(A-a+1)x(B-b+1)
img = cv2.imread('./data/gd04.jpg', 0) template = cv2.imread('./data/gd_face.jpg', 0) h, w = template.shape[:2]
它們的關(guān)系就是(A-a+1)x(B-b+1)
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
- TM_SQDIFF:計(jì)算平方不同,計(jì)算出來(lái)的值越小,越相關(guān)
- TM_CCORR:計(jì)算相關(guān)性,計(jì)算出來(lái)的值越大,越相關(guān)
- TM_CCOEFF:計(jì)算相關(guān)系數(shù),計(jì)算出來(lái)的值越大,越相關(guān)
- TM_SQDIFF_NORMED:計(jì)算歸一化平方不同,計(jì)算出來(lái)的值越接近0,越相關(guān)
- TM_CCORR_NORMED:計(jì)算歸一化相關(guān)性,計(jì)算出來(lái)的值越接近1,越相關(guān)
- TM_CCOEFF_NORMED:計(jì)算歸一化相關(guān)系數(shù),計(jì)算出來(lái)的值越接近1,越相關(guān)
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR', 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED'] res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF) # res.shape 為(139, 458) # 最小值最大值及其坐標(biāo)位置 因?yàn)橛玫腸v2.TM_SQDIFF 所以越小越好 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
比如我要用到的圖像和臉:
for meth in methods: img2 = img.copy() # 匹配方法的真值 method = eval(meth) print (method) res = cv2.matchTemplate(img, template, method) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 如果是平方差匹配TM_SQDIFF或歸一化平方差匹配TM_SQDIFF_NORMED,取最小值 if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]: top_left = min_loc else: top_left = max_loc bottom_right = (top_left[0] + w, top_left[1] + h) # 畫矩形 cv2.rectangle(img2, top_left, bottom_right, 255, 2) plt.subplot(121), plt.imshow(res, cmap='gray') plt.xticks([]), plt.yticks([]) # 隱藏坐標(biāo)軸 plt.subplot(122), plt.imshow(img2, cmap='gray') plt.xticks([]), plt.yticks([]) plt.suptitle(meth) plt.show()
匹配多個(gè)對(duì)象
能匹配一個(gè)當(dāng)然也能匹配多個(gè):
img_rgb = cv2.imread('./data/mario.jpg') img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) template = cv2.imread('./data/mario_coin.jpg', 0) h, w = template.shape[:2] res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) threshold = 0.8 # 取匹配程度大于%80的坐標(biāo) loc = np.where(res >= threshold) for pt in zip(*loc[::-1]): # *號(hào)表示可選參數(shù) bottom_right = (pt[0] + w, pt[1] + h) cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2) cv2.imshow('img_rgb', img_rgb) cv2.waitKey(0)
匹配金幣:
總結(jié)
到此這篇關(guān)于openCV入門學(xué)習(xí)基礎(chǔ)教程的文章就介紹到這了,更多相關(guān)openCV第三篇內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 匿名函數(shù)與三元運(yùn)算學(xué)習(xí)筆記
這篇文章主要介紹了python 匿名函數(shù)與三元運(yùn)算的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)python 編程,感興趣的朋友可以了解下2020-10-10python 將print輸出的內(nèi)容保存到txt文件中
本文通過代碼給大家介紹了python 將print輸出的內(nèi)容保存到txt文件中,代碼很簡(jiǎn)短,需要的朋友可以參考下2018-07-07python將紅底證件照轉(zhuǎn)成藍(lán)底的實(shí)現(xiàn)方法
這篇文章主要介紹了python將紅底證件照轉(zhuǎn)成藍(lán)底,本文給大家分享四種方法通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08tensorflow 保存模型和取出中間權(quán)重例子
今天小編就為大家分享一篇tensorflow 保存模型和取出中間權(quán)重例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-01-01Python OpenCV對(duì)本地視頻文件進(jìn)行分幀保存的實(shí)例
今天小編就為大家分享一篇Python OpenCV對(duì)本地視頻文件進(jìn)行分幀保存的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2019-01-01python中numpy.empty()函數(shù)實(shí)例講解
在本篇文章里小編給大家分享的是一篇關(guān)于python中numpy.empty()函數(shù)實(shí)例講解內(nèi)容,對(duì)此有興趣的朋友們可以學(xué)習(xí)下。2021-02-02Python scrapy爬取起點(diǎn)中文網(wǎng)小說(shuō)榜單
爬蟲的基礎(chǔ)內(nèi)容已經(jīng)全部學(xué)玩,博主決定想著更加標(biāo)準(zhǔn)化以及實(shí)用能力更強(qiáng)的scrapy進(jìn)發(fā),今天記錄自己第一個(gè)scrapy爬蟲項(xiàng)目. scrapy爬取起點(diǎn)中文網(wǎng)24小時(shí)熱銷榜單,需要的朋友可以參考下2021-06-06