基于Python和C++实现的图像网络爬虫与图像处理
1、实验概述
设计、实现图片网络爬虫,从网站上批量下载图片;并对图片进行图像处理。
2、实验环境
- 语言
- 网络爬虫:Python
-
图片处理:C++
-
软件环境
- Visual Studio2015
- Pycharm2017
3、实验设计思路
4、实验实现
4.1 全局变量及导入的库
```python
coding=utf-8
urllib模块提供了读取Web页面数据的接口
import urllib.request
re模块主要包含了正则表达式
import re import requests import urllib.error import BitVector.BitVector import socket
如果连接时间超出2秒就跳过这个网址
socket.setdefaulttimeout(2.0)
urls = ["http://www.ivsky.com/"] # 自定义要爬去的链接 depth = 5 # 自定义爬去的深度 picNum = 0 ```
4.2 网址获取
python
class MyCrawler:
def __init__(self, seeds):
# 初始化当前抓取的深度
self.currentDepth = 1
# 使用种子初始化url栈
self.linkQuence = linkQuence()
self.bloomfilter = BloomFilter()
if isinstance(seeds, str):
self.linkQuence.addUnvisitedUrl(seeds)
if isinstance(seeds, list):
for i in seeds:
self.linkQuence.addUnvisitedUrl(i)
定义了一个MyCrawler类用于获得当前页面的链接网址,其中成员linkQuence用于存储已访问过的网址和即将访问的网址;成员bloomfilter类用于网页去重。
```python
抓取过程主函数
def crawling(self, seeds, crawlDepth, static_url): # 循环条件:抓取深度不超过crawl_deepth while self.currentDepth <= crawlDepth: # 循环条件:待抓取的链接不空 print("depth",self.currentDepth) while len(self.linkQuence.visted) <= 1023 and not self.linkQuence.unVisitedUrlsEnmpy(): try: # url出栈 visitUrl = self.linkQuence.unVisitedUrlDeQuence() if visitUrl is None or visitUrl == "": continue elif (self.bloomfilter.isContaions(visitUrl) == True): continue self.bloomfilter.insert(visitUrl) # 获取超链接 links = self.getHyperLinks(visitUrl, static_url) # 将url放入已访问的url中 self.linkQuence.addVisitedUrl(visitUrl) #print(len(self.linkQuence.visted)) # 未访问的url入列 for link in links: if ("http" == str(i)[:4]): self.linkQuence.addUnvisitedUrl(link) except: continue self.currentDepth += 1 print("urlTotal:",len(self.linkQuence.visted)) ```
crawling函数用于抓取网页中的链接并将已访问过的网址加入已访问网址栈,当抓取深度超过自定义的需要爬取的深度即停止爬虫;当已访问过的不重复的网址超过1023或者未访问过的网址栈为空,即开始下一深度的爬取。将网址加入已访问网址栈时需要对其利用BloomFilter进行去重。爬取网址结束后打印出已访问网址栈的长度,即已访问的网址个数。
python
# 获取源码中得超链接
def getHyperLinks(self, url, static_url):
result = []
r = requests.get(url,allow_redirects=False)
data = r.text
#print(url)
# 利用正则查找所有连接
link_list = re.findall(r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')", data)
for i in link_list:
if ("http" == str(i)[:4]):
result.append(i)
return result
通过requests中的get函数得到网页url中的源码内容存储在data中;正则表达式r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')"通过匹配从data中取出标签href中的内容;if判断筛选出有效超连接。
```python class linkQuence: def init (self): # 已访问的url集合 self.visted = [] # 待访问的url集合 self.unVisited = []
# 获取访问过的url栈
def getVisitedUrl(self):
return self.visted
# 获取未访问的url栈
def getUnvisitedUrl(self):
return self.unVisited
# 添加到访问过得url栈中
def addVisitedUrl(self, url):
self.visted.append(url)
# 移除访问过得url
def removeVisitedUrl(self, url):
self.visted.remove(url)
# 未访问过得url出栈
def unVisitedUrlDeQuence(self):
try:
return self.unVisited.pop()
except:
return None
# 保证每个url只被访问一次
def addUnvisitedUrl(self, url):
if url != "" and url not in self.visted and url not in self.unVisited:
self.unVisited.insert(0, url)
# 获得已访问的url数目
def getVisitedUrlCount(self):
return len(self.visted)
# 获得未访问的url数目
def getUnvistedUrlCount(self):
return len(self.unVisited)
# 判断未访问的url栈是否为空
def unVisitedUrlsEnmpy(self):
return len(self.unVisited) == 0
```
实现一个网址访问栈类。
4.3 网页去重
```python class BloomFilter(): #n = 1200, m = 15000, k = 7 def init (self, BIT_SIZE=15000): self.BIT_SIZE = 15000 self.seeds = [5, 7, 11, 13, 31, 37, 61] # 建立一个大小为15000位的二进制向量,分配内存 self.bitset = BitVector.BitVector(size=self.BIT_SIZE) self.hashFunc = [] # 利用7个素数初始化7个随机数生成器 for i in range(len(self.seeds)): self.hashFunc.append(SimpleHash(self.BIT_SIZE, self.seeds[i]))
def insert(self, value):
for f in self.hashFunc:
loc = f.hash(value)
self.bitset[loc] = 1
def isContaions(self, value):
if value == None:
return False
ret = True
for f in self.hashFunc:
loc = f.hash(value)
# 用同样的随机数产生方法对比相应位的二进制值,只要发现有一个不同即返回结果为假
ret = ret & self.bitset[loc]
if ret == False:
return ret
# 只有当7个二进制位都相等时才返回真
return ret
```
实现一个BloomFilter类。根据公式
以及要求假阳性概率在0.1%之内,且应有至少1000个不重复的网址,则取布隆过滤器的n = 1200, m = 15000, k = 7。
```python class SimpleHash(): def init (self, capability, seed): self.capability = capability self.seed = seed
# 传入的value即为url值,ord(value[i])表示第i位字符的ascii码值
def hash(self, value):
ret = 0
for i in range(len(value)):
ret += self.seed * ret + ord(value[i])
# 最终产生的数是二进制向量最大下标与随机数的按位与结果
return (self.capability - 1) & ret
```
利用DEK哈希算法生成哈希值。
4.4 图片下载
```python def getHtml(url): global picNum try: headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'} url = urllib.request.Request(url=url, headers=headers) page = urllib.request.urlopen(url, timeout = 2) #urllib.urlopen()方法用于打开一个URL地址 html = page.read() #read()方法用于读取URL上的数据
reg = r'src="([a-zA-Z0-9\.:/]+?\.jpg)"' # 正则表达式,得到图片地址
imgre = re.compile(reg) # re.compile() 可以把正则表达式编译成一个正则表达式对象.
html = html.decode("utf-8", "ignore")
imglist = re.findall(imgre, html) # re.findall() 方法读取html 中包含 imgre(正则表达式)的 数据
# 把筛选的图片地址通过for循环遍历并保存到本地
# 核心是urllib.urlretrieve()方法,直接将远程数据下载到本地,图片通过x依次递增命名
for imgurl in imglist:
try:
if ("http" == str(imgurl)[:4]):
urllib.request.urlretrieve(imgurl, 'E:\images\%s.jpg' % picNum)
picNum += 1
except:
continue
except:
print("something is wrong")
```
利用request库中的urlopen方法打开一个url地址并用read方法读取url上的数据;正则表达式r'src="([a-zA-Z0-9.:/]+?.jpg)"'用来获取url上的图片地址;再利用urlretrieve方法保存图片到本地。
4.5 图像处理
```python class PixImage { private: cv::Mat image; //图片名称 std::string name; //图片尺寸、高、宽 int size; int height; int width; //像素信息 uchar color; uchar colorR; uchar colorG; uchar colorB; //经过处理后的图片 cv::Mat imageCR; cv::Mat imageBluring; cv::Mat imageSobel; //读取图片像素信息 void colorRead(cv::Mat &image); //像素减少处理 void colorReduce(cv::Mat &image, cv::Mat &imageColorReduce); //模糊处理 void Bluring(cv::Mat &image, cv::Mat &imageBluring, int winSize); //将多通道图片转换成单通道 int GrayImage(cv::Mat &image, cv::Mat &imageSobel); //利用索贝尔算子检测图片边缘 void Sobel(int thresh, cv::Mat &imageSobel, cv::Mat &gray);
public: //根据图片名称构造函数 PixImage(string imageName) { image = cv::imread(imageName); if (image.empty()) { cout << "图像加载失败" << endl; exit(0); } name = imageName; size = image.cols image.rows; width = image.cols; height = image.rows; color = new uchar [height]; colorR = new uchar [height]; colorG = new uchar [height]; colorB = new uchar*[height]; colorRead(image); } //析构函数 ~PixImage() { delete []color; delete []colorR; delete []colorG; delete []colorB; }
void colorReduce() {
colorReduce(image, imageCR);
}
void Bluring(int winSize) {
Bluring(image, imageBluring, winSize);
}
void Sobel() {
Mat gray;
int thresh = GrayImage(image, gray);
Sobel(thresh, imageSobel, gray);
}
//在屏幕上显示图片
void PicShow();
//保存图片
void PicSave(string name);
}; ```
定义了一个PixImage类,private部分存有图片的尺寸、像素信息、处理后的图片信息以及图片处理方式对应的函数;public部分有相应的像素处理函数接口、中值滤波图片模糊处理接口、sobel算子处理接口以及图片显示、保存的函数。
c++
//通过.ptr<>函数得到一行的指针,并用[]操作符访问某一列的像素值。
void PixImage::colorRead(cv::Mat &image) {
Mat imageClone = image.clone();
int rowNumber = imageClone.rows;
int colNumber = imageClone.cols;
int div = 64;
for (int i = 0; i < rowNumber; i++) {
color[i] = imageClone.ptr<uchar>(i);
colorR[i] = new uchar[colNumber];
colorG[i] = new uchar[colNumber];
colorB[i] = new uchar[colNumber];
for (int j = 0; j < colNumber; j++) {
colorR[i][j] = color[i][3 * j];
colorG[i][j] = color[i][3 * j + 1];
colorB[i][j] = color[i][3 * j + 2];
}
}
}
利用.ptr<>函数访问并得到图片的像素信息。
c++
void PixImage::colorReduce(cv::Mat &image, cv::Mat &imageColorReduce) {
imageColorReduce = image.clone();
int rowNumber = imageColorReduce.rows;
int colNumber = imageColorReduce.cols*imageColorReduce.channels();
int div = 64;
for (int i = 0; i < rowNumber; i++) {
uchar* data = imageColorReduce.ptr<uchar>(i);
for (int j = 0; j < colNumber; j++) {
data[j] = data[j] / div*div + div / 2;
}
}
}
对图像中的像素进行量化处理。如常见的RGB24图像有256*256*256中颜色,通过Reduce Color将每个通道的像素减少8倍至256/8=32种,则图像只有32*32*32种颜色。假设量化减少的倍数是N,则代码实现时就是简单的value/N*N,通常会再加上N/2以得到相邻的N的倍数的中间值,最后图像被量化为(256/N)×(256/N)×(256/N)种颜色。
```c++ //均值滤波器消除噪声 void PixImage::Bluring(cv::Mat &image, cv::Mat &imageBluring, int winSize) { imageBluring = image.clone(); int rowNumber = imageBluring.rows; int colNumber = imageBluring.cols; uchar * data = new uchar [rowNumber]; int pos = (winSize - 1) / 2, temp = 0;
//模糊处理中间一圈
for (int i = pos; i < rowNumber - pos; i++) {
data[i] = imageBluring.ptr<uchar>(i);
for (int j = pos; j < colNumber - pos; j++) {
temp = 0;
//处理红色像素
for (int winRow = 0; winRow < winSize; winRow++)
for (int winCol = 0; winCol < winSize; winCol++)
temp += colorR[i - pos + winRow][j - pos + winCol];
data[i][j * 3] = temp / (winSize*winSize);
temp = 0;
//处理绿色像素
for (int winRow = 0; winRow < winSize; winRow++)
for (int winCol = 0; winCol < winSize; winCol++)
temp += colorG[i - pos + winRow][j - pos + winCol];
data[i][j * 3 + 1] = temp / (winSize*winSize);
temp = 0;
//处理蓝色像素
for (int winRow = 0; winRow < winSize; winRow++)
for (int winCol = 0; winCol < winSize; winCol++)
temp += colorB[i - pos + winRow][j - pos + winCol];
data[i][j * 3 + 2] = temp / (winSize*winSize);
}
}
data[0] = imageBluring.ptr<uchar>(0);
data[rowNumber - 1] = imageBluring.ptr<uchar>(rowNumber - 1);
//处理左上角像素
temp = 0;
for (int i = 0; i <= pos; i++)
for (int j = 0; j <= pos; j++)
temp += colorR[i][j];
data[0][0] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = 0; i <= pos; i++)
for (int j = 0; j <= pos; j++)
temp += colorG[i][j];
data[0][1] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = 0; i <= pos; i++)
for (int j = 0; j <= pos; j++)
temp += colorB[i][j];
data[0][2] = temp / ((pos + 1)*(pos + 1));
//处理右上角像素
temp = 0;
for (int i = 0; i <= pos; i++)
for (int j = colNumber-pos-1; j < colNumber; j++)
temp += colorR[i][j];
data[0][3 * (colNumber - 1)] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = 0; i <= pos; i++)
for (int j = colNumber - pos - 1; j < colNumber; j++)
temp += colorG[i][j];
data[0][3 * (colNumber - 1) + 1] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = 0; i <= pos; i++)
for (int j = colNumber - pos - 1; j < colNumber; j++)
temp += colorB[i][j];
data[0][3 * (colNumber - 1) + 2] = temp / ((pos + 1)*(pos + 1));
//处理左下角像素
temp = 0;
for (int i = rowNumber - pos - 1; i < rowNumber; i++)
for (int j = 0; j <= pos; j++)
temp += colorR[i][j];
data[rowNumber - 1][0] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = rowNumber - pos - 1; i < rowNumber; i++)
for (int j = 0; j <= pos; j++)
temp += colorG[i][j];
data[rowNumber - 1][1] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = rowNumber - pos - 1; i < rowNumber; i++)
for (int j = 0; j <= pos; j++)
temp += colorB[i][j];
data[rowNumber - 1][2] = temp / ((pos + 1)*(pos + 1));
//处理右下角像素
temp = 0;
for (int i = rowNumber - pos - 1; i < rowNumber; i++)
for (int j = colNumber - pos - 1; j < colNumber; j++)
temp += colorR[i][j];
data[rowNumber - 1][3 * (colNumber - 1)] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = rowNumber - pos - 1; i < rowNumber; i++)
for (int j = colNumber - pos - 1; j < colNumber; j++)
temp += colorG[i][j];
data[rowNumber - 1][3 * (colNumber - 1) + 1] = temp / ((pos + 1)*(pos + 1));
temp = 0;
for (int i = rowNumber - pos - 1; i < rowNumber; i++)
for (int j = colNumber - pos - 1; j < colNumber; j++)
temp += colorB[i][j];
data[rowNumber - 1][3 * (colNumber - 1) + 2] = temp / ((pos + 1)*(pos + 1));
delete[]data;
} ```
利用均值滤波器消除噪声使图片模糊,即将任意一个像素,取其周围n*n个像素范围内的平均值来置换该像素;为了便于操作,将图片中间的像素与边缘像素分开处理。
c++
//三通道彩色图片转换成单通道图片
int PixImage::GrayImage(cv::Mat &image, cv::Mat &imageSobel) {
int threshL = 0, thresh = 0;
imageSobel = image.clone();
int rowNumber = imageSobel.rows;
int colNumber = imageSobel.cols;
for (int i = 0; i < rowNumber; i++) {
uchar* data = imageSobel.ptr<uchar>(i);
for (int j = 0; j < colNumber; j++) {
data[j * 3] = (uchar)(0.136*data[j * 3] + 0.514*data[j * 3 + 1] + 0.350*data[j * 3 + 2]);
data[j * 3 + 1] = data[j * 3];
data[j * 3 + 2] = data[j * 3];
threshL += data[3 * j];
}
thresh += (threshL / colNumber);
threshL = 0;
}
thresh = thresh/rowNumber;
thresh = sqrt(200 * thresh);
return thresh;
}
灰度处理,即将多通道图片转换成单通道图片。将RGB像素值按照比例相加,并且使RGB三个通道的值相等即完成转换;并且求得该幅图片所有点灰度的平均值,即Sobel阈值。经多次试验比较,得出scale在200左右比较合适。
```c++ //利用索贝尔算子检测图片边缘 void PixImage::Sobel(int thresh, cv::Mat &imageSobel, cv::Mat &gray) { imageSobel = image.clone(); int rowNumber = imageSobel.rows; int colNumber = imageSobel.cols; uchar data = new uchar*[rowNumber]; uchar temp = new uchar*[rowNumber];
for (int i = 0; i < rowNumber; i++) {
data[i] = image.ptr<uchar>(i);
temp[i] = imageSobel.ptr<uchar>(i);
}
for (int i = 1; i < rowNumber - 1; i++) {
for (int j = 1; j < colNumber - 1; j++) {
int Gx = (data[i + 1][3 * (j - 1)] + 2 * data[i + 1][j * 3] + data[i + 1][3 * (j + 1)])
- (data[i - 1][3 * (j - 1)] + 2 * data[i - 1][j * 3] + data[i - 1][3 * (j + 1)]);
int Gy = (data[i - 1][3 * (j - 1)] + 2 * data[i][3 * (j - 1)] + data[i + 1][3 * (j - 1)])
- (data[i - 1][3 * (j + 1)] + 2 * data[i][3 * (j + 1)] + data[i + 1][3 * (j + 1)]);
uchar G = sqrt(Gx*Gx + Gy*Gy);
if (G > thresh)
temp[i][j*3] = (uchar)255;
else
temp[i][j*3] = (uchar)0;
temp[i][j * 3 + 1] = temp[i][j * 3];
temp[i][j * 3 + 2] = temp[i][j * 3];
}
}
delete[]data;
delete[]temp;
} ```
利用索贝尔算子检测图片边缘。将图像与两组矩阵分别作平面卷积,得出横向及纵向的亮度差分近似值。若某点灰度值大于Sobel阈值,则将其赋值为255;若该点灰度值小于等于Sobel阈值,则将其赋值为0。
```c++ //显示图片 void PixImage::PicShow() { int timeShow = 1000; if (imageCR.empty()) { cout << "没有进行像素减少处理" << endl; } else { namedWindow("PicShow"); imshow("PicShow", imageCR); waitKey(timeShow); } if (imageBluring.empty()) { cout << "没有进行模糊处理" << endl; } else { namedWindow("PicShow"); imshow("PicShow", imageBluring); waitKey(timeShow); } if (imageSobel.empty()) { cout << "没有进行图像边缘处理" << endl; } else { namedWindow("PicShow"); imshow("PicShow", imageSobel); waitKey(timeShow); } }
//保存图片 void PixImage::PicSave(string name) { if (imageCR.empty()) { cout << "没有进行像素减少处理" << endl; } else cv::imwrite(name + "_像素减少.jpg", imageCR); if (imageBluring.empty()) { cout << "没有进行模糊处理" << endl; } else cv::imwrite(name + "_模糊处理.jpg", imageBluring); if (imageSobel.empty()) { cout << "没有进行图像边缘处理" << endl; } else cv::imwrite(name + "_图像边缘处理.jpg", imageSobel); } ```
图片处理和保存函数,若没有进行相应的处理则输出提示切不保存。
```c++
include
include
include
include "imageProcess.h"
using namespace std; //总共要处理照片数量
define picTotal 1
int main() { string path = "E:/images/"; //存储路径 for (int i = 0; i < picTotal; i++) { stringstream ss; string n; ss << i; ss >> n; PixImage image(path+n+".jpg"); image.colorReduce(); image.Bluring(3); image.Sobel(); image.PicShow(); image.PicSave(path + n); } return 0; } ```
图片处理部分主函数,保存图片至相应的路径并加后缀命名指出进行了何种处理。
5、运行结果
5.1 爬虫部分
爬取了1013个不重复的网址以及10035张图片
5.2 图片处理部分
参考文献
- 网络爬虫技术在云平台上的研究与实现(电子科技大学·刘小云)
- 主题网络爬虫的研究和实现(武汉理工大学·林捷)
- 恶意URL检测项目中基于PageRank算法的网络爬虫的设计和实现(北京邮电大学·王晓梅)
- 基于网络爬虫的数字隐写图像采集系统设计与实现(华中师范大学·王年丰)
- 分布式网络爬虫技术的研究与实现(哈尔滨工业大学·苏旋)
- 主题爬虫关键技术研究(哈尔滨工程大学·黄正德)
- 分布式Web Crawler系统研究与实现(江西理工大学·胡炜)
- 基于领域的网络爬虫技术的研究与实现(武汉理工大学·谭龙远)
- 基于Bloom Filter算法的URL去重算法研究及其应用(河南大学·孟慧君)
- 分布式网络爬虫在农产品搜索系统中的应用与研究(南昌大学·袁龙涛)
- 面向垂直搜索引擎的聚焦网络爬虫关键技术研究与实现(华中师范大学·陈欢)
- 分布式网络爬虫技术的研究与实现(哈尔滨工业大学·苏旋)
- 面向人脸检测的主题网络爬虫系统(重庆大学·杨东权)
- 面向中小学教育资源的网络爬虫的研究与设计(中央民族大学·郑名达)
- 主题网络爬虫的研究和实现(武汉理工大学·林捷)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:源码驿站 ,原文地址:https://m.bishedaima.com/yuanma/35495.html