首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • c# 爬虫程序 的url队列问题 [已结贴,结贴人:ranmer]
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 18:36:18 楼主
    小弟最近在搞一个c# 的爬虫程序 以前没有搞过现在搞得苦啊,最大就是多线程的问题了

    我现在是想做一个可以移动的程序,就是随便在那台电脑上随便安装下就能运行^_^

    现在我的问题就是 url队列 的问题,因为互联网上的网页很多,所以要把下载下来的链接存起来,放入队列,以备使用;
    要是全部放入内存的话,肯定运行不了多久,就会因为内存耗光布终止的,所以我选择了放在数据库
    因为是移动的程序,所以决定用access数据库,现在问题就来了,access数据库本身不支持多线程,一个线程打开了会锁住数据库其它的线程也就无法访问了 ,我用了很多方法来尽量不让他们同时打开,可难免会出现同时访问到,还有就是效率也就底了很多


    我在网上下了一个别人的exe程序 他的没用任何数据库,来存放url队列,程序运行十分流畅,内存也没有因为运行的时间而增加

    我想问的是,他是如何处理这个url队列的,如果不用数据库不存放url队列,还有其它方法吗?
    最好是能给小弟个简单的例子,呵呵!可能我的表达不是很清楚,还请多体谅一下小弟,先在此谢过了
    200  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 19:02:571楼 得分:50
    为了提高效率, url都是直接存放于内存中.

    Google也是这样做的.

    至于存放在什么数据结构中, 你可以用很多选择.

    最简单不过也最慢的莫过于直接存放于链表或者数组中. 遍历搜索.

    好一点你可以将URL拆分, 用树来存储, 提高收缩性能, 降低内存使用量.

    比如www.sina.com.cn/news/viewnews.asp?id=2
        www.163.com.cn

    你可以将它放入下面这样一个树中. (不好画, 看懂意思就行了)
                                              cn
                                            com
                                        sina  163
                                      www      www
                                    news
                                news.asp
                              id=2
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 19:26:122楼 得分:0
    到底要存多少地址呀?算出来要多少内存?
    爬过的网址还爬么?
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 22:28:123楼 得分:10
    把url都存放于内存中是不现实的,应该对url队列进行封装,如一次从数据库取出多少URL放于内存中,对外屏蔽对access数据库的操作,使多线程都共用这个URL队列,而不是每个线程都去数据库去取,也就是对于各个线程,面向的是URL队列,而不是数据库。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 22:34:084楼 得分:10
    URL放在文件或数库中,一个数组放抓到的URL,抓到一定数量写入数据库。继续抓
    一个数组放取出的URL。一次取一定量,给多线程的代码去处理这个URL。取完再从数据库拿~
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 22:41:265楼 得分:12
    建两张虚拟表,每张规定多少个url,根据最长时间淘汰制置换。就是建表+计数引用,淘汰计数最少的url,不用队列,用双向链表,队列默认16个元素,每到17个元素,会扩增一倍,浪费内存。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wangsaokui
    • 等级:
    发表于:2008-05-10 22:41:286楼 得分:12
    定义一个ArrayList,然后多线程向其中添加数据,当ArrayList的长度达到一定时,把数据拷贝到第二个ArrayList,把第一个清空,之后把第二个一项项存到数据库中
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 22:47:217楼 得分:20
    楼上的方法不行,ArrayList多线程读没问题,但是多线程写必定要引发同步问题,必须要加锁,你这样会引起重复写,或者写锁死,性能不可取。另外你这样会引发内存“热点”问题,对内存频繁读写会造成错误。

    建两张虚拟表,每张规定多少个url,根据最长时间淘汰制置换。就是建表+计数引用,淘汰计数最少的url,不用队列,用双向链表,队列默认16个元素,每到17个元素,会扩增一倍,浪费内存。

    楼主是不是单用户???如果单用户不需要创建那么多线程,一个线程专门管理淘汰机制和从数据库拖数据,然后用异步调用访问队列就可以了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 23:19:118楼 得分:5
    Google是把URL全部放入内存中处理.

    当然楼主没有google那么多机器,但是我也相信楼主你不可能搜索范围和Google一样庞大, 可以尝试全部放入内存中.


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 23:19:229楼 得分:1
    学习了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 23:24:5810楼 得分:10
    如果楼主只是考虑怎么存储"待搜索"的URL, 那当然不用全部放入内存.

    但是, 对于"已搜索"的URL, 由于你需要防止重复搜索, 防止环搜索, 如果不放入内存, 效率将低的无法接受.

    除非你的网页数非常小.


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wangsaokui
    • 等级:
    发表于:2008-05-11 00:45:2011楼 得分:10
    To:terrible12

    摘自MSDN
    ArrayList
    此类型的公共静态(在 Visual Basic 中为 Shared)成员是线程安全的。但不能保证任何实例成员是线程安全的。
    只要集合未修改,ArrayList 就可以同时支持多个阅读器。若要保证 ArrayList 的线程安全,则必须通过由 Synchronized 方法返回的包装来执行所有操作。
    从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

    以上说明ArrayList可以多线程写,不需要加锁,但不能在写的同时读取,所以在保存的时候先拷贝到第二个ArrayList,然后清空第一个重新写,把第二个保存到数据库。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-11 12:12:1112楼 得分:10
    楼上的,是可以多线程写,没说不可以啊,你写的时候不可能全部清空去写,而是指定一个已经要淘汰的item吧,如果这个item正在被人读取或者在你写之后,人家要访问原始值的速度比你写的速度慢,你必须加锁达到线程同步(消费者生产者模式PV操作)。我不是哪个不能读写的意思,而是同步的意义。楼上的应该理解的吧。

    如你所说,保持两个ArrayList,一个用于存放,一个用于去更新,你得了解垃圾回收机制,在没有回收的时候,那种清空的对象还是在内存中,那你内存占用,导致之后内存频繁“抖动”问题。

    如果只是单用户问题则不需要考虑多个线程写,那是不可能的,单用户,只允许一个线程去写,读取线程等待写线程完毕之后唤醒。如果按照楼上清空、赋值、保存,那计算机要花多大的性能代价
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-11 12:30:4913楼 得分:1
    关注中..

    可以把网页源码中的所有链接地址抓取后放到内存中.能否遍历处理就行的吧
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-11 14:05:2814楼 得分:1
    SQL Express不能用么?
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-11 16:21:4815楼 得分:1
    我做过

    思想是这样的.

    一个当前要查询的url.

    一个保存已查询的url.

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-11 17:23:2416楼 得分:1
    se
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • qq562342
    • 等级:
    发表于:2008-05-11 17:34:4617楼 得分:1
    序列化
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-11 18:54:3918楼 得分:0

    多谢各位朋友的关注,呵呵

    ArrayList加锁在用多线程是可以办到的,如果全部放在到ArrayList中,内存不一会儿就会被耗尽,我现在已经做出的用的是acc数据库,在程序运行不到10分钟数据库就可以达到10M以上,压缩后也上M的,我看过网络上一位高人这样算过一下
    平均每个Url有50个字符。如果有很多Url,每个Url占50个字符,一百万个Url就是会占用50M的存储空间 ,如果程序能连续运行1个晚上,Url数可以超过一百万的好多倍,所以如果直接存放在内存的话,程序会越运行越慢,最后死掉,

    如果说使用3楼的方法的法在取队列上边是完全可以,解决多个线程的抢占问题,但是每个线程都会自己再生产Url并存入队列,这样还是会引起,数据库的"锁"问题,

    因为,要达到"移动"所以 SQL Express 在这个方案中不是很可行,

    我个人觉得1楼的方法,很不错,把URL分割,这样可以减少很多相同字符的存入,就是我对这个"树"不是很懂,在分割后它们怎么在取出的时候,怎么进行重组,还有就是"消费",就是在用完之后要怎么能它进行删除操作(一个队列的如每次最上的20条),

    shrinerain 很希望你能我多讲讲,最好能写个简单的例程,
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wangsaokui
    • 等级:
    发表于:2008-05-11 21:31:0319楼 得分:2
    放在文件中不就完了,为什么一定要放在数据库中呢,可以每小时写一个文件,或者每天写一个文件
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wangsaokui
    • 等级:
    发表于:2008-05-11 21:31:4920楼 得分:1
    要移动,自己把文件拷走就是,还可以把数据刻录成光盘
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 00:29:4021楼 得分:9
    功能需求少,同时连接数小的话,可以用SQL Server Compact
    http://msdn.microsoft.com/en-us/library/ms838019.aspx
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 06:36:2222楼 得分:10
    SQLite

    为什么一次有100万个URL要处理呢??

    种子爬虫,只需要一次处理一个URL 正则获取新的URL后,写入 SQLite 队列 ,再释放掉.处理新的一个URL. 一个页面的URL能上10000 也就很NB了....


    数据爬取爬虫,一个进程,多线程处理. 不要一次就把所有的URL取完啊...一个进程,可以选择性地 取出 SQLite 中的一段URL..

    别的进程(线程)再去取另一段URL处理就成了....

    归到底,应该是设计的问题吧...
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 08:28:4023楼 得分:1
    该回复于2008-05-16 16:36:01被版主删除
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 10:02:5724楼 得分:10
    引用 18 楼 ranmer 的回复:
    如果说使用3楼的方法的法在取队列上边是完全可以,解决多个线程的抢占问题,但是每个线程都会自己再生产Url并存入队列,这样还是会引起,数据库的"锁"问题,

    其实存入队列也一样,线程自己产生的新URL都是先放到队列中,等缓冲区满后再写入数据库。数据库的"锁"问题是可以避免的。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 12:44:3725楼 得分:0

    还是先谢谢各位朋友的关注,在先前看了   LS   各位朋友的方法,也使我想通了一些问题,在效率上和稳定上也比原来有所提高,但是在问题的深入的时候,我看到了一个排序,就是说在分析这个url的时候,就判断这个url   的重要性   高的就先“爬取”差的就往后排,看看针对这个,大家有什么好的建议给我,呵呵

    22楼的朋友不满你说,SQLite   我也使用过   用的是网络上一个封装好托管组件   System.Data.SQLite   在使用的时候不知道是怎么回事,老是会出现一些如“访问已损坏的内存”之类的怪问题(我想可能是操作太频繁了),还有就是我并不是一次就取100W   url   而恰好用的方法就是跟你说的差不多(我的每个队列为20),不知道你有没有其它什么好的SQLite   托管组件

    24   楼,因为每个线程都是使用的自己的队列,所以如果自己生产自己用的话,会阻塞其它线程,所以这样的后果就是只有一个线程在工作

    还有就是,在线程发生死锁时,我想用一个timer来维护这些工作线程,在死锁后,重新开启,没想到试了好多次,都不行,不知道,死锁后还能不能解锁??


    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 12:58:3426楼 得分:5
    看来楼主还是没有明白我的意思。

    每个线程都是使用的自己的队列? 我说过是所有的线程共用一个队列,而不是每个线程都有自己的队列。

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 12:58:4827楼 得分:1
    绑定,学习了,别忘了把答案贴上来啊
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • IamBM
    • 等级:
    发表于:2008-05-12 13:18:3628楼 得分:2
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 13:33:0429楼 得分:2
    学习 帮顶
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-12 13:51:1330楼 得分:2
    引用 29 楼 yuxuanji 的回复:
    学习 帮顶
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-14 19:31:5631楼 得分:0

    虽然,没有解决问题,但还是谢谢各位了~~~

    准备把重新设计一下,先把帖子结了.
    修改 删除 举报 引用 回复

    网站简介广告服务网站地图帮助联系方式诚聘英才English 问题报告
    世纪乐知(北京)网络技术有限公司 版权所有 京 ICP 证 020026 号
    Copyright © 2000-2007, CSDN.NET, All Rights Reserved