这是我参与「第四届青训营 」笔记创作活动的第15天
系统架构
- redis:存储URL
- Flink:流式计算管理url
- 爬虫系统:网页爬取
- Zookeeper:监控爬虫状态
- 存储MYSQL和HBase:存储爬取网络后的结果
软件安装
MySQL安装与卸载
安装
MySQL官网下载 dev.mysql.com/downloads/m…
下载完解压,将安装包放到任意位置即可
以管理员身份打开cmd命令行,进入mysql文件夹的bin目录
- 初始化MySQL,并记录生成的用户密码root的随机密码,输入
mysqld --initialize --console - 安装MySQL服务,命令窗口内输入:
mysqld --install - 启动MySQL服务,命令窗口内输入:
net start mysql - 输入
mysql -u root -p输入密码进入数据库,密码在第一步运行结果 - 在命令窗口内依次输入:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;#修改加密规则 ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';#更新一下用户的密码FLUSH PRIVILEGES;#刷新权限alter user 'root'@'localhost' identified by '123456';#更改root的密码为123456- 配置环境变量
卸载
-
停止服务: 【win+R快捷键】》输入services.msc》进入服务窗口关闭mysql服务;
-
卸载程序: 【控制面板】》【程序和功能】》右键卸载程序;
-
删除项目根文件夹:进入mysql安装位置,删除mysql的解压文件夹;
-
检查服务是否完全删除:如果mysql服务还在,可以使用
sc delete mysql来删除服务; -
删除C盘隐藏文件夹:显示隐藏文件后,删除C盘下的“C:\ProgramData\MySQL ”所有文件;
-
删除注册表信息:【win+R快捷键】》输入regedit 命令打开注册表窗口,删除以下文件
- HKEY_LOCAL_MACHINE/SYSTEM/ControlSet001/Services/Eventlog/Applications/MySQL
- HKEY_LOCAL_MACHINE/SYSTEM/ControlSet002/Services/Eventlog/Applications/MySQL
- HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Eventlog/Applications/MySQL
-
删除环境变量里的配置:如果有更改了环境变量,那么进入【计算机】》右键【属性】》【高级系统设置】》【环境变量】》删除系统变量中的MYSQL_HOME变量和删除Path变量中的mysql路径;
-
全盘搜索mysql关键字检查:这里使用everything全盘搜索mysql关键字,可以查看系统所有相关文件,以防遗漏。
redis 安装
windows版本readis下载(GitHub):
github.com/tporadowski…
解压到文件夹
配置环境变量
redis-server.exe redis.windows.conf启动redis服务
redis-cli.exe -h 127.0.0.1 -p 6379打开客户端
Flink安装
archive.apache.org/dist/flink/…
flink1.9.3版本在widows系统下,下载解压,即可使用
系统实现
flink程序
一个 Flink 程序,其实就是对 DataStream 的各种转换。具体来说,代码基本上都由以下几 部分构成,如图 所示:
-
获取执行环境(execution environment)
getExecutionEnvironment
-
读取数据源(source)
DataStream stream = env.addSource(...);
从集合、文件、socket、Kafka、自定义的Source
-
定义基于数据的转换操作(transformations)
基本转换算子:map、filter、flatmap
聚合算子:按键分区、简单聚合、归约聚合
-
定义计算结果的输出位置(sink)
-
触发程序执行(execute)
Flink实现分布式爬虫
主程序
public class FlinkSpider {
public static void main(String[] args) throws Exception {
ISpider iSpider = ISpider.getInstance();
// 创建流式执行环境
StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 添加数据源
DataStreamSource<String> stream = env.addSource(new MyRedisSource());
// 处理数据
stream.map(new SpiderMapFunction(iSpider));
env.execute();
}
}
自定义redis数据源
public class MyRedisSource implements SourceFunction<String> {
private boolean isRunning =true;
private Jedis jedis=null;
private final long SLEEP_MILLION=5000;
@Override
public void run(SourceContext<String> sourceContext) throws Exception {
this.jedis = new Jedis("127.0.0.1", 6379);
while(isRunning){
String randomDomain = jedis.srandmember(SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY); // jd.com
String key = randomDomain + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX; // jd.com.higher
String url = jedis.lpop(key);
if(url == null) { // 如果为null,则从低优先级中获取
key = randomDomain + SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX; // jd.com.lower
url = jedis.lpop(key);
}
//System.out.println("---Flink source url");
sourceContext.collect(url);
//JedisUtil.returnJedis(jedis);
}
}
@Override
public void cancel() {
isRunning=false;
while(jedis!=null){
jedis.close();
}
}
}
自定义map函数
public class SpiderMapFunction implements MapFunction<String , String> {
private ISpider iSpider;
SpiderMapFunction(ISpider iSpider){
this.iSpider = iSpider;
}
@Override
public String map(String s) throws Exception {
// 6.启动爬虫
// iSpider.start();
iSpider.startSingle(s);
return null;
}
}
爬虫程序
public void startSingle(String url) {
//while (true) { // 要想开启循环爬取商品,则必须是执行一个死循环
//String url = repository.poll();
String domain = SpiderUtil.getTopDomain(url); // 获取url对应的顶级域名
System.out.println("-----flink url"+" "+url);
if (url != null) { // 从url仓库中获取的url不为null
// 下载网页
Page page = download(url);
// 解析网页
if (page.getContent() != null) { // 只有content不为null时才进行后面的操作,否则没有意义
parser(page, domain); // 如果该url为列表url,从这里有可能解析出很多的url
for (String pUrl : page.getUrls()) { // 向url仓库中添加url
logger.info(pUrl);
String higherUrlMark = urlLevelMarker.get(domain).get("higher");
String lowerUrlMark = urlLevelMarker.get(domain).get("lower");
if (pUrl.startsWith(higherUrlMark)) { // 高优先级
repository.offerHigher(pUrl);
} else if (pUrl.startsWith(lowerUrlMark)) { // 低优先级
repository.offerLower(pUrl);
}
}
if (page.getId() != null) { // 当商品id不为null时,说明前面解析的url是商品url,而不是列表url,这时存储数据才有意义
// 存储解析数据
// store(page);
// System.out.println(page);
MysqlClient.insert(page);
}
}
// 上面操作结束之后必须要休息一会,否则频率太高的话很有可能会被封ip
SpiderUtil.sleep(1000);
} else { // 从url仓库中没有获取到url
logger.info("没有url,请及时添加种子url");
SpiderUtil.sleep(2000);
}
//}
}
参考:
zhuanlan.zhihu.com/p/150360780 blog.csdn.net/weixin_4489… www.cnblogs.com/darange/p/1…