抓产品,采用多进程--perfect

前段时间发现单进程爬虫实在是太慢太慢,有时候一天也不一定爬的完,后面就考虑到用多进程,写好程序后,试试看,速度666的。废话不多说,直接干活。

我是用的语言是ruby(当然python也是可以的,我这边就不贴python的,这边我们说ruby的),socket这边我才用udp通讯,因为tcp速度上会比较慢些,虽然保证不丢包啥的,但是速度成本比较高,如果比较大型的网站。

这边直说下载源码,解析过程比较简单,上次的文章有说过了,这次就不说了。

 

服务器一直开着,直到程序结束,客户端,写一个脚本,跑完了,让他继续重启。自己认为这个代码思想很不错的,具体提供一些思路,和部分代码。

 

比如一个 网站 我们点击三层。 

具体思路呢,就是我们使用udp,需要客户端先发送一个信号(比如hi),服务器收到了,给他分发一个url,cilent点击之后,如果是产品页面,就告诉sever,是产品页面。下面情况我分两个if说明一下。

if 不是产品页面 && 未到达制定层数

  就返回点击后产品的所有url

  server 负责去重后存进数据库

end

if 不是产品页面&&到达指定层数

  就直接返回0

end

  

 

下面贴sever的部分代码,具体过程都很详细

加载ruby的库
加载一些写好的函数文件


#获取代理 这边我是用me  se也是一样的 我也写了一套 
proxy = get_proxy
puts proxy
agent = Mechanize.new
agent.open_timeout = 15
agent.set_proxy proxy[0],proxy[1]

#连接数据库  (因为ruby内存不会回收,我们要把还没点的连接存进数据库 程序挂了 重启保证从断点开始)
client = Mysql2::Client.new(:host => "数据库地址", :username => "用户名",:password=>"密码",:database=>"数据库名称")

deep = 0
puts "请输入要遍历的商品信息的主url:"
root_url = gets.chomp
page = agent.get root_url
puts "请输入链接需要点击的层数:"
num = gets.chomp.to_i	
host = root_url.scan(/www\.(.*?)\./)[0][0]
host.gsub!("-","_")
##创建目录  (下载源码的存放位置)
FileUtils.mkdir_p("/samba/xww/me/made-in-china/#{host}")



#这边就是实现从数据库读取 还未点击的url的集合 根据主url来查找 
#要是有集合存在  就直接导出 
#没有的话 就插入
stack = []
Node = Struct.new :url,:deep,:line
res_select = sql_select(client,root_url)
if res_select[0] == 1 #查到click_url
	stack = res_select[1]
	puts "当前数据库已经有这条记录啦 直接导出"
elsif res_select[0] == -1 #查到root_url
	puts "该网站已经遍历结束 程序退出"
	exit
else #什么都没查到	
	deep = deep + 1
	page.links.each do |link|
		line = Digest::MD5.hexdigest("#{link.href}:#{deep}")
		obj = Node.new(link.href,deep,line)
		stack.push obj
	end
	stack.uniq!
	puts "第一层的链接数长度为#{stack.length}"
	
	res_select = sql_select(client,root_url)
	if res_select[0] == 0
		puts "没有该记录"
		res_insert = sql_insert(client,stack,root_url)
		if res_insert == 1
			puts "插入成功"
		end	
	end	
end



#这边还要创建一个表 保证点过的不在点击
res_exist_table = host_table_exist(client,host)
if res_exist_table == 0
	create_table_host(client,host)
end	


puts "======================="
puts "当前需要遍历的url是#{root_url}"
puts "该网站需要遍历的层数是#{num}"
puts "======================="

update_flag = 0

##绑定server的地址 这个是我服务器的地址
server = UDPSocket.new
server.bind("192.168.16.141", 4913)


