抓产品,采用多进程--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
现在运行这个程序,都觉得爬虫速度快了不少!继续加油

浙公网安备 33010602011771号