while stack.length !=0

	puts "----------------------------------"	
	puts "等待 client 中..."	
	
	mesg, addr = server.recvfrom(65536)
	
	#收到hi 进行分发url 否则更新client 传回来的值
	if mesg == "hi"
		
		puts "server 收到的信息是 : #{mesg}"
		puts "有 client 连接进来了..."
		url_and_deep = stack.pop
		puts "stack的长度 #{stack.length}"
		
		puts "----------------------------------"	
		##更新host_url 表的root_url字段  以及 当前的host表 eveaddition
		sql_update(client,stack,root_url)
		line = Digest::MD5.hexdigest("#{url_and_deep.url}:#{url_and_deep.deep}")
		insert_host_table(client,host,url_and_deep.url,url_and_deep.deep,line)
		puts "发之前的 url_and_deep : #{url_and_deep}"
		server.send url_and_deep.to_s, 0, addr[3], addr[1]
	
	#不是收到hi的情况   
	else
		new_stack = mesg
		puts "client端出来的数据回来啦"
		next if new_stack=="[]"
	
		if new_stack == "this is a next"
			puts "收到 this is a next"
			res_update = sql_update(client,stack,root_url)
			next
		end
		
		ok = new_stack.scan(/url=\"(.*?)\".*?deep=(.*?)\,.*?line=\"(.*?)\">/)
		new_stack = []
		for i in 0..ok.length-1
			obj = Node.new ok[i][0],ok[i][1].to_i,ok[i][2]
			new_stack.push obj
		end
		
		puts "查重前的stack的长度: #{stack.length}"
		
		#(1)对数据库 进行查重 (2)对当前stack查重
		new_stack.each do |obj|
			res_select_link_exist = select_host_table(client,host,obj.line)
			res_include_link = include_link(obj.url,stack)
			#puts "数据库中的查询结果: #{res_select_link_exist}   stack中的查询结果:#{res_include_link}"
			if (res_select_link_exist == 1 || res_include_link == 1)
				#puts "该链接已经存在啦 或者stack中已有 不存stack"
				next
			else
				puts "存进stack"
				stack.push obj
			end			
			puts "----------------------------------"	
		end
		puts "查重后的stack的长度: #{stack.length}"
		
		puts "更新数据库"
		res_update = sql_update(client,stack,root_url)
		if res_update == 1
			puts "更新成功"
		end	
		
	end

	
end

puts "server work done"
puts "stack 的长度 : #{stack.length}"

if stack.length == 0
	puts "----------------------------------"	
	puts "等待 client 中..."
	
	mesg, addr = server.recvfrom(10000000)
	
	if mesg == "hi"
		puts "server 收到的信息是 : #{mesg}"
		puts "有 client 连接进来了..."
		server.send "work ok", 0, addr[3], addr[1]
	#不是收到hi的情况
	else
		puts "程序退出,服务器的任务已经完成"
		exit
	end
end

  

 

client端就做很简单的事情,点击url,返回最后的结果给server

#加载一些库
#加载文件

proxy = get_proxy
puts proxy
agent = Mechanize.new
agent.open_timeout = 15
agent.set_proxy proxy[0],proxy[1]
sql_client = Mysql2::Client.new(:host => "数据库地址", :username => "用户名",:password=>"密码",:database=>"数据库名称")

root_url = "https://www.made-in-china.com"
page = agent.get root_url
host = root_url.scan(/www\.(.*?)\./)[0][0]
host.gsub!("-","_")
#puts "请输入链接需要点击的层数:"
#num = gets.chomp.to_i
num = 3
Node = Struct.new :url,:deep,:line
server_ip = "192.168.16.141"
a=100
return_err = "this is a next"


while a > 0
	
	sql_num = sql_select(sql_client,root_url)
	
	if sql_num == -1
		puts "server分发结束 程序退出"
		exit
	end
	
	puts a
	a = a-1
	client = UDPSocket.new
	ok = client.send "hi", 0, server_ip, 4913
	puts  "client 发送hi成功"
	
	url_and_deep=""
	begin
		status = Timeout::timeout(10) {
			url_and_deep = client.recv(10000)
		}
	rescue
		puts "============================================"
		puts "10秒了还没收到服务器发来的数据,可能是丢包了 重新发送hi信号"
		puts "============================================"
		next
	end
	
	## recv 就是接受 recvfrom 多一个地址
	url_and_deep = client.recv(10000)
	
	
	puts "client 收到的不是 work ok"
	url_and_deep = url_and_deep.scan(/url=\"(.*?)\".*?deep=(.*?)\,.*?line=\"(.*?)\">/)
	get_url = url_and_deep[0][0]
	deep = url_and_deep[0][1].to_i
	line = url_and_deep[0][2]
	
	
	begin
		puts "client 收到的 url : #{get_url} deep:#{deep}"
	rescue
		puts "可能该url 乱码 不打印 继续执行"
	end
	

	#判断一个链接要不要点
	get_check_res = check(get_url,root_url)
	if get_check_res == 1
		puts "链接中含有pdf 或者 mp4 不点 跳过 或者 facebook twitter 等"
		puts "----------------------------------"		
		client.send return_err, 0,server_ip, 4913		
		next
	end
	
	#判断一个链接是否可以点击成功
	res_click_link = click_link(agent,get_url,root_url)
	if res_click_link[0] == 1
		new_page = res_click_link[1]
		#puts "当前的链接数为#{new_page.links.length}"
		puts "点击该链接成功"
		
	else
		puts "该链接点击失败 跳过"
		puts "----------------------------------"
			
		client.send return_err, 0,server_ip, 4913		
		next
	end	
			
	#判断内容是否为0
	if new_page.body.length <= 0
		puts "当前页面没有内容 跳过"
		puts "----------------------------------"
		
		client.send return_err, 0,server_ip, 4913		
		next
	end
			
	#判断是否是产品页 是产品页  就next
	res = is_product_page(new_page.body,host)
	if res == 1
		puts "该页面有完整的产品信息 存进磁盘" 
		client.send return_err, 0,server_ip, 4913	
		next
	end
	
	stack = []
	if deep < num
			
		deep = deep+1
		begin	
			puts "当前页面new_page的链接数为#{new_page.links.length}"
			new_page.links.each do |link|
				line = Digest::MD5.hexdigest("#{link.href}:#{deep}")
				obj = Node.new(link.href,deep,line)
				stack.push obj
			end
			stack.uniq!
		rescue Exception=>e
			puts e.message
			puts "当前页面的链接数为0"
			client.send return_err, 0,server_ip, 4913
			next
		end
	end
	begin
		puts "发送前的stack的长度 : #{stack.length}"
		client.send stack.to_s, 0,server_ip, 4913
		puts "----------------------------------------"
	rescue
		puts "发送内容过长 分开发送"
		begin
			client.send stack[0...stack.length/2].to_s, 0,server_ip, 4913
			puts "第一次发送成功"
			client.send stack[stack.length/2..stack.length-1].to_s, 0,server_ip, 4913
			puts "第二次发送成功"
			puts "----------------------------------------"
		rescue
			puts "分开两次还是发送失败 内容太长 不要了"
			client.send return_err, 0,server_ip, 4913
		end
	end
end

  

 

现在运行这个程序,都觉得爬虫速度快了不少!继续加油

 

posted @ 2018-09-13 10:03  WangHello  阅读(236)  评论(0编辑  收藏  举